Calculate the Normal of a vertex in maya with Python - maya

I have a script when I make a light it points to my object. I did this with a normalConstrain. But is there any other way to do this without constrains ?
I think I need to calculate the normal of the closest vertex form my Light ?
But I don't know how to do this, if someone can help me would be nice !
See Screenshot
Here is how I did the NormalConstrain :
myNormalConstrain = cmds.normalConstraint('mySphere','myLight', aimVector = (0,0,1), worldUpType = 'scene',name='NormalConstrainToObject')

The easy way is to create a closestPointOnMesh node an connect it to the worldMesh attribute of your mesh. The only wrinkle is that there's a bug in Maya 2016 (not sure about others) where the normal value coming back from the node is not actually normalized if you are using units other than centimeters. Here's a function which does it:
import maya.cmds as cmds
import maya.api.OpenMaya as api
def get_closest_normal(surface, x, y , z):
node = cmds.createNode('closestPointOnMesh')
cmds.connectAttr(surface + '.worldMesh ', node + ".inMesh")
cmds.setAttr( node + ".inPosition", x, y, z, type='double3')
normal = cmds.getAttr(node + ".normal")
# there's a bug in Maya 2016 where the normal
# is not properly normalized. Not sure
# if it's fixed in other years.... this
# is the workaround
result = api.MVector(*normal)
cmds.delete(node)
result.normalize()
return result
print get_closest_normal('pSphereShape1', 10, 1, 1)
Instead of getting the number and deleting the node as done here, you could keep the node around and connect it's normal attribute to something for live updates. This is a fairly expensive node to update, however, so don't use that version for something like an animation rig without testing performance to be sure it's affordable

Related

Error: no applicable method for 'bbox' applied to an object of Class "Extent"

I am plotting how collared animals utilise water points using the recurse package, and working through the code supplied here, with my data replacing Leo's data: http://dx.doi.org/10.5441/001/1.46ft1k05
I'm currently trying to map movement based on most frequently visited locations. However I keep getting an error in UseMethod "bbox".
When I use show(leoGeo), it returns as a Move object, and I have enabled and registered a Google API key. I have recurse, move, ggplot2, ggmap, RgoogleMaps, raster, scales, viridis, lubridate, reshape2, raster, rworldmap, aptools, cluster, amt, sp, rgdal,curl and dplyr loaded.
leovisit50 = getRecursions(leo.df, 50)
revisitThreshold = 75
leoGeo.map.df = as(leoGeo,'data.frame')
leoGeo.map.df$revisits = leovisit50$revisits
and when I go to use this command
map.leoGeo = qmap(bbox(extent(leoGeo[leovisit50$revisits >
revisitThreshold,])), zoom = 13, maptype = "road.Dist")
it keeps returning the error below
Error in UseMethod("bbox", x) :
no applicable method for 'bbox' applied to an object of class "Extent"
(I can provide full code for this if that is required, it was just the map.leoGeo line I was having difficulty with).
I'm new to movement analysis and am not sure how to fix this problem,any help would be greatly appreciated!
The method clearly exists
library(raster)
r <- raster()
e <- extent(r)
#bbox(e)
# min max
#s1 -180 180
#s2 -90 90
So probably you are loading a package that hides that method. As you are not calling the method directly, you cannot do raster::bbox. Start with a fresh R session, and see if there are warnings that tell you about this when you load your packages. Try to avoid loading many packages, and avoid those that hide methods in other packages.

How to get data from an upstream node in maya?

