Selecting Joints in hierarchy one at a time - python

I have this code
joint_name = cmds.ls(sl=1)[0]
circle_name = cmds.circle(name = joint_name + "_CTL", nr=(1, 0, 0) )
group_name = cmds.group(name = joint_name + "_OFFSET")
cmds.select(joint_name, group_name)temp_constraint = cmds.parentConstraint()
cmds.delete(temp_constraint)
cmds.select(circle_name, joint_name)
cmds.pointConstraint()
cmds.orientConstraint()
When you select a joint and run this code you will get a circle that will control that joint. While going down the hierarchy you have to select that joint and then run the code.
How would I be able to have all the joints to have circles controlling them without having to go through the outliner selection a joint?

run this on your chain of joint :
for x, joint_name in enumerate(cmds.ls(sl=1, dag=True, type='joint')):
circle_name = cmds.circle(name = '{}_CTL{:02d}'.format(joint_name,x), nr=(1, 0, 0) )
group_name = cmds.group(name = '{}_OFFSET{:02d}'.format(joint_name,x))
cmds.select(joint_name, group_name)
temp_constraint = cmds.parentConstraint()
cmds.delete(temp_constraint)
cmds.select(circle_name, joint_name)
cmds.pointConstraint()
cmds.orientConstraint()
Note that instead of use select, you could feed pointConstraint :
cmds.pointConstraint(circle_name, joint_name, n='something')

Related

Select the right context for the OUTLINER using PYTHON and QT

I have the following code :
def make_override_editable():
for area in bpy.context.screen.areas:
if area.type == "OUTLINER":
ctx = bpy.context.copy()
ctx["area"] = area
with bpy.context.temp_override(area=area):
print(bpy.context.area.type)
bpy.ops.outliner.liboverride_operation(
type="OVERRIDE_LIBRARY_CREATE_HIERARCHY",
selection_set="SELECTED_AND_CONTENT",
)
The following line when is ran through the script :
print(bpy.context.area.type)
outputs OUTLINER
but I still get the error that I have the incorrect context :
RuntimeError: Operator bpy.ops.outliner.liboverride_operation.poll() failed, context is incorrect
which normally works in blender text editor with 3 lines :
bpy.context.area.type = 'OUTLINER'
bpy.ops.outliner.liboverride_operation(type="OVERRIDE_LIBRARY_CREATE_HIERARCHY",selection_set="SELECTED_AND_CONTENT")
bpy.context.area.type = 'TEXT_EDITOR'
I'm using python in a much more complex script with QT.
any suggestions ?
I'm trying to make a linked skeleton (Armature) editable so I can change it's pose.
I've also searched for a low level function that I can use, but did not had any success.
or perhaps is there another method to LINK and animation to a LINKED Armature ?
I've tried this :
def make_override_editable():
for area in bpy.context.screen.areas:
if area.type == "OUTLINER":
ctx = bpy.context.copy()
ctx["area"] = area
with bpy.context.temp_override(area=area):
print(bpy.context.area.type)
bpy.ops.outliner.liboverride_operation(
type="OVERRIDE_LIBRARY_CREATE_HIERARCHY",
selection_set="SELECTED_AND_CONTENT",
)
And was expecting to have this
bpy.ops.outliner.liboverride_operation(type="OVERRIDE_LIBRARY_CREATE_HIERARCHY",selection_set="SELECTED_AND_CONTENT")
this works even if the Armature is linked.
no need to use operators.
# Reset the Armature to the default POSE.
for n in bpy.context.object.pose.bones:
n.location = (0, 0, 0)
n.rotation_quaternion = (1, 0, 0, 0)
n.rotation_axis_angle = (0, 0, 1, 0)
n.rotation_euler = (0, 0, 0)
n.scale = (1, 1, 1)

Connector displacement assignment in Abaqus python scripting - empty elements in input file

