maya python, polyvertexpernormal - python

So in a nutshell I am needing to export the vertex normals from a character into a text file, or whatever, and then reimport them onto the same character in a different scene.
I have the import export part working in a method that I think is ok, but actually going through the loop and setting the normal on each vertex is taking over twenty minutes and usually overloads the ram on my machine and crashes maya.
I guess I am looking for a way to make my code more efficient or just run faster, any advice would be appreciated. Thanks.
def ImoNorms(self):
ll = mc.ls ('head.vtxFace[*][*]')
input = open('My desktop.txt', 'r')
spltOne = ll[:len(ll)/2]
spltTwo = ll[len(ll)/2:]
i = 0
for each in spltOne:
CurrentLine = input.readline()
kk = re.split(r'\[|\]|\,|\/n|\ ',CurrentLine)
aa = float(kk[1])
aa = round(aa, 3)
bb = float(kk[3])
bb = round(bb,3)
cc = float(kk[5])
cc = round(cc,3)
mc.select(each)
mc.polyNormalPerVertex(xyz =(aa, bb, cc))
i = i + 1
if i%1000 == 0:
print i
init()
Sorry for the formatting issues, still new to this site.

+1 to using OpenMaya if you want better performance.
Check out MFnMesh.getNormals and MFnMesh.setNormals. I admit I haven't used these methods myself, but if it's anything like MFnMesh.setPoints it should be a significant boost in speed as it's setting the normals all at once. Seems like you don't have to deal with its history either.
Here's an example on its usage that will re-direct all of a sphere's vert normals to point down. (Go to Display->Polygons->Vertex Normals to visualize the normals)
import maya.OpenMaya as OpenMaya
# Create a sphere to change vert normals with
mesh_obj, _ = cmds.polySphere()
# Wrap sphere as MDagPath object
sel = OpenMaya.MSelectionList()
sel.add(mesh_obj)
dag_path = OpenMaya.MDagPath()
sel.getDagPath(0, dag_path)
# Pass sphere to mesh function set
mesh_fn = OpenMaya.MFnMesh(dag_path)
# Create empty vector array
vec_array = OpenMaya.MFloatVectorArray()
# Get sphere normals and stuff data in our vector array
mesh_fn.getNormals(vec_array, OpenMaya.MSpace.kObject)
for i in range( vec_array.length() ):
# Point all normals downwards
vec_array[i].assign( OpenMaya.MFloatVector(0, -1, 0) )
# Apply normals back to sphere
mesh_fn.setNormals(vec_array, OpenMaya.MSpace.kObject)
You may also want to consider a different way on how you read your file instead of reading each line one by one. Maybe using json.dumps to store the data to a file and json.loads to retrieve the data. This could potentially speed things up.

What you have will be very slow because you are creating a lot of history on your mesh: if you exit out of the loop after a couple of iterations you'll see that your mesh is accumulating a polyNormalPerVertex node for every iteration.
Unfortunately there's no way to turn of construction history for this command (which seems like an oversight: most commands have a ch flag for exactly this purpose). So the first thing to try is to add an mc.delete(ch=True) after every polyNormalPerVertex call. That will be much faster and it might be enough for what you're doing.
Otherwise you'll need to use the OpenMaya api, which is a bit harder than cmds but will let you do bulk normal operations faster. I would get the cmds version working first and see if it's good enough, it should be much more performant without the history overhead.
UPDATE
Unless you want to learn the API to do this, the right thing is probably:
Save out the mesh with the right normals as an MB or MA file
in the other file, load that mesh and use Transfer Attributes to copy the normals across.
Delete history and remove the mesh you loaded in (2)
That gets you off the hook for both making your own file format and for perf issues, plus it gives you pre-made options for cases where the topology does not line up for some reason

Related

Publishing normals in ROS

