2

I need to flatten a selection of points on their LOCAL normal axis. I'm assuming that if the normals are correct, that this axis will always be the same regardless if they select points from any side of the object?

To visually represent what I'm trying to achieve, I'd like to turn this:

enter image description here

Into this programmatically:

enter image description here

If I set my scale tool to 'Normals Average', and manually scale them, I can flatten the points to a plane, however I need to calculate or do this by code.

I've looked at the polyMoveFacet command and it has a flag called localScaleZ, which even has 'Flattening' written in it's description, but I've had no luck. Any suggestions?

joojaa
  • 4,354
  • 1
  • 27
  • 45
Shannon Hochkins
  • 11,763
  • 15
  • 62
  • 95
  • I don't know much about maya or what kind of data it gives you access to but given certain data this can be solved as a geometrical problem. Could you state what you know how to get access to? For example, do you have all the point coordinates? Do you have a list of all the planes that these points form(ie, a list of groups of 4 points each), etc...? – entropy Mar 06 '13 at 14:09
  • Is there any particular reason why this has to be done with poly move facet? – joojaa Mar 06 '13 at 18:25
  • @entropy, I have vactor coordinates of each point selected, normal direction, face normals, HEAPS of information, I just don't know how to takle it! – Shannon Hochkins Mar 06 '13 at 21:28
  • @ShannonHochkins can you move points around individually? Also, would it work for you to move all the points to the plane defined by the four corners of all selected points? – entropy Mar 06 '13 at 21:39
  • yes that would work, and yes I can move them individually through a loop – Shannon Hochkins Mar 06 '13 at 21:55

2 Answers2

2

Easiest would be to just use the same thing your doing manually. The code for doing that in mel would look as follows:

{ // protect global namespace
     setToolTo Scale;
     manipScaleContext -e -mode 9 Scale;
     $oa = `manipScaleContext -q  -orientAxes Scale`;
     $p = `manipScaleContext -q  -position Scale`;
     scale -ws -r 
           -p ($p[0]) ($p[1]) ($p[2]) 
           -oa ($oa[0]+"rad") ($oa[1]+"rad") ($oa[2]+"rad") 
            0 1 1;
}

And Python:

cmds.setToolTo('Scale')
cmds.manipScaleContext("Scale", e=1, mode=9)
p = cmds.manipScaleContext("Scale", q=1, p=1)
oa = cmds.manipScaleContext("Scale", q=1, oa=1) 
cmds.scale(0,1,1, 
           p=(p[0],p[1],p[2]),
           oa=("%srad"%oa[0],"%srad"%oa[1],"%srad"%oa[2]))
joojaa
  • 4,354
  • 1
  • 27
  • 45
  • For some reason **scmh** does no longer work with rotate and scale. – joojaa Mar 06 '13 at 18:32
  • I couldn't get this to run @joojaa, There's no particular reason in me useing the polyMoveFacet, it was just the only thing I could find that was relative, when I run your script in the script editor, it doesn't evaluate '$p' properly, did you manage to get this to work? what did you have selected? – Shannon Hochkins Mar 06 '13 at 21:27
  • Never mind @joojaa, after some time I managed to get it to work, I also added a Python version to your post, thanks again! – Shannon Hochkins Mar 07 '13 at 03:57
  • Although in saying that, the '0,1,1', that we both have in the scale command, isn't always correct unfortunately, is there any way of calculating this depending on the selection? – Shannon Hochkins Mar 07 '13 at 04:08
  • Well yes i can see my error now. I didn't underline the fact that you need to have scale tool selected. I tough that was evident because i'm doing the same thing you are. The value for -mode does indeed go up to 9 despite the manual. And the orientation might not have updated because its using the tool manipulator, try forcing refresh. Obviously you can do the normals calculation manually too. – joojaa Mar 07 '13 at 06:44
  • Would you mind explaining how I could calculate this? I just didn't want to stop the script mid way and ask them more or less 'what normal facing direction would you like to flatten' :( – Shannon Hochkins Mar 07 '13 at 08:17
  • You don't have to stop just refresh the screen with refresh command. Anyway just do what entropy says just replace the calculation of normals with queries into vertex normals instead. If you do this you need to use mayas api. PS: is average normal what you want really? And average of what exactly face normals, face vertex normals, average face vertex normals, vertex normals or geometry normals as entropy is doing. – joojaa Mar 07 '13 at 19:43
  • I'm not quite sure what I'm after in terms of 'terminology', but I do know the final outcome I'm trying to achieve (images above). I thought that the average normals would be the closest thing to correct, I tried last night converting the selection to contained faces, using the polyInfo command to query the face normals, and then averaging the result, but still no luck unfortunately. What would you suggest? Obviously what you sent already does work perfectly, but only with a manual option change of the '0,1,1', in the beginning of the scale command. Any more ideas? – Shannon Hochkins Mar 07 '13 at 20:40
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/25804/discussion-between-joojaa-and-shannon-hochkins) – joojaa Mar 07 '13 at 20:48
2

