Creating temporary curve in maya python plugin - python

I would like to get a world position of a specific parameter along a nurbs curve defined by a list of positions.
Currently I'm creating a temporary curve in the plugin just to get an array of positions along this curve:
# targetXforms = array of MPoints
# knots = knots list
# uValue = array of floats (0->1)
#
curveFn = om.MFnNurbsCurve()
curveFn.create(targetXforms, knots, 3, om.MFnNurbsCurve.kOpen, False, False, nullObj)
for i in range (numRefXforms):
point = curveFn.getPointAtParam (uValue[i])
print point
Is there a better way to do this (i.e. not have the overhead of creating a curve)? Some math libraries perhaps?
If not how do I delete this curve so I don't have a curve created every time the plugin is evaluated (MDGModifier seems to be a bit crashy)
Also, is there a way to find length along a curve for a given parameter value. Maya 2016 Extension 2 has a function for this:
MFnNurbsCurve::findLengthFromParam()
But, We don't have this extension yet. :(
Thanks in advance!

If you provide a Nurbs curve data object to MFnNurbsCurve.create() as the parent, instead of leaving it null, then the data doesn't appear as a curve in the scene graph and therefore you don't have to remove it.
Try this:
import pymel.core as pm
pts = ( [ 0,0,0 ], [ 0,10,0 ], [ 0,10,10 ], [ 0,0,10 ])
knots = [0,0,0,1,1,1]
curveFn = om.MFnNurbsCurve()
dataCreator = om.MFnNurbsCurveData()
curveDataObject = dataCreator.create()
curveFn.create(pts, knots, 3, om.MFnNurbsCurve.kOpen, False, False, curveDataObject)
for i in range(11):
point = curveFn.getPointAtParam (i/10.0)
pm.spaceLocator(p=(point.x, point.y, point.z))
To get the arc length at a parameter without the API, you could create an arcLengthDimension node. It means you would have to create a curve in the scene and connect it up.
dimNode = pm.arcLengthDimension( 'curveShape1.u[0]' )
dimNode.uParamValue.set( 0.5 )
print(dimNode.arcLength.get()

Related

Why Gmsh create more surfaces than expected when I use "gmhs.model.occ.cut()" command?

I am trying to create a volume in Gmsh (using Python API) by cutting some small cylinders from a bigger one.
When I do that, I expect to have one surface for each cutted region, instead, I get the result in the figure. I have highlighted in red the surfaces that give me the problem (some cutted regions behave as expected), as you can see, instead of one surface I get two, that sometimes aren't even equal.
gmsh creates more surfaces than expected:
So, my questions are:
Why gmsh behaves like that?
How can I fix this as I need predictable behavior?
Below is the code I used to generate the geometry.
The code to work requires some parameters such as core_height, core_inner_radius and core_outer_radius, the number of small cylinders and their radius.
gmsh.initialize(sys.argv)
#gmsh.initialize()
gmsh.clear()
gmsh.model.add("circle_extrusion")
inner_cyl_tag = 1
outer_cyl_tag = 2
inner_cyl = gmsh.model.occ.addCylinder(0,0,0, 0, 0, core_height, core_inner_radius, tag = inner_cyl_tag)
outer_cyl = gmsh.model.occ.addCylinder(0,0,0, 0, 0, core_height, core_outer_radius, tag = outer_cyl_tag)
core_tag = 3
cut1 = gmsh.model.occ.cut([(3,outer_cyl)],[(3,inner_cyl)], tag = core_tag)
#create a set of filled cylinders
#set position
angle_vector = np.linspace(0,2*np.pi,number_of_hp+1)
pos_x = hp_radial_position*np.cos(angle_vector)
pos_y = hp_radial_position*np.sin(angle_vector)
pos_z = 0.0
#cut one cylinder at the time and assign the new core tag
for ii in range(0,len(angle_vector)):
old_core_tag = core_tag
heat_pipe = gmsh.model.occ.addCylinder(pos_x[ii], pos_y[ii], pos_z, 0, 0, core_height,hp_outer_radius, tag =-1)
core_tag = heat_pipe+1
core = gmsh.model.occ.cut([(3,old_core_tag)],[(3,heat_pipe)], tag = core_tag)
gmsh.model.occ.synchronize()
#get volume entities and assign physical groups
volumes = gmsh.model.getEntities(dim=3)
solid_marker = 1
gmsh.model.addPhysicalGroup(volumes[0][0], [volumes[0][1]],solid_marker)
gmsh.model.setPhysicalName(volumes[0][0],solid_marker, "solid_volume")
#get surfaces entities and apply physical groups
surfaces = gmsh.model.getEntities(dim=2)
surface_markers= np.arange(1,len(surfaces)+1,1)
for ii in range(0,len(surfaces)):
gmsh.model.addPhysicalGroup(2,[surfaces[ii][1]],tag = surface_markers[ii])
#We finally generate and save the mesh:
gmsh.model.mesh.generate(3)
gmsh.model.mesh.refine()
gmsh.model.mesh.refine()
gmsh.option.setNumber("Mesh.MshFileVersion", 2.2) #save in ASCII 2 format
gmsh.write(mesh_name+".msh")
# Launch the GUI to see the results:
#if '-nopopup' not in sys.argv:
# gmsh.fltk.run()
gmsh.finalize()
I do not think that you have additional surfaces in the sense of gmsh.model.occ surfaces. To me this looks like your volume mesh is sticking out of your surface mesh, i.e. volume and surface mesh do not fit together.
Here is what I did to check your case:
First I added the following lines at the beginning of our code to get a minimum working example:
import gmsh
import sys
import numpy as np
inner_cyl_tag = 1
outer_cyl_tag = 2
core_height = 1
core_inner_radius = 0.1
core_outer_radius = 0.2
number_of_hp = 5
hp_radial_position = 0.1
hp_outer_radius = 0.05
What I get with this code is the following:
To visualize it like this go to "Tools"-->"Options"-->"Mesh" and check "2D element faces", "3D element edges" and "3D element faces".
You can see that there are some purple triangles sticking out of the green/yellowish surfaces triangles of the inner surfaces.
You could try to visualize your case the same way and check <--> uncheck the "3D element faces" a few times.
So here is the solution for this behaviour, I did not know that gmsh behaves like this myself. It seems that when you create your mesh and refine it the refinement will be applied on the 2D surface mesh and the 3D volume mesh seperately, which means that those two meshes are not connected after the refinement anymore. What I did next was to try what happens if you create the 2D mesh only, refine it, and then create the 3D mesh, i.e.:
replace:
gmsh.model.mesh.generate(3)
gmsh.model.mesh.refine()
gmsh.model.mesh.refine()
by:
gmsh.model.mesh.generate(2)
gmsh.model.mesh.refine()
gmsh.model.mesh.refine()
gmsh.model.mesh.generate(3)
The result then looks like this:
I hope that this was actually your problem. But in future it would be good if you could provide us a minimum working example of code that we can copy-paste and get the same visualization you showed us in your image.

Plot PDF of Pareto distribution in Python

I have a specific Pareto distribution. For example,
Pareto(beta=0.00317985, alpha=0.147365, gamma=1.0283)
which I obtained from this answer and now I want to plot a graph of its Probability Density Function (PDF) in matplotlib. So I believe that the x-axis will be all positive real numbers, and the y-axis will be the same.
How exactly can I obtain the appropriate PDF information and plot it? Programmatically obtaining the mathematical PDF function or coordinates is a requirement for this question.
UPDATE:
The drawPDF method returns a Graph object that contains coordinates for the PDF. However, I don't know how to access these coordinates programmatically. I certainly don't want to convert the object to a string nor use a regex to pull out the information:
In [45]: pdfg = distribution.drawPDF()
In [46]: pdfg
Out[46]: class=Graph name=pdf as a function of X0 implementation=class=GraphImplementation name=pdf as a function of X0 title= xTitle=X0 yTitle=PDF axes=ON grid=ON legendposition=topright legendFontSize=1
drawables=[class=Drawable name=Unnamed implementation=class=Curve name=Unnamed derived from class=DrawableImplementation name=Unnamed legend=X0 PDF data=class=Sample name=Unnamed implementation=class=Sam
pleImplementation name=Unnamed size=129 dimension=2 data=[[-1610.7,0],[-1575.83,0],[-1540.96,0],[-1506.09,0],[-1471.22,0],[-1436.35,0],[-1401.48,0],[-1366.61,0],...,[-1331.7,6.95394e-06],[2852.57,6.85646e-06]] color
=red fillStyle=solid lineStyle=solid pointStyle=none lineWidth=2]
I assume that you want to perform different tasks:
To plot the PDF
To compute the PDF at a single point
To compute the PDF for a range of values
Each of these needs requires a different script. Please let me detail them.
I first create the Pareto distribution:
import openturns as ot
import numpy as np
beta = 0.00317985
alpha = 0.147365
gamma = 1.0283
distribution = ot.Pareto(beta, alpha, gamma)
print("distribution", distribution)
To plot the PDF, use drawPDF() method. This creates a ot.Graph which can be viewed directly in Jupyter Notebook or IPython. We can force the creation of the plot with View:
import openturns.viewer as otv
graph = distribution.drawPDF()
otv.View(graph)
This plots:
To compute the PDF at a single point, use computePDF(x), where x is a ot.Point(). This can also be a Python list or tuple or 1D numpy array, as the conversion is automatically managed by OpenTURNS:
x = 500.0
y = distribution.computePDF(x)
print("y=", y)
The previous script prints:
y= 5.0659235352823877e-05
To compute the PDF for a range of values, we can use the computePDF(x), where x is a ot.Sample(). This can also be a Python list of lists or a 2D numpy array, as the conversion is automatically managed by OpenTURNS.
x = ot.Sample([[v] for v in np.linspace(0.0, 1000.0)])
y = distribution.computePDF(x)
print("y=", y)
The previous script prints:
y=
0 : [ 0 ]
1 : [ 0.00210511 ]
[...]
49 : [ 2.28431e-05 ]

Snapping an object to another object vertex in Maya over Python Script

I want to snap / align an object A to vertex of object B. I have tried using below script but its not snapping exactly to the vertex but snapping with an offset. Can any one suggest me a solution. Here are the snapshots.
import maya.cmds as cmds
vertices = [];
srcObj = "pCone1";
def snapToVertex(vertex,object):
cmds.select(vertex);
x,y,z = cmds.pointPosition();
cmds.select(object);
cmds.duplicate();
cmds.move(x,y,z);
def processTask():
cmds.select( cmds.polyListComponentConversion( tv=True ) );
vertices = cmds.ls(sl = True);
print vertices;
for vrtx in vertices:
snapToVertex(vrtx,srcObj);
processTask();
Snapped with my above Script, Which didn't snap exactly to vertex.
But it should be snapped exactly to the Vervex's as below image.
It's better not to use Reset Transformations command for your Cone from Modify main menu, 'cause pivot point goes back to its initial place. The analog for useful Freeze Transformations command in Python is cmds.makeIdentity(). Even if you've moved you pivot point by 1 unit (for example) along +Y axis, do not forget to subtract that 1 from variable y cause Maya somehow remembers pivot's position. Offset of the Cone's pivot point (for snapping pivot to a vertex) depends on the Cone's size itself. By default it's 1.
Add this snippet to your code to move the duplicates in World Space:
# cmds.makeIdentity( 'pCone1', apply=True )
pivSnap = 1
cmds.xform( 'pCone1', piv=[ 0, pivSnap, 0 ] )
cmds.move( x, y-pivSnap, z, a=True, ws=True, wd=True )
You can test this code (here I moved pivot up by 0.5):
import maya.cmds as cmds
cmds.polyCube( sx=1, sy=15, sz=1, w=1, h=15, d=1 )
cmds.polyCone( r=.5, h=1, sx=10 )
cmds.move( 10, x=True )
pivSnap = .5
cmds.xform( 'pCone1', piv=[ 0, pivSnap, 0 ] )
cmds.rotate( 0, 0, '45deg' )
cmds.select( 'pCube1.e[33]','pCube1.e[37]','pCube1.e[41]','pCube1.e[45]','pCube1.e[49]','pCube1.e[53]','pCube1.e[57]','pCube1.e[61]' )
vertices = []
srcObj = "pCone1"
def snapToVertex( vertex, object ):
cmds.select( vertex )
x,y,z = cmds.pointPosition()
print( x,y,z )
cmds.select( object )
cmds.manipMoveContext( m=2 )
cmds.delete( 'pCube1', ch=True )
cmds.duplicate()
cmds.move( x, y-pivSnap, z, a=True, ws=True, wd=True )
def processTask():
cmds.select( cmds.polyListComponentConversion( tv=True ) )
vertices = cmds.ls( sl=True )
print( vertices )
for vrtx in vertices:
snapToVertex( vrtx, srcObj )
processTask()
you could greatly simplify your code and only use a few commands
import maya.cmds as mc
verts = mc.ls(os=True)
src = 'pCone1'
base_name = 'fancy_cone'
for i, vert in enumerate(verts):
dup = mc.duplicate(src, n='%s_%s' %(base_name, i))[0]
mc.xform(dup, ws=True, t=mc.xform(vert, q=True, ws=True, t=True))
keep in mind a few caveats:
1) this is assuming that the pivot of your source cone is at the tip with the tip at the origin... so it requires setting up that source cone the way you want it as if the origin point is a vertex you will be snapping it to. This would also be beneficial if the object you're 'pinning' to is rotated (no need to do any kind of vector math to determine the positional offset that way)
2) to use the orderedSelection (os) flag in the ls command, you need to be sure track selection order is turned on in your maya preferences under the selection sub section - this way you can select the verts you want in the order you want them to be created (top-down, bottum-up, whatever) instead of the vert index order
This should also be significantly faster - by tracking any created objects, you don't actually need to select anything and can manipulate the objects directly. I also put it all into one loop without any function calls which would be faster (although, you probably won't notice it unless you're making a LOOOOOOT of these).
Depending on your needs though, you may actually want to utilize a snapping function multiple places; However, I would suggest keeping the duplication out of it in case there are situations you want to snap without duplicating.
Something like this (subject to same caveats as above):
import maya.cmds as mc
def snapToVertex(vertex, object):
pos = mc.xform(vertex, q=True, ws=True, t=True)
mc.xform(object, ws=True, t=pos) # combine to single line if prefered
def processTask():
verts = mc.ls(os=True)
src = 'pCone1'
base_name = 'fancy_cone'
for i, vert in enumerate(verts):
dup = mc.duplicate(src, n='%s_%s' %(base_name, i))[0]
snapToVertex(vert, dup)
processTask()