I've been trying to set up a script to automatically assign connector displacement boundary conditions. When I run the script it all looks fine in the GUI (wires are created, BCs are created and assigned the right value), but when I submit I get the following error: "Element connectivity is missing for element x of type "CONN3D2" and the element connectivity is in fact missing in the input file. I assign the edges by using the midpoints between the wire start and ends, but for some reason it doesn't assign them to the elements. This is my connector assignment function:
def assignConnectors(self):
p = self.m.parts[self.partName]
a = self.m.rootAssembly
a.Instance(name=self.instanceName, part=p, dependent=ON)
e = a.edges
n = a.instances[self.instanceName].nodes
#allelements = p.Set(name='allElements', elements=self.listObjElem)
elset = a.instances[self.instanceName].elements
elsetAssembly = a.Set('assemblyElements', elements=elset)
a.regenerate()
v1 = a.instances[self.instanceName].vertices
rows = len(self.listConstraints)
columns = len(self.listConstraints[0])
total = rows*columns
listObjNode=[];
self.listObjElem=[];
self.listObjConnector=[];
for j,pairElem in enumerate(self.listElem):
p1 = a.getCoordinates(self.listNodes[pairElem[0]-1])
p2 = a.getCoordinates(self.listNodes[pairElem[1]-1])
#print(p1,p2)
wires = a.WirePolyLine(points=((p1,p2),), mergeType=IMPRINT, meshable=OFF)
a.regenerate()
pt1 = a.getCoordinates(self.listNodes[pairElem[0]-1])
pt2 = a.getCoordinates(self.listNodes[pairElem[1]-1])
print(pt1,pt2)
pt11 = np.asarray(pt1[0])
pt12 = np.asarray(pt1[1])
pt13 = np.asarray(pt1[2])
pt21 = np.asarray(pt2[0])
pt22 = np.asarray(pt2[1])
pt23 = np.asarray(pt2[2])
new_p1 = (pt11+pt21)/2
new_p2 = (pt12+pt22)/2
new_p3 = (pt13+pt23)/2
new_p = tuple([new_p1,new_p2,new_p3])
print(new_p)
a = self.m.rootAssembly
e = a.edges
edges1 = e.findAt((new_p, ))
print(edges1)
region = a.Set(edges = edges1, name='Set'+str(j))
self.m.ConnectorSection(name='ConnSect-1'+str(j),translationalType=AXIAL)
csa = a.SectionAssignment(sectionName='ConnSect-1'+str(j), region=region)
self.m.ConnDisplacementBC(name='BC-'+str(j+total), createStepName=self.stepName, fastenerSetName='Set'+str(j), u1=float(self.listElongations[j]), u2=UNSET, u3=UNSET, ur1=UNSET, ur2=UNSET, ur3=UNSET, amplitude=UNSET, fixed=OFF, distributionType=UNIFORM)
a.regenerate()
Am I assigning the elements wrong somehow?
Thanks a lot for any help!

How do I animate multiple objects simultaneously with each having a different KeyFrameTrack in pythreejs?

I generate the 4x4 transformation matrices over time for each object in a scene and use the VectorKeyframeTrack to set the transform matrix on a Mesh object. I am able to animate the objects individually with an AnimateAction for each object but cannot figure out how to have all objects animate simultaneously with a single .play() call and keep them in sync. This is as far as I've gotten, hoping that starting with the second loop the objects would be time synchronized, but they aren't:
import pythreejs as pjs
rod_mesh = pjs.Mesh(
pjs.CylinderBufferGeometry(0.005, 0.005, sys.constants[lB] - sys.constants[h] / 2,),
pjs.MeshStandardMaterial(color='red')
)
plate_mesh = pjs.Mesh(pjs.PlaneBufferGeometry(sys.constants[h], sys.constants[w]),
pjs.MeshStandardMaterial(color='blue', side='DoubleSide'))
# Animation will not update without this set.
rod_mesh.matrixAutoUpdate = False
plate_mesh.matrixAutoUpdate = False
# Set initial position/orientation
rod_mesh.matrix = rod_matrices[0]
plate_mesh.matrix = plate_matrices[0]
# Setup scene
view_width = 600
view_height = 400
camera = pjs.PerspectiveCamera(position=[0.25, 0.25, 0.25], aspect=view_width/view_height)
key_light = pjs.DirectionalLight(position=[0, 10, 10])
ambient_light = pjs.AmbientLight()
scene_pjs = pjs.Scene(children=[rod_mesh, plate_mesh, camera, key_light, ambient_light])
controller = pjs.OrbitControls(controlling=camera)
renderer = pjs.Renderer(camera=camera, scene=scene_pjs, controls=[controller], width=view_width, height=view_height)
# Specify KeyframeTracks
rod_track = pjs.VectorKeyframeTrack(name='.matrix', times=list(sys.times), values=rod_matrices)
plate_track = pjs.VectorKeyframeTrack(name='.matrix', times=list(sys.times), values=plate_matrices)
rod_clip = pjs.AnimationClip(tracks=[rod_track])
plate_clip = pjs.AnimationClip(tracks=[plate_track])
rod_mixer = pjs.AnimationMixer(rod_mesh)
plate_mixer = pjs.AnimationMixer(plate_mesh)
rod_action = pjs.AnimationAction(rod_mixer, rod_clip, rod_mesh)
plate_action = pjs.AnimationAction(plate_mixer, plate_clip, plate_mesh)
# Try to enforce syncronization among actions at each loop start
plate_action.exec_three_obj_method('syncWith', 'IPY_MODEL_' + rod_action.get_view_spec()['model_id'])
rod_action.exec_three_obj_method('syncWith', 'IPY_MODEL_' + plate_action.get_view_spec()['model_id'])
listener_func = '() => {{ {}.syncWith({}); }}'.format('IPY_MODEL_' + plate_action.get_view_spec()['model_id'], 'IPY_MODEL_' + rod_action.get_view_spec()['model_id'])
rod_mixer.exec_three_obj_method('addEventListener', 'loop', listener_func)
syncWith() must need to be called at every frame transition but I'm not sure how to affect the animation frame loop from pythreejs.
A solution was given here:
https://github.com/jupyter-widgets/pythreejs/issues/262
The key is to animate the scene instead of the individual meshes and the trick to do so is to name the meshes and then use scene/<mesh name>.<attribute to change> when setting up the KeyFrameTracks. Here is my example that was reworked:
rod_mesh = pjs.Mesh(pjs.CylinderBufferGeometry(0.005, 0.005, sys.constants[lB] - sys.constants[h] / 2),
pjs.MeshStandardMaterial(color='red'),
name='rod' # name each mesh!
)
plate_mesh = pjs.Mesh(pjs.PlaneBufferGeometry(sys.constants[h], sys.constants[w]),
pjs.MeshStandardMaterial(color='blue', side='DoubleSide'),
name="plate" # name each mesh!
)
# For updating the transform matrices directly set:
rod_mesh.matrixAutoUpdate = False
plate_mesh.matrixAutoUpdate = False
# Scene setup
view_width = 600
view_height = 400
camera = pjs.PerspectiveCamera(position=[0.25, 0.25, 0.25], aspect=view_width/view_height)
key_light = pjs.DirectionalLight(position=[0, 10, 10])
ambient_light = pjs.AmbientLight()
scene_pjs = pjs.Scene(children=[rod_mesh, plate_mesh, camera, key_light, ambient_light])
controller = pjs.OrbitControls(controlling=camera)
renderer = pjs.Renderer(camera=camera, scene=scene_pjs, controls=[controller], width=view_width, height=view_height)
# Key thing here is to set the attribute you want to change as a sub-item of the scene. Use the names of the meshes above.
rod_track = pjs.VectorKeyframeTrack(name='scene/rod.matrix',
times=list(sys.times),
values=rod_matrices)
plate_track = pjs.VectorKeyframeTrack(name='scene/plate.matrix',
times=list(sys.times),
values=plate_matrices)
# Now create a single clip with both tracks and animate the scene:
clip = pjs.AnimationClip(tracks=[rod_track, plate_track], duration=sys.times[-1])
action = pjs.AnimationAction(pjs.AnimationMixer(scene_pjs), clip, scene_pjs)
Here is my fully working example:
https://gist.github.com/moorepants/c5ebb846499c4002744b8c101705015f