I have a maya node myNode, which creates a shadeNode, which inMesh attribute is connected to shapeNode.outMesh and has an attribute distance.
myNode.outMesh -> shapeNode.inMesh
myNode.distance = 10
Then i have a command, which works on the shape node, but requires the distance argument, which it does by iterating over the inMesh connections:
MPlugArray meshConnections;
MPlug inMeshPlug = depNodeFn.findPlug("inMesh");
inMeshPlug.connectedTo(meshConnections, true, false); // in connections
bool node_found = false;
for(uint i = 0; i < numConnections; i++) {
MPlug remotePlug = meshConnections[i];
myNode = remotePlug.node();
if(MFnDependencyNode(myNode ).typeName() == "myNode") {
node_found = true;
break;
}
}
MFnDependencyNode myDepNode(myNode);
MPlug distancePlug = myDepNode.findPlug("distance");
Now i get a problem, when applying another node (of another type) to myShape, because the dependency graph now looks like this:
myNode.outMesh -> myOtherNode.inMesh
myOtherNode.outMesh -> shapeNode.inMesh
myNode.distance = 10
I tried to remove the check for typeName() == "myNode", because i understood the documentation like there should be recursion to the parent node, when the next node return Mstatus::kInvalidParameter for the unknown MPlug, but i cannot reach the distance plug without implementing further graph traversion.
What is the correct way to reliably find an attribute of a parent node, even when some other nodes were added in between?
The command itself should use the distance Plug to either connect to myNode or to some plug which gets the value recursively. If possible i do not want to change myOtherNode to have a distance plug and correspondig connections for forwarding the data.
The usual Maya workflow would be to make the node operate in isolation -- it should not require any knowledge of the graph structure which surrounds it, it just reacts to changes in inputs and emits new data from its outputs. The node needs to work properly if a user manually unhooks the inputs and then manually reconnects them to other objects -- you can't know, for example, that some tool won't insert a deformer upstream of your node changing the graph layout that was there when the node was first created.
You also don't want to pass data around outside the dag graph -- if the data needs to be updated you'll want to pass it as a connection. Otherwise you won't be able to reproduce the scene from the graph alone. You want to make sure that the graph can only ever produce an unambiguous result.
When you do have to do DAG manipulations -- like setting up a network of connectiosn -- put them into an MPXCommand or a mel/python script.
I found the answer in an answer (python code) to the question how to get all nodes in the graph. My code to find the node in the MPxCommand now looks like this:
MPlugArray meshConnections;
MPlug inMeshPlug = depNodeFn.findPlug("inMesh");
MItDependencyGraph depGraphIt(inMeshPlug, MFn::kInvalid, MItDependencyGraph::Direction::kUpstream);
bool offset_mesh_node_found = false;
while(!depGraphIt.isDone()) {
myNode = depGraphIt.currentItem();
if(MFnDependencyNode(myNode).typeName() == "myNode") {
offset_mesh_node_found = true;
break;
}
depGraphIt.next();
}
The MItDependencyGraph can traverse the graph in upstream or downstream direction either starting from an object or a plug. Here i just search for the first instance of myNode, as I assume there will only be one in my use case. It then connects the distance MPlug in the graph, which still works when more mesh transforms are inserted.
The MItDependencyGraph object allows to filter for node ids, but only the numeric node ids not node names. I probably add a filter later, when I have unique maya ids assigned in all plugins.

Maya creating shader with cmds does not appear in hypershade

using maya 2014/2015 creating a shader like so:
import maya.cmds as cmds
my_shader = cmds.createNode('lambert', n='mine')
will create this result.
anybody knows how to get that shader to be reflected in the hypershade?
shaders are a slightly different node type:
cmds.shadingNode('lambert', asShader=1)
You'll also need to create a shadingEngine node, what we usually know as ShaderGroups or SG`s:
cmds.shadingNode('shadingEngine', asUtility=1)
and connect the .outColor of the shader to the .surfaceShader attribute of the SG. The SG node is actually a subclass of a Maya set, and to assign objects or faces to it use the sets command.
Adding to the answer for completeness:
In some cases (namely ShaderFx shaders) you need to connect the material to the scene's defaultShaderList1 node's next available plug.
import maya.cmds as mc
material = mc.shadingNode('lambert', asShader=True)
shading_engine = mc.sets(name="%sSG" % material, empty=True, renderable=True, noSurfaceShader=True)
mc.connectAttr('%s.outColor' % material, '%s.surfaceShader' % shading_engine)
mc.connectAttr('%s.msg'%material, ':defaultShaderList1.s', na=True)

FSharpChart with Windows.Forms very slow for many points