I'm new to the ROS and i have a struggle with the normals. So here is my situation.
I subscribe to my camera (realsense camera), I get the point cloud, I convert the point cloud from ROS_to_pcl and finally I use the function make_NormalEstimation() of python_pcl to get the normals. So far so good!
Now I want to publish somehow these normals to a ROS topic and by publishing i mean visualizing them also in RVIZ. The python_pcl function make_NormalEstimation() returns 4 values in the form of a vector. The 3 first values are normal_x, normal_y, normal_z and the 4th value is the curvature. I want to publish and visualize the normals in RVIZ through PoseStamed messages. As far as i know a PoseStamped message needs a Pose and a Quaternion. For the Pose field i use the x, y, z of the desired point in my point cloud i want to find the normal. But when it comes for the quaternion (and here is my main problem and struggle) i don't know what to use! I tried to use the returned values as they were quaternion_x, quaternion_y, quaernion_z, quaternion_w but the results where not so good...
So. my questions are:
How can i use the returned values of make_NormalEstimation() to
create a PoseStamed message?
Is there a way to transform the returned values into quaternion?
Am i missing something about the returned values?
Is there another way of finding and using normals in ROS?
How can i generate and publish a normal in ROS? Not only it's
normal_x, normal_y, normal_z values but also it's orientation.
Do i have to publish both it's normal_x, normal_y, normal_z and
orientation or just it's normal_x, normal_y, normal_z values? And
if so how will a robot know the orientation it needs to approach a
point of interest?
Sorry for the chaos in my questions! I really hope they make sense!
Thanks in advance!
You can simply setup a publisher and create messages on demand for every normal in your list.
import rospy
from geometry_msgs import PoseStamped
pose_pub = rospy.Publisher('/normals_topic', PoseStamped, queue_size=10)
def calc_and_publish():
#Calculate your normals
norms = calculate_norms()
for n in norms:
output_pose = PoseStamped
output_pose.pose.position.x = n[0]
output_pose.pose.position.y = n[1]
output_pose.pose.position.z = n[2]
pose_pub.publish(output_pose)
I should note that this can eat up a lot of resources since you'll be having to loop over the pointcloud multiple times per message.

Generating objects with position in Maya using Python

I have some code that uses Python to read some data from a file, and then while still using Python generates an object in Maya based on the file. Everything is working good and the object comes out looking correctly. The problem is however that non of the segments that the object is made up of has a correct actual position, the Translate XYZ is set to 0 for all of them, even though they look correctly. The problem with this is that when I later on import the model into Unity3D I can't interact with the objects properly as the position is still at 0 while the mesh is were it should be. Is there some correct way to generate objects to make them have a position?
The code calls multiple functions to make different segments (one example of one of these functions is shown below). Then it uses maya.cmds.polyUnite to make it into one object. This is then repeated using a for-loop that runs for some amount of times (specified in the file). This for-loop calls cmds.duplicate(object made above) and moves the new object in the z-axis using cmds.move(0, 0, z, duped object). Due to some bad coding the code then calls polyUnite to make it all into one big object and then calls polySeperate to split it into small segements again. Is it possible that this is causing the problem?
Each segment is generated something like this:
cmds.polyCube(n='leftWall', w=w, h=h, d=d, sw=w, sh=h, sd=d)
cmds.setAttr('leftWall.translateX', -Bt/float(2)-(THICKNESS/float(2)))
cmds.setAttr('leftWall.translateY', a)
cmds.setAttr('leftWall.rotateZ', -90)
cmds.polyNormal('leftWall', nm=0, unm=0)
cmds.polyCube(n='rightWall', w=w, h=h, d=d, sw=w, sh=h, sd=d)
cmds.setAttr('rightWall.translateX', Bt/float(2)+(THICKNESS/float(2)))
cmds.setAttr('rightWall.translateY', a)
cmds.setAttr('rightWall.rotateZ', 90)
cmds.polyNormal('rightWall', nm=0, unm=0)
cmds.polyUnite('leftWall', 'rightWall', n='walls')
addTexture('walls', 'wall.jpg')

how to add location constraints for an object in blender?

I want to make it so that my program will stop running and print object is out of bounds if an object goes say into the negative z part of the plane in blender.
the objects name is Cube.031. I will sudo code what I want to do I just am not sure about sure how to do the syntax for it.
if(Cube.031.zLocation < 0)
print(object is out of bounds)
end
If you know some programming, learning python won't take long.
For the blender specific info, almost everything is accessed through the bpy module, the API reference is online.
You can refer to an object by name in bpy.data.objects[]. There are also other lists available, like bpy.context.selected_objects[] and bpy.context.visible_objects[].
An objects location is an array of three values (x,y,z), you can either access the z location as location.z or location[2].
import bpy
obj = bpy.data.objects['Cube.031']
if obj.location.z < 0:
print('object is out of bounds')
If you wanted to go through all selected objects
for obj in bpy.context.selected_objects:
if obj.location.z < 0:
print('object {} is out of bounds'.format(obj.name))
Note that v2.80 is due for release soon and has some changes to the API, if you are just starting with blender you may want to start with 2.80. You will also find blender.stackexchange a better place to ask for blender specific help.

Creating publication-quality geometric figures in Python

