What I have here is a script where I create a bundle of circles and place them into a group. I then center the groups pivot to the bounding box. Lastly I'm wanting to align the groups position to the location in space by first collecting the locators position. How can I fix this? Currently it doesn't seem to align properly. The error occurs in the last line of the code. Thank in advanced guys.
import maya.cmds as cmds
import random
cmds.file(new=True, f=True)
a = cmds.spaceLocator(n='pipeCtrl_00')
x = random.uniform(-10,10)
z = random.uniform(-10,10)
cmds.xform(a, t=(x,0,z) )
#select all the locators you want to make curves on
locList = cmds.ls(type='locator')
nodes = maya.cmds.listRelatives(locList, type='transform', parent=True)
sorted(nodes)
points = []
#get position points
for n in nodes:
pos = cmds.xform(n, q = True, ws = True, t = True)
points.append(pos)
# create 2D grid of circles
numRows = 4
numColumns = 4
#create empty group for nodes
nodeGroup = cmds.group(em=True, name='Pipe_group_00')
for r in range(0,numRows):
for c in range(0,numColumns):
# Create circle shape and transform it
node = cmds.circle(ch=True, o=True, nr=(0, 0, 1), c=(0, 0, 0), r=.5)
cmds.xform(node, t=(r*(.5*2), c*(.5*2), 0) )
# Parent node under the group node
cmds.parent(node[0], nodeGroup, relative=False)
# center pivot of group node
cmds.xform(nodeGroup, cp=True)
# align group to path
# ------------------------------------------------------------------
print points[0]
cmds.xform(nodeGroup, ws=True, t=points[0] )
I think you want
cmds.xform(nodeGroup, cp=True, p=True)
check the docs for the -p flag
Related
My aim is to create a mesh in Python and setting markers on some 1-dimensional subset.
Up until now, I have always creates a set, for example a rectangle in gmsh, then put for example a circle in it. Then gmsh puts a mesh on my structure and I can mark the boundary of the rectangle and of the circle as facets (as xdmf files). I can then let Python read my mesh and boundary facets and using it, f.e. to solve PDEs.
What I want to do now is the following: I still want to have my rectangle with a mesh, but instead of defining the facet markes in gmsh, I want to define them using the image of a function.
More precicely: Instead of creating a circle in gmsh, I want to consider, for example, the function
Then I want to use as my facet and mark it in my mesh.
Is there a way to do this? I feel kind of lost here.
I am not sure if I understood everything in your question correctly, but here is some code that might help you. One thing I do not know is how to add element edges to physical groups in gmsh. But maybe you can figure that out.
So here is the code:
import gmsh
import numpy as np
def mapping(point):
x = point[0]
y = point[1]
z = point[2]
result = [2*x,3*y,z]
return result
def inverseMapping(point):
x = point[0]
y = point[1]
z = point[2]
result = [(1/2)*x,(1/3)*y,z]
return result
def getNodes():
nodeTags, nodeCoord, _ = gmsh.model.mesh.getNodes()
nodeCoord = np.reshape(nodeCoord,(len(nodeTags),3))
return nodeTags, nodeCoord
def getEdgeNodeCoordinates():
edgeTags, edgeNodes = gmsh.model.mesh.getAllEdges()
edgeNodes = np.reshape(edgeNodes,(len(edgeTags),2))
nodeTags, nodeCoord = getNodes()
coord = []
for i in range(0,len(edgeTags)):
tagNode1 = edgeNodes[i][0]
tagNode2 = edgeNodes[i][1]
nodeIndex1 = list(nodeTags).index(tagNode1)
nodeIndex2 = list(nodeTags).index(tagNode2)
nodeCoord1 = nodeCoord[nodeIndex1]
nodeCoord2 = nodeCoord[nodeIndex2]
coord.append([nodeCoord1,nodeCoord2])
return edgeTags, edgeNodes, nodeTags, coord
def getInverseNodeCoordinates(edgeNodeCoordinates):
coord = []
for edgeNodes in edgeNodeCoordinates:
nodeCoord1 = edgeNodes[0]
nodeCoord2 = edgeNodes[1]
newCoord1 = inverseMapping(nodeCoord1)
newCoord2 = inverseMapping(nodeCoord2)
coord.append([newCoord1, newCoord2])
return coord
def checkIntersection(edgeTags, edgeNodeCoordinates, inverseCoordinates):
intersectingEdgeTags = []
intersectingEdgeNodeCoord = []
# 1 = inside, 0 = outside
for i in range(0,len(inverseCoordinates)):
pair = inverseCoordinates[i]
coord1 = pair[0]
coord2 = pair[1]
e1 = 1 if np.linalg.norm(coord1) <= 1 else 0
e2 = 1 if np.linalg.norm(coord2) <= 1 else 0
s = e1 + e2 # s = 0 --> both nodes outside of manifold
# s = 1 --> one node inside and one node outside of manifold
# s = 2 --> both nodes inside of manifold
if s == 1:
intersectingEdgeTags.append(edgeTags[i])
intersectingEdgeNodeCoord.append(edgeNodeCoordinates[i])
return intersectingEdgeTags, intersectingEdgeNodeCoord
def visualizeEdges(intersectingEdgeNodeCoord):
for pair in intersectingEdgeNodeCoord:
p1 = pair[0]
p2 = pair[1]
t1 = gmsh.model.occ.addPoint(p1[0],p1[1],p1[2])
t2 = gmsh.model.occ.addPoint(p2[0],p2[1],p2[2])
line = gmsh.model.occ.addLine(t1, t2)
gmsh.model.occ.synchronize()
gmsh.model.setColor([(1,line)], 255, 0, 0)
gmsh.model.occ.synchronize()
gmsh.initialize()
# Create a rectangle which will be meshed later.
tag_vol_1 = gmsh.model.occ.addRectangle(-3, -4, 0, 6, 8)
# Sample the S1 manifold with n_points
S1_sampling_points = []
n_points = 100
maxAngle = 2*np.pi
angle = maxAngle/n_points
z = 0
for i in range(0,n_points):
x = np.cos(i*angle)
y = np.sin(i*angle)
S1_sampling_points.append([x,y,z])
# Map the sampled S1 points to 2*x, 3*y, z.
# This is only for "visualization" via a spline.
mappedPoints = []
mappedPointTags = []
for point in S1_sampling_points:
mappedPoint = mapping(point)
tag = gmsh.model.occ.addPoint(mappedPoint[0], mappedPoint[1], mappedPoint[2])
mappedPointTags.append(tag)
# Here the spline fitting is performed
# You will see it visualized when gmsh opens.
tagMappedS1 = gmsh.model.occ.addSpline(mappedPointTags + [mappedPointTags[0]]) # make the spline periodic by setting the last point equal to the first one
gmsh.model.occ.synchronize()
# Mesh the rectangle and tell gmsh to create edges which we can access.
gmsh.model.mesh.generate(2)
gmsh.model.mesh.createEdges() # You need to call this before using gmsh.model.mesh.getAllEdges()
# Get all these self-explanatory things
edgeTags, edgeNodes, nodeTags, edgeNodeCoordinates = getEdgeNodeCoordinates()
# Calculate the inverse-mapped coordinates of all nodes.
# With this we can just check if the transformed nodes are inside a circle of radius 1 or outside.
# If for every egde one node is inside, and the other node is outside the circle, then the edge is
# intersected by the mapped manifold f(S1) --> (2*x, 3*y, z). We then save the tag of such an edge.
inverseCoordinates = getInverseNodeCoordinates(edgeNodeCoordinates)
intersectingEdgeTags, intersectingEdgeNodeCoord = checkIntersection(edgeTags, edgeNodeCoordinates, inverseCoordinates)
# Here all intersecting edges are created within gmsh so you can see it.
# This is for visualization only. A lot of nodes with the same coordinates are created here twice.
visualizeEdges(intersectingEdgeNodeCoord)
gmsh.fltk.run()
gmsh.finalize()
And the result in gmsh looks like this:
I am trying to create a polygon from a mesh. My idea is to get all the border edges and make a polygon.i found somewhat similar answer to mine in this question
Get border edges of mesh - in winding order
I tried to implement the algorithm mention there but i am only getting a small triangle as my border edges
import plyfile
def edges_to_polygon_loop(edges):
# Initialize an empty polygon loop
polygon_loop = []
# Pick an unvisited edge segment
start_edge = edges[0]
v_start, v_next = start_edge
polygon_loop.append(v_start)
polygon_loop.append(v_next)
# Initialize a set to keep track of visited edges
visited_edges = set()
visited_edges.add(start_edge)
# Iterate until we get back to the start vertex
while v_next != v_start:
# Find the next unvisited edge segment
for edge in edges:
if edge not in visited_edges:
v_i, v_j = edge
if v_i == v_next:
polygon_loop.append(v_j)
v_next = v_j
visited_edges.add(edge)
break
elif v_j == v_next:
polygon_loop.append(v_i)
v_next = v_i
visited_edges.add(edge)
break
# Return the polygon loop
return polygon_loop
with open('M:\\volume.ply', 'rb') as f:
# Read the .ply file
ply = plyfile.PlyData.read(f)
# Extract the vertex and face data
vertices = ply['vertex']
faces = ply['face']
# Initialize an empty list of edges
edges = []
# Iterate over the faces
for face in faces:
# Get the indices of the vertices in the face
vertex_indices = face['vertex_indices']
# Add the edges to the list
for i in range(len(vertex_indices)):
# Get the indices of the endpoints of the edge
v1_index = vertex_indices[i]
v2_index = vertex_indices[(i + 1) % len(vertex_indices)]
# Get the coordinates of the endpoints
v1 = (vertices[v1_index]['x'], vertices[v1_index]['y'], vertices[v1_index]['z'])
v2 = (vertices[v2_index]['x'], vertices[v2_index]['y'], vertices[v2_index]['z'])
# Add the edge to the list
edges.append((v1, v2))
# Print the edges
polygon = edges_to_polygon_loop(edges)
You would need to eliminate inner edges first, then you can compose the outer loop.
:
# Extract the vertex and face data
vertices = ply['vertex']
faces = ply['face']
# Initialize an empty list of indices-pairs
pairs = []
# Iterate over the faces
for face in faces:
# Get the indices of the vertices in the face
vertex_indices = face['vertex_indices']
# Add the edge-indices to the list
for i in range(len(vertex_indices)):
# Get the indices of the endpoints of the edge
v1_index = vertex_indices[i]
v2_index = vertex_indices[(i + 1) % len(vertex_indices)]
if ((v2_index, v1_index) not in pairs):
pairs.append((v1_index, v2_index))
else:
# Remove inner edge
pairs.remove((v2_index, v1_index))
# Initialize an empty list of edges
edges = []
# Add the edges to the list
for (v1_index, v2_index) in pairs:
# Get the coordinates of the endpoints
v1 = (vertices[v1_index]['x'], vertices[v1_index]['y'], vertices[v1_index]['z'])
v2 = (vertices[v2_index]['x'], vertices[v2_index]['y'], vertices[v2_index]['z'])
# Add the edge to the list
edges.append((v1, v2))
# Print the edges
polygon = edges_to_polygon_loop(edges)
i am trying to generate geometries and meshes with the Python api of Gmsh, planning to use it in FEniCS.
I started creating my geometry following the steps reported here: https://jsdokken.com/src/tutorial_gmsh.html
The author first create the volume and then retrieve the surfaces with the command:
surfaces = gmsh.model.occ.getEntities(dim=2)
Finally, he is able to relate the surface to the tag simply by finding the center of mass (com). He uses the command gmsh.model.occ.getCenterOfMass(dim,tag) and compares it with the know com position of his surfaces, like this:
inlet_marker, outlet_marker, wall_marker, obstacle_marker = 1, 3, 5, 7
walls = []
obstacles = []
for surface in surfaces:
com = gmsh.model.occ.getCenterOfMass(surface[0], surface[1])
if np.allclose(com, [0, B/2, H/2]):
gmsh.model.addPhysicalGroup(surface[0], [surface[1]], inlet_marker)
inlet = surface[1]
gmsh.model.setPhysicalName(surface[0], inlet_marker, "Fluid inlet")
elif np.allclose(com, [L, B/2, H/2]):
gmsh.model.addPhysicalGroup(surface[0], [surface[1]], outlet_marker)
gmsh.model.setPhysicalName(surface[0], outlet_marker, "Fluid outlet")
elif np.isclose(com[2], 0) or np.isclose(com[1], B) or np.isclose(com[2], H) or np.isclose(com[1],0):
walls.append(surface[1])
else:
obstacles.append(surface[1])
Now, my problem is that this cannot work if two or more surfaces share the same com, such as two concentric cylinders.
How can i discriminate between them in such situation?
For example in case of an hollow cylinder, i would like to have a tag for each surface in order to apply different boundary conditions in FEniCS.
Thanks in advance!
You can make use of gmsh.model.getAdjacencies(dim,tag) where dim and tag are the dimension and tag of your entity of interest. This functions returns two lists up, down. The first one gives you the tags of all entities adjacent (=neighbouring) to the entity of interest with dimension dim + 1. The second list gives you the tags of all entities adjacent to the entity of interest with dimension dim - 1.
In 3D (i.e. dim = 3) the up list will be empty because there are no 4D structures in gmsh. The down list will contain all surface tags the boundary of the volume is made of.
Below is an example code. Part 1 is straight forward and in Part 2 I added a functions that sorts the surface tags by their x-coordinate.
import gmsh
gmsh.initialize()
## PART 1:
tag_cylinder_1 = gmsh.model.occ.addCylinder(0, 0, 0, 1, 0, 0, 0.1)
tag_cylinder_2 = gmsh.model.occ.addCylinder(0, 0, 0, 1, 0, 0, 0.2)
gmsh.model.occ.synchronize()
up_cyl_1, down_cyl_1 = gmsh.model.getAdjacencies(3,tag_cylinder_1)
up_cyl_2, down_cyl_2 = gmsh.model.getAdjacencies(3,tag_cylinder_2)
com_1 = gmsh.model.occ.getCenterOfMass(2, down_cyl_1[0])
com_2 = gmsh.model.occ.getCenterOfMass(2, down_cyl_1[1])
com_3 = gmsh.model.occ.getCenterOfMass(2, down_cyl_1[2])
## PART 2:
def calcBoxVolume(box):
dx = box[3] - box[0]
dy = box[4] - box[1]
dz = box[5] - box[2]
return dx*dy*dz
def getOrderedTags(cylTag):
up, down = gmsh.model.getAdjacencies(3,cylTag)
surf_COM = []
for surface in down:
com = [surface] + list(gmsh.model.occ.getCenterOfMass(2, surface))
surf_COM.append(com)
orderedSurfaces = sorted(surf_COM,key = lambda x: x[1])
orderedSurfaceTags = [item[0] for item in orderedSurfaces]
return orderedSurfaceTags
def setPhysicalTags(name,cylTag):
orderedSurfaces = getOrderedTags(cylTag)
gmsh.model.addPhysicalGroup(2, [orderedSurfaces[0]],name="inlet_"+name)
gmsh.model.addPhysicalGroup(2, [orderedSurfaces[1]],name="tube_"+name)
gmsh.model.addPhysicalGroup(2, [orderedSurfaces[3]],name="outlet_"+name)
def setPhysicalTagsCylDiff(name,cylTag):
orderedSurfaces = getOrderedTags(cylTag)
tag_A = orderedSurfaces[1]
tag_B = orderedSurfaces[2]
box_tube_A = gmsh.model.getBoundingBox(2,tag_A)
box_tube_B = gmsh.model.getBoundingBox(2,tag_B)
volBoxA = calcBoxVolume(box_tube_A)
volBoxB = calcBoxVolume(box_tube_B)
if volBoxA > volBoxB:
innerTag = tag_B
outerTag = tag_A
else:
innerTag = tag_A
outerTag = tag_B
gmsh.model.addPhysicalGroup(2, [orderedSurfaces[0]],name="inlet_"+name)
gmsh.model.addPhysicalGroup(2, [innerTag],name="tube_inner_"+name)
gmsh.model.addPhysicalGroup(2, [outerTag],name="tube_outer_"+name)
gmsh.model.addPhysicalGroup(2, [orderedSurfaces[3]],name="outlet_"+name)
# setPhysicalTags("Cylinder_1",tag_cylinder_1)
# setPhysicalTags("Cylinder_2",tag_cylinder_2)
outDimTags, outDimTagsMap = gmsh.model.occ.cut([(3,tag_cylinder_2)],[(3,tag_cylinder_1)])
cylDiffTag = outDimTags[0][1]
gmsh.model.occ.synchronize()
setPhysicalTagsCylDiff("CylDiff",cylDiffTag)
gmsh.model.mesh.generate(2)
gmsh.fltk.run()
gmsh.finalize()
I have an oriented cylinder generated with vtkCylinderSource and some transformations are applied on it to get the orientation that i want. Here is the code for creating this oriented-cylinder:
def cylinder_object(startPoint, endPoint, radius, my_color="DarkRed", opacity=1):
colors = vtk.vtkNamedColors()
# Create a cylinder.
# Cylinder height vector is (0,1,0).
# Cylinder center is in the middle of the cylinder
cylinderSource = vtk.vtkCylinderSource()
cylinderSource.SetRadius(radius)
cylinderSource.SetResolution(50)
# Generate a random start and end point
# startPoint = [0] * 3
# endPoint = [0] * 3
rng = vtk.vtkMinimalStandardRandomSequence()
rng.SetSeed(8775070) # For testing.8775070
# Compute a basis
normalizedX = [0] * 3
normalizedY = [0] * 3
normalizedZ = [0] * 3
# The X axis is a vector from start to end
vtk.vtkMath.Subtract(endPoint, startPoint, normalizedX)
length = vtk.vtkMath.Norm(normalizedX)
vtk.vtkMath.Normalize(normalizedX)
# The Z axis is an arbitrary vector cross X
arbitrary = [0] * 3
for i in range(0, 3):
rng.Next()
arbitrary[i] = rng.GetRangeValue(-10, 10)
vtk.vtkMath.Cross(normalizedX, arbitrary, normalizedZ)
vtk.vtkMath.Normalize(normalizedZ)
# The Y axis is Z cross X
vtk.vtkMath.Cross(normalizedZ, normalizedX, normalizedY)
matrix = vtk.vtkMatrix4x4()
# Create the direction cosine matrix
matrix.Identity()
for i in range(0, 3):
matrix.SetElement(i, 0, normalizedX[i])
matrix.SetElement(i, 1, normalizedY[i])
matrix.SetElement(i, 2, normalizedZ[i])
# Apply the transforms
transform = vtk.vtkTransform()
transform.Translate(startPoint) # translate to starting point
transform.Concatenate(matrix) # apply direction cosines
transform.RotateZ(-90.0) # align cylinder to x axis
transform.Scale(1.0, length, 1.0) # scale along the height vector
transform.Translate(0, .5, 0) # translate to start of cylinder
# Transform the polydata
transformPD = vtk.vtkTransformPolyDataFilter()
transformPD.SetTransform(transform)
transformPD.SetInputConnection(cylinderSource.GetOutputPort())
cylinderSource.Update()
# Create a mapper and actor for the arrow
mapper = vtk.vtkPolyDataMapper()
actor = vtk.vtkActor()
if USER_MATRIX:
mapper.SetInputConnection(cylinderSource.GetOutputPort())
actor.SetUserMatrix(transform.GetMatrix())
else:
mapper.SetInputConnection(transformPD.GetOutputPort())
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d(my_color))
actor.GetProperty().SetOpacity(opacity)
return actor, transformPD
Now i want to ray cast a line with this oriented cylinder. unfortunately, using the vtkCylinderSource as the dataset for vtkOBBTree produces the wrong points as the result. how can i use ray-casting with a PolyDataFilter?
I came up with a solution where i export my oriented-cylinder to a .stl file and then read it again to implement the ray-casting algorithm using IntersectWithLine. The problem is i have thousands of these oriented-cylinders and this method (exporting and reading) makes my code extremely slow.
def ray_cast(filename, p_source, p_target):
'''
:param filename: STL file to perform ray casting on.
:param p_source: first point
:param p_target: second point
:return: code --> 0 : No intersection.
:return: code --> +1 : p_source lies OUTSIDE the closed surface.
:return; code --> -1 : p_source lies INSIDE closed surface
'''
reader = vtk.vtkSTLReader()
reader.SetFileName(filename)
reader.Update()
mesh = reader.GetOutput()
obbtree = vtk.vtkOBBTree()
obbtree.SetDataSet(mesh)
obbtree.BuildLocator()
pointsVTKIntersection = vtk.vtkPoints()
code = obbtree.IntersectWithLine(p_source, p_target, pointsVTKIntersection, None)
# Extracting data
pointsVTKIntersectionData = pointsVTKIntersection.GetData()
noPointsVTKIntersection = pointsVTKIntersectionData.GetNumberOfTuples()
pointsIntersection = []
for idx in range(noPointsVTKIntersection):
_tup = pointsVTKIntersectionData.GetTuple3(idx)
pointsIntersection.append(_tup)
return code, pointsIntersection, noPointsVTKIntersection
Below image shows the desired result using export-stl method. (the green spheres are intersection points)
I would appreciate any suggestion and help..
With vedo:
from vedo import *
cyl = Cylinder() # vtkActor
cyl.alpha(0.5).pos(3,3,3).orientation([2,1,1])
p1, p2 = (0,0,0), (4,4,5)
ipts_coords = cyl.intersectWithLine(p1, p2)
print('hit coords are', ipts_coords)
pts = Points(ipts_coords, r=10).color("yellow")
# print(pts.polydata()) # is the vtkPolyData object
origin = Point()
ln = Line(p1,p2)
show(origin, cyl, ln, pts, axes=True)
I've created a function which creates a grid of circles and I need to collect the circle nodes created into a list so I can later manipulate the nodes. The problem is that I noticed the nodeList is given the nodes name before it's auto-renamed by maya to be unique. You'll notice when you run this script that the collected names are all the same but when you selected them in maya they are incremented to be unique.
I'm returned this
[u'mainShape_00', u'makeNurbCircle1']
[u'|mainShape_00', u'makeNurbCircle2']
[u'|mainShape_00', u'makeNurbCircle3']...
When it should be
[u'mainShape_00', u'makeNurbCircle1']
[u'|mainShape_01', u'makeNurbCircle2']
[u'|mainShape_02', u'makeNurbCircle3']...
Here is the script
# Import Modules
import maya.cmds as cmds
import random
# Scene setup
try:
cmds.select(all=True)
cmds.delete()
except:
pass
# create 2D grid of circles
numRows = 4
numColumns = 3
radiusMin = .1
radiusMax = .75
#create empty group for nodes
nodeGroup = cmds.group(em=True, name='main_group_00')
nodeList = []
for r in range(0,numRows):
for c in range(0,numColumns):
# Calculate random radius
radius = random.uniform(radiusMin,radiusMax)
# Create circle shape and transform it
node = cmds.circle(n='mainShape_00', ch=True, o=True, nr=(0, 0, 1), c=(0, 0, 0), r=radius)
cmds.xform(node, t=(r*(radiusMax*2), c*(radiusMax*2), 0) )
# Parent node under the group node
cmds.parent(node[0], nodeGroup, relative=False)
# Append nodes to list
nodeList.append(node)
for n in nodeList:
shape = n
print shape
node is 'mainShape_00' because at that time, that's what it's named. There is no collision until it's parented under nodeGroup. Grab the real name after parenting:
node[0] = cmds.parent(node[0], nodeGroup, relative=False)[0]
This substitutes the original node[0] with the newly parented node[0]
Why not naming yourself your nodes as this :
x = 0
padding = str(x).zfill(2)
mainShapeName = 'mainShape_' + padding
x += 1
# Create circle shape and transform it
node = cmds.circle(n=mainShapeName, ch=True, o=True, nr=(0, 0, 1), c=(0, 0, 0), r=radius)
cmds.xform(node, t=(r*(radiusMax*2), c*(radiusMax*2), 0) )
By incrementing yourself, you avoid maya problems.
You should even gave unique name throught every groups.
Cheers.