Maya Python: Apply Transformation Matrix

I have been looking for thi answer but i don't seem to figure it out anywhere, so i hope i could get my answer here...
I'm in Maya Python API and i want to apply a transformation Matrix to a mesh.
This is how i made the mesh:
mesh = om.MFnMesh()
ShapeMesh = cmds.group(em=True)
parentOwner = get_mobject( ShapeMesh )
meshMObj = mesh.create(NumVerts, len(FaceCount), VertArray, FaceCount, FaceArray ,parentOwner)
cmds.sets( ShapeMesh, e=True,forceElement='initialShadingGroup')
defaultUVSetName = ''
defaultUVSetName = mesh.currentUVSetName(-1)
mesh.setUVs ( UArray, VArray, defaultUVSetName )
mesh.assignUVs ( FaceCount, FaceArray, defaultUVSetName )
This is how i create the TFM:
m = struct.unpack("<16f",f.read(64))
mm = om.MMatrix()
om.MScriptUtil.createMatrixFromList(m,mm)
mt = om.MTransformationMatrix(mm)
Basically i read 16 floats and convert them into a Transformation Matrix, however i don't know how to apply the mt matrix to my mesh...
I managed to get the Position,Rotation and Scale from this though, maybe it helps, this way:
translate = mt.translation(om.MSpace.kWorld)
rotate = mt.rotation().asEulerRotation()
scaleUtil = om.MScriptUtil()
scaleUtil.createFromList([0,0,0],3)
scaleVec = scaleUtil.asDoublePtr()
mt.getScale(scaleVec,om.MSpace.kWorld)
scale = [om.MScriptUtil.getDoubleArrayItem(scaleVec,i) for i in range(0,3)]
Now my last step comes in applying this Matrix to the mesh, but i can't find a good way to do it, does someone know how to do this on maya?
Thanks in advance:
Seyren.
Not sure what you mean by applying the matrix to your mesh, but if you want to update the position of each point by transforming them with that matrix, then here you go for a given MFnMesh mesh and a given MMatrix matrix:
import banana.maya
banana.maya.patch()
from maya import OpenMaya
mesh = OpenMaya.MFnMesh.bnn_get('pCubeShape1')
matrix = OpenMaya.MMatrix()
points = OpenMaya.MPointArray()
mesh.getPoints(points)
for i in range(points.length()):
points.set(points[i] * matrix, i)
mesh.setPoints(points)
If you don't want to directly update the points of the mesh, then you need to apply the matrix to the transformation node by retrieving its parent transform and using the MFnTransform::set() method.
Note that I've used in my code snippet a set of extensions that I've wrote and that might be helpful if you're using the Maya Python API. The code is available on GitHub and it also comes with a documentation to give you an idea.