I am a mathematician. Recently, I became the editor of the puzzles and problems column for a well-known magazine. Occasionally, I need to create a figure to accompany a problem or solution. These figures mostly relate to 2D (occasionally, 3D) euclidean geometry (lines, polygons, circles, plus the occasional ellipse or other conic section). The goal is obtaining figures of very high quality (press-ready), with Computer Modern ("TeX") textual labels. My hope is finding (or perhaps helping write!) a relatively high-level Python library that "knows" euclidean geometry in the sense that natural operations (e.g., drawing a perpendicular line to a given one passing through a given point, bisecting a given angle, or reflecting a figure A on a line L to obtain a new figure A') are already defined in the library. Of course, the ability to create figures after their elements are defined is a crucial goal (e.g., as Encapsulated Postscript).
I know multiple sub-optimal solutions to this problem (some partial), but I don't know of any that is both simple and flexible. Let me explain:
Asymptote (similar to/based on Metapost) allows creating extremely high-quality figures of great complexity, but knows almost nothing about geometric constructions (it is a rather low-level language) and thus any nontrivial construction requires quite a long script.
TikZ with package tkz-euclide is high-level, flexible and also generates quality figures, but its syntax is so heavy that I just cry for Python's simplicity in comparison. (Some programs actually export to TikZ---see below.)
Dynamic Geometry programs, of which I'm most familiar with Geogebra, often have figure-exporting features (EPS, TikZ, etc.), but are meant to be used interactively. Sometimes, what one needs is a figure based on hard specs (e.g., exact side lengths)---defining objects in a script is ultimately more flexible (if correspondingly less convenient).
Two programs, Eukleides and GCLC, are closest to what I'm looking for: They generate figures (EPS format; GCLC also exports to TikZ). Eukleides has the prettiest, simplest syntax of all the options (see the examples), but it happens to be written in C (with source available, though I'm not sure about the license), rather limited/non-customizable, and no longer maintained. GCLC is still maintained but it is closed-source, its syntax is significantly worse than Eukleides's, and has certain other unnatural quirks. Besides, it is not available for Mac OS (my laptop is a Mac).
Python has:
Matplotlib, which produces extremely high-quality figures (particularly of functions or numerical data), but does not seem to know about geometric constructions, and
Sympy has a geometry module which does know about geometric objects and constructions, all accessible in delightful Python syntax, but seems to have no figure-exporting (or even displaying?) capabilities.
Finally, a question: Is there a library, something like "Figures for Sympy/geometry", that uses Python syntax to describe geometric objects and constructions, allowing to generate high-quality figures (primarily for printing, say EPS)?
If a library with such functionality does not exist, I would consider helping to write one (perhaps an extension to Sympy?). I will appreciate pointers.
There is a way to generate vector images with matplotlob, outputting with the library io to a vector image (SVG) with this approach.
I personally tried to run the code of the approach (generate a vectorial histogram) in that webpage as a python file, and it worked.
The code:
import numpy as np
import matplotlib.pyplot as plt
import xml.etree.ElementTree as ET
from io import BytesIO
import json
plt.rcParams['svg.fonttype'] = 'none'
# Apparently, this `register_namespace` method is necessary to avoid garbling
# the XML namespace with ns0.
ET.register_namespace("", "http://www.w3.org/2000/svg")
# Fixing random state for reproducibility
np.random.seed(19680801)
# --- Create histogram, legend and title ---
plt.figure()
r = np.random.randn(100)
r1 = r + 1
labels = ['Rabbits', 'Frogs']
H = plt.hist([r, r1], label=labels)
containers = H[-1]
leg = plt.legend(frameon=False)
plt.title("From a web browser, click on the legend\n"
"marker to toggle the corresponding histogram.")
# --- Add ids to the svg objects we'll modify
hist_patches = {}
for ic, c in enumerate(containers):
hist_patches['hist_%d' % ic] = []
for il, element in enumerate(c):
element.set_gid('hist_%d_patch_%d' % (ic, il))
hist_patches['hist_%d' % ic].append('hist_%d_patch_%d' % (ic, il))
# Set ids for the legend patches
for i, t in enumerate(leg.get_patches()):
t.set_gid('leg_patch_%d' % i)
# Set ids for the text patches
for i, t in enumerate(leg.get_texts()):
t.set_gid('leg_text_%d' % i)
# Save SVG in a fake file object.
f = BytesIO()
plt.savefig(f, format="svg")
# Create XML tree from the SVG file.
tree, xmlid = ET.XMLID(f.getvalue())
# --- Add interactivity ---
# Add attributes to the patch objects.
for i, t in enumerate(leg.get_patches()):
el = xmlid['leg_patch_%d' % i]
el.set('cursor', 'pointer')
el.set('onclick', "toggle_hist(this)")
# Add attributes to the text objects.
for i, t in enumerate(leg.get_texts()):
el = xmlid['leg_text_%d' % i]
el.set('cursor', 'pointer')
el.set('onclick', "toggle_hist(this)")
# Create script defining the function `toggle_hist`.
# We create a global variable `container` that stores the patches id
# belonging to each histogram. Then a function "toggle_element" sets the
# visibility attribute of all patches of each histogram and the opacity
# of the marker itself.
script = """
<script type="text/ecmascript">
<![CDATA[
var container = %s
function toggle(oid, attribute, values) {
/* Toggle the style attribute of an object between two values.
Parameters
----------
oid : str
Object identifier.
attribute : str
Name of style attribute.
values : [on state, off state]
The two values that are switched between.
*/
var obj = document.getElementById(oid);
var a = obj.style[attribute];
a = (a == values[0] || a == "") ? values[1] : values[0];
obj.style[attribute] = a;
}
function toggle_hist(obj) {
var num = obj.id.slice(-1);
toggle('leg_patch_' + num, 'opacity', [1, 0.3]);
toggle('leg_text_' + num, 'opacity', [1, 0.5]);
var names = container['hist_'+num]
for (var i=0; i < names.length; i++) {
toggle(names[i], 'opacity', [1, 0])
};
}
]]>
</script>
""" % json.dumps(hist_patches)
# Add a transition effect
css = tree.getchildren()[0][0]
css.text = css.text + "g {-webkit-transition:opacity 0.4s ease-out;" + \
"-moz-transition:opacity 0.4s ease-out;}"
# Insert the script and save to file.
tree.insert(0, ET.XML(script))
ET.ElementTree(tree).write("svg_histogram.svg")
Previously, you need to pip install the required libraries on the top lines, and it successfully saved a SVG file with a plot (you can read the file and zoomwant in the histogram and you will get no pixels, as the image is generated with mathematicals functions).
It (obviously for our time) uses python 3.
You then could import the SVG image within your TeX document for the publication rendering.
I hope it may help.
Greetings,
Javier.