I have never used maya nor do I know what scripting language it uses. Therefore this answer only deals with a mathematical/geometric approach to the problem. Code is in python to demonstrate the concept but you should be able to translate.

Note that I didn't test the code, but it should hopefully at least give you tools to grapple with the problem.

from math import sqrt

def point_average(points):
    l = len(points)
    return [(p.x/l,p.y/l,p.z/l) for p in points]

def find_normal(points):
    normal = point_average([p.normal for p in points])
    normal_length = sqrt(sum(c**2 for c in normal))
    normal = [c/normal_length for c in normal]
    return normal

def find_plane(points):
    normal = find_average_normal(points)
    center = point_average(points)
    # a point and a normal are enough to uniquely identify a plane
    # we anchor the plane to the farthest point from the center
    # that should be one of the corners
    dcenter = lambda p:sqrt((p.x-center.x)**2+(p.y-center.y)**2+(p.z-center.z)**2)
    points = [(dcenter(p),p) for p in points]
    points.sort()
    anchor = points[-1][1]
    return (anchor,normal)

def project_point_onto_plane(point, plane):
    anchor,normal = plane
    # kudos to http://stackoverflow.com/questions/9605556/how-to-project-a-3d-point-to-a-3d-plane
    # for the math behind this
    v = (point.x-anchor[0], point.y-anchor[1], point.z-anchor[2])
    dist = v[0]*normal[0] + v[1]*normal[1] + v[2]*normal[2]
    projected_point = (point.x-dist*normal[0],
                       point.y-dist*normal[1],
                       point.z-dist*normal[1])
    return projected_point
entropy
  • 3,134
  • 20
  • 20
  • Thankyou for sending over this!, Would you mind explaining which variables I'd have to define my self? and what they would have to be? Example: points, would have to be an array of the selected points? – Shannon Hochkins Mar 06 '13 at 23:02
  • Yes, points would have to be an array of points. I'm assuming each point has a `normal` property the point itself and the `normal` property have `x` `y` and `z` properties. As I said I've never used maya so I have no clue about what the actual format will end up being. Is maya even scriptable in python? – entropy Mar 06 '13 at 23:08
  • Essentially, if you can get your data into that format(a set of points with x,y,z coordinates as well as a normal which has x,y,z) then all you'd need to do is call `find_plane()` on the points and then pass each point in turn with the plane to `project_point_onto_plane()` and then move the point to wherever it tells you to – entropy Mar 06 '13 at 23:10
  • PS: I'm assuming you understand python given that I took a quick look at your profile. If I was wrong let me know and I'll try to explain a bit more about how this works – entropy Mar 06 '13 at 23:10
  • If I query a single point normal in maya, it returns the xyz coordinates for all it's surrounding points. Example: `polyNormalPerVertex -q -xyz; // Result: -0.000384672 0.499967 0.866045 -0.000384672 0.499967 0.866045 -0.000384672 0.499967 0.866045 -0.000384672 0.499967 0.866045 // `. Would this still work? – Shannon Hochkins Mar 06 '13 at 23:18