Change color depending on height in Mayavi iso_surface

Is is possible to change the colour of an iso-surface depending on height of the points (in python / mayavi) ?
I can create an iso-surface visualization with my script, but I don't know how to make the iso_surface change colour with z axis so that it will be let's say black at the bottom and white at the top of the plot.
I need this in order to make sense of the visualization when it is viewed from directly above the graph.
If you know any other way to achieve this, please let me know as well.
I only want to show one iso_surface plot.
I managed to do this by combining some code from examples http://docs.enthought.com/mayavi/mayavi/auto/example_atomic_orbital.html#example-atomic-orbital and http://docs.enthought.com/mayavi/mayavi/auto/example_custom_colormap.html . Basically you must create a surface as in atomic-orbital example and then make it change colour depending on x. You must create an array of values for x. My code is (the relevant part) :
#src.image_data.point_data.add_array(np.indices(list(self.data.shape)[self.nx,self.ny,self.nz])[2].T.ravel())
src.image_data.point_data.add_array(np.indices(list(self.data.shape))[0].T.ravel())
src.image_data.point_data.get_array(1).name = 'z'
# Make sure that the dataset is up to date with the different arrays:
src.image_data.point_data.update()
# We select the 'scalar' attribute, ie the norm of Phi
src2 = mlab.pipeline.set_active_attribute(src, point_scalars='scalar')
# Cut isosurfaces of the norm
contour = mlab.pipeline.contour(src2)
# contour.filter.contours=[plotIsoSurfaceContours]
# contour.filter.contours=[plotIsoSurfaceContours[0]]
min_c = min(contour.filter._data_min * 1.05,contour.filter._data_max)
max_c = max(contour.filter._data_max * 0.95,contour.filter._data_min)
plotIsoSurfaceContours = [ max(min(max_c,x),min_c) for x in plotIsoSurfaceContours ]
contour.filter.contours= plotIsoSurfaceContours
# Now we select the 'angle' attribute, ie the phase of Phi
contour2 = mlab.pipeline.set_active_attribute(contour, point_scalars='z')
# And we display the surface. The colormap is the current attribute: the phase.
# mlab.pipeline.surface(contour2, colormap='hsv')
xxx = mlab.pipeline.surface(contour2, colormap='gist_ncar')
colorbar = xxx.module_manager.scalar_lut_manager
colorbar.reverse_lut = True
lut = xxx.module_manager.scalar_lut_manager.lut.table.to_array()
lut[:,-1] = int(plotIsoSurfaceOpacity * 254)
xxx.module_manager.scalar_lut_manager.lut.table = lut
# mlab.colorbar(title='Phase', orientation='vertical', nb_labels=3)
self.data is my data, and for unknown reasons if you want to set opacity of your surface you must reverse the lut first and then set the opacity. Multiplication by 254 instead of 255 is done to avoid a possible bug in mayavi.
I hope this helps someone.

Categories

Resources