ROS frame transformation (camera to base)

I am working with Baxter robot and what I am trying to do is get the position of an object using augmented reality markers and move the hand to that position in order to grasp it.
I am using the ar_tools package to get the position/orientation of the object, but that with respect to the head_camera which I am using. The last couple of days I've been experimenting with how to change that reference frame (head_camera) to the base frame as this frame is used by moveit to make the plans. I have tried to set the frame_id of the header of the message I receive from the ar_tools manually to 'base':
pose_target.header.frame_id = 'base'
but the position and orientation I am getting are still WRT the head_camera. I also tried to do:
self.tl.lookupTransform("/base", "/head_camera", self.t)
where self.t = self.tl.getLatestCommonTime("/head_camera", "/base"), but I was getting an extrapolation error. It was something like
the transformation requires to extrapolate in the past
(I can't really remember now and I'm not in the lab.) Then I thought I might need to run the lookupTransform from the head_cam to head, from head to torso and from torso to Baxter's base.
Could someone guide me on how to change the frame of the marker of the ar_tools from head_camera to base?
Also, for the extrapolation error, is there a way to do this in a static way?
There is a slightly more straightforwards way to do this, presuming you're reviving a PoseStamped message from ar_tools:
on_received_pose(pose):
'''
Handler for your poses from ar_tools
'''
if self.tl.waitForTransform(pose.header.frame_id, "/base", pose.header.stamp, rospy.Duration(0.1)): # this line prevents your extrapolation error, it waits up to 0.1 seconds for the transform to become valid
transd_pose = self.tl.transformPose("/base",pose)
# do whatever with the transformed pose here
else:
rospy.logwarn('Couldn\'t Transform from "{}" to "/base" before timeout. Are you updating TF tree sufficiently?'.format(pose.header.frame_id))
You're getting that extrapolation error likely because the transform network wasn't fully formed at the time you got your first message; tf refuses to extrapolate, it will only interpolate, so if you haven't received at least one transform message for every frame both before and after (or exactly at) the timestamp you're trying to transform to, it will throw an exception. That added if statement checks to see if it can actually perform the transform before trying to do so. You could of course also just surround the transformPose() call in a try/catch block instead, but I feel that for tf this makes it more explicit what you're trying to do.
In general, check out the simple ROS tf Python Tutorial for more examples/modes of operation.

Categories

Resources