How to fix 'TypeError: in method 'MFnMesh_anyIntersection', argument 4 of type 'MIntArray const *' error in Python in Maya

I wanted to try out some raycasting with Python in Maya using OpenMaya.MFnMesh.anyIntersection().
I just want to cast a ray from on object downwards and hit a plane, not go any further so I get only one raycasthit and get the translation values from the raycasthit.
I got the code from a video and made it a bit clearer to understand.
For the code to run properly in Maya you need an object that is higher in the Y-axis than a different one, preferably a polyPlane(for example: having a polySphere at position [0, 3, 0] and a polyPlane at position [0, 0, 0], select the polySphere and run the code)
import maya.OpenMaya as OpenMaya
import maya.cmds as cmds
def RayReposition(*args):
direction = (0.0, -1, 0)
sel = cmds.ls(sl = True)
fromPositionRay = cmds.xform(sel[0], query = True, translation = True)
selShape = cmds.listRelatives(shapes = True)
meshes = cmds.ls(geometry = True)
cmds.select(clear = True)
for x in meshes:
if x == selShape[0]:
continue
else:
OpenMaya.MGlobal.selectByName(x)
sList = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(sList)
item = OpenMaya.MDagPath()
sList.getDagPath(0, item)
item.extendToShape()
fnMesh = OpenMaya.MFnMesh(item)
raySource = OpenMaya.MFloatPoint(fromPositionRay[0], fromPositionRay[1], fromPositionRay[2], 1.0)
rayDir = OpenMaya.MFloatVector(direction[0], direction[1], direction[2])
faceIds = None
triIds = None
idsSorted = False
testBothDirections = False
worldSpace = OpenMaya.MSpace.kWorld
maxParam = 999999
accelParams = None
sortHits = True
hitPoints = OpenMaya.MFloatPointArray()
hitRayParams = OpenMaya.MFloatArray()
hitFaces = OpenMaya.MIntArray()
hitTris = None
hitBarys1 = None
hitBarys2 = None
tolerance = 0.0001
hit = fnMesh.anyIntersection(raySource, rayDir, worldSpace, maxParam, testBothDirections, faceIds, triIds, idsSorted, accelParams, tolerance, hitPoints, hitRayParams, hitFaces, hitTris, hitBarys1, hitBarys2)
OpenMaya.MGlobal.clearSelectionList()
firstHit = (hitPoints[0].x, hitPoints[0].y, hitPoints[0].z)
print firstHit
RayReposition()
I expected to get the translation values from the raycasthit but I get the following error:
TypeError: in method 'MFnMesh_anyIntersection', argument 4 of type 'MIntArray const *'
Using the OpenMaya.MFnMesh.allIntersections() function instead works perfectly fine but I get every single hit from the raycast, but I only want the first hit.
Links to the OpenMaya.MFnMesh API:
link: https://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__py_ref_class_open_maya_1_1_m_fn_mesh_html
The main thing is that anyIntersection is looking for a single intersection it hits first, not multiple. So your out parameters are of the wrong types because they're arrays.
I would also avoid clearing or making new selections in your loop as it would just slow down performance by having to redraw the viewports every time.
Here's a working example that will create a locator on the first mesh it hits:
import maya.OpenMaya as OpenMaya
import maya.cmds as cmds
def RayReposition(*args):
direction = (0.0, -1, 0)
sel = cmds.ls(sl=True)
fromPositionRay = cmds.xform(sel[0], query=True, translation=True)
selShape = cmds.listRelatives(shapes=True)
meshes = cmds.ls(geometry=True)
for x in meshes:
if x == selShape[0]:
continue
else:
sList = OpenMaya.MSelectionList()
sList.add(x)
item = OpenMaya.MDagPath()
sList.getDagPath(0, item)
item.extendToShape()
fnMesh = OpenMaya.MFnMesh(item)
raySource = OpenMaya.MFloatPoint(fromPositionRay[0], fromPositionRay[1], fromPositionRay[2], 1.0)
rayDir = OpenMaya.MFloatVector(direction[0], direction[1], direction[2])
worldSpace = OpenMaya.MSpace.kWorld
maxParam = 999999
testBothDirections = False
faceIds = None
triIds = None
idsSorted = False
accelParams = None
sortHits = True
hitPoints = OpenMaya.MFloatPoint()
hitRayParams = None
hitFaces = None
hitTris = None
hitBarys1 = None
hitBarys2 = None
tolerance = 0.0001
hit = fnMesh.anyIntersection(
raySource, rayDir, faceIds, triIds, idsSorted, worldSpace, maxParam, testBothDirections, accelParams,
hitPoints, hitRayParams, hitFaces, hitTris, hitBarys1, hitBarys2, tolerance)
if hit:
firstHit = (hitPoints.x, hitPoints.y, hitPoints.z)
loc = cmds.spaceLocator()[0]
cmds.xform(loc, ws=True, t=firstHit)
print "Hit on {} at {}".format(x, firstHit)
break
RayReposition()
I find the c++ documentation a bit more clearer of what the method expects for parameters.