I use code like the example below to do basic plotting of a list of values from F# Interactive. When plotting more points, the time taken to display increases dramatically. In the examples below, 10^4 points display in 4 seconds whereas 4.10^4 points take a patience-testing 53 seconds to display. Overall it's roughly as if the time to plot N points is in N^2.
The result is that I'll probably add an interpolation layer in front of this code, but
1) I wonder if someone who knows the workings of FSharpChart and Windows.Forms could explain what is causing this behaviour? (The data is bounded so one thing that seems to rule out is the display needing to adjust scale.)
2)Is there a simple remedy other than interpolating the data myself?
let plotl (f:float list) =
let chart = FSharpChart.Line(f, Name = "")
|> FSharpChart.WithSeries.Style(Color = System.Drawing.Color.Red, BorderWidth = 2)
let form = new Form(Visible = true, TopMost = true, Width = 700, Height = 500)
let ctl = new ChartControl(chart, Dock = DockStyle.Fill)
form.Controls.Add(ctl)
let z1 = [for i in 1 .. 10000 do yield sin(float(i * i))]
let z2 = [for i in 1 .. 20000 do yield sin(float(i * i))]
plotl z1
plotl z2
First of all, FSharpChart is a name used in an older version of the library. The latest version is called F# Charting, comes with a new documentation and uses just Chart.
To answer your question, Chart.Line and Chart.Points are quite slow for large number of points. The library also has Chart.FastLine and Chart.FastPoints (which do not support as many features, but are faster). So, try getting the latest version of F# Charting and using the "Fast" version of the method.

Using PyMEL to set the "Alpha to Use" attribute in an object of class psdFileTex

I am using Maya to do some procedural work, and I have a lot of textures that I need to load into Maya, and they all have transparencies (alpha channels). I would very much like to be able to automate this process. Using PyMEL, I can create my textures and hook them up to a shader, but the alpha doesn't set properly by default. There is an attribute in the psdFileTex node called "Alpha to Use", and it must be set to "Transparency" in order for my alpha channel to work. My question is this - how do I use PyMEL scripting to set the "Alpha to Use" attribute properly?
Here is the code I am using to set up my textures:
import pymel.core as pm
pm.shadingNode('lambert', asShader=True, name='myShader1')
pm.sets(renderable=True, noSurfaceShader=True, empty=True, name='myShader1SG')
pm.connectAttr('myShader1.outColor', 'myShader1SG.surfaceShader', f=True)
pm.shadingNode('psdFileTex', asTexture=True, name='myShader1PSD')
pm.connectAttr('myShader1PSD.outColor', 'myShader1.color')
pm.connectAttr('myShader1PSD.outTransparency', 'myShader1.transparency')
pm.setAttr('myShader1ColorPSD.fileTextureName', '<pathway>/myShader1_texture.psd', type='string')
If anyone can help me, I would really appreciate it.
Thanks
With any node, you can use listAttr() to get the available editable attributes. Run listAttr('myShaderPSD'), note in it's output, there will be two attributes called 'alpha' and 'alphaList'. Alpha, will return you the current selected alpha channel. AlphaList will return you however many alpha channels you have in your psd.
Example
pm.PyNode('myShader1PSD').alphaList.get()
# Result: [u'Alpha 1', u'Alpha 2'] #
If you know you'll only ever be using just the one alpha, or the first alpha channel, you can simply do this.
psdShader = pm.PyNode('myShader1PSD')
alphaList = psdShader.alphaList.get()
if (len(alphaList) > 0):
psdShader.alpha.set(alphaList[0])
else:
// No alpha channel
pass
Remember that lists start iterating from 0, so our first alpha channel will be located at position 0.
Additionally and unrelated, while you're still using derivative commands of the maya.core converted for Pymel, there's still some commands you can use to help make your code read nicer.
pm.setAttr('myShader1ColorPSD.fileTextureName', '<pathway>/myShader1_texture.psd', type='string')
We can convert this to pymel like so:
pm.PyNode('myShader1ColorPSD').fileTextureName.set('<pathway>/myShader1_texture.psd')
And:
pm.connectAttr('myShader1PSD.outColor', 'myShader1.color')
Can be converted to:
pm.connect('myShader1PSD.outColor', 'myShader1.color')
While they may only be small changes, it reads just the little bit nicer, and it's native PyMel.
Anyway, I hope I have helped you!

Resources