Maya Python GUI Rotate Sphere

(New Coder) I'm trying it create a basic GUI which allows the user to create a sphere and choose the radius and rotation. I managed to do the radius however i am a bit stuck with the rotation; the xform command. Like i said the radius is working but not the rotation.
Heres the GUI at the moment: https://gyazo.com/5f3626443334da1abdecaea434fe4e03
Any help appreciated:)
Heres my code:
import maya.cmds as cmds
def shapeTool ():
ram = 'RenamerWin'
if cmds.window(ram, q = True, exists =True):
cmds.deleteUI(ram)
ram = cmds.window("RenamerWin",t = "Shape Tool", w=300, h=300)
cmds.columnLayout(adj = True)
cmds.separator(h=20)
cmds.text("Welcome to the Shape Creator")
cmds.separator(h=20)
sphR = cmds.intSliderGrp(l = "Radius", min =0, max = 10, field = True)
sphA = cmds.intSliderGrp(l = "Rotate", field = True)
def mySphere(_):
mySphereRadius = cmds.intSliderGrp(sphR , q= True,value =True)
mySphereRotate = cmds.intSliderGrp(sphA , q= True,value =True)
finalSphere = cmds.polySphere(r=mySphereRadius, n = "mySphere")
finalSphere = cmds.xform( r=True, ro=(0, 90, 0) )
cmds.button(l = "Create a Sphere",c=mySphere)
cmds.showWindow(ram)
shapeTool()
Your rotate function will always rotate the new sphere 90 degrees in Y. Is that the intent? it ignores the rotate slider.
As written, it will rotate the sphere you create -- but for clarity and to understand what's really going on you might want to do this:
finalSphere, finalShape = cmds.polySphere(r=mySphereRadius, n = "mySphere")
cmds.xform(finalSphere, r=True, ro=(0, mySphereRotate, 0))

Categories

Resources