Page MenuHome

Expose BM_face_calc_plane in python API
AbandonedPublic

Authored by Campbell Barton (campbellbarton) on May 11 2016, 8:50 PM.

Details

Summary

This patch exposes the internal bmesh function "BM_Face_calc_plane" (used to create negative Y axis for the normal transform orientation) in the python API. This would be useful for python scripts that need to align objects to the faces of a mesh.

The function can be accessed like this:

BMFace.calc_plane() # Returns a Vector representing the negative Y direction along the face

Diff Detail

Repository
rB Blender
Branch
arcpatch-D1988

Event Timeline

Isaac Weaver (wisaac) retitled this revision from to Expose BM_face_calc_plane in python API.
Isaac Weaver (wisaac) updated this object.
Isaac Weaver (wisaac) set the repository for this revision to rB Blender.
Campbell Barton (campbellbarton) requested changes to this revision.EditedJun 2 2016, 6:40 AM
Campbell Barton (campbellbarton) edited edge metadata.

BM_face_calc_plane was written so we can get an orientation from the face for the manipulator and transform code.

Its not really intended as a low level API feature (could be moved out of BMesh API, its just convenient to keep it there).

The reason not to expose this is that its more of a best-guess then some exact value to use as a basis for further calculations.


For this to be exposed, it would make more sense to have the different behaviors exposed as different methods, as we do for center.

eg:

  • calc_tangent_edge(): pick the longest edge.
  • calc_tangent_edge_pair(): 2 longest disconnected edges (useful for quads)
  • calc_tangent_edge_diagonal(): the edge furthest away from any other vertex in the face.
  • calc_tangent_vert_diagonal(): longest distance between vertices on the face.

Then developers can pick a mothod that makes sense for their usage and gives predictable results.

This revision now requires changes to proceed.Jun 2 2016, 6:40 AM

Should calc_tangent_edge_pair use the longest edge and the second longest edge that isn't connected to the longest? Or should it use the two edges that have the longest length when added together? E.g. in the image below, the red edges when added together are longer than the blue edges, but the longest edge in the entire shape is blue.


Also, what should it do in the case of a triangle where there are no two disconnected edges?

@Isaac Weaver (wisaac), In general I think its best to prefer methods give most predictable results (least likely to jitter based on minor changes to the shape).

The problem with picking the longest edge is there may be 2 adjacent edges which are the same length, where the longest will be picked based on some minor difference caused by float precision,
so think it makes most sense to pick the longest edge pair (which aren't connected).

For tri's, picking 2 longest should be fine.
This is a little more complicated in the case of ngons... for even number of sides we _could_ pick the longest edge-pair too... but then we need to use some different method for odd-numbers of sides (possible but seems a bit arbitrary). So think it may be OK to in this case to pick the longest edge... then the second, disconnected longest edge.

Isaac Weaver (wisaac) edited edge metadata.
Isaac Weaver (wisaac) removed rB Blender as the repository for this revision.

@Campbell Barton (campbellbarton) I've implemented those four functions, while doing some testing I thought it might be a useful to add a fifth function calc_tangent_edge_center_diagonal which would use the vector going from the farthest vert to the center of the edge (useful for tris and odd sided ngons) Thoughts?

Just for fun, here's the result from profiling the performance of calling each function on every face in a torus with 20,992 faces (all quads):

      83974 function calls in 0.093 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.005    0.005    0.093    0.093 <string>:1(<module>)
     1    0.024    0.024    0.088    0.088 Text:6(main)
     1    0.000    0.000    0.000    0.000 {built-in method bmesh.new}
     1    0.000    0.000    0.093    0.093 {built-in method builtins.exec}
 20992    0.005    0.000    0.005    0.000 {method 'calc_tangent_edge' of 'BMFace' objects}
 20992    0.012    0.000    0.012    0.000 {method 'calc_tangent_edge_diagonal' of 'BMFace' objects}
 20992    0.004    0.000    0.004    0.000 {method 'calc_tangent_edge_pair' of 'BMFace' objects}
 20992    0.009    0.000    0.009    0.000 {method 'calc_tangent_vert_diagonal' of 'BMFace' objects}
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     1    0.034    0.034    0.034    0.034 {method 'from_mesh' of 'BMesh' objects}
Campbell Barton (campbellbarton) requested changes to this revision.Jun 9 2016, 10:49 AM
Campbell Barton (campbellbarton) edited edge metadata.

Checked over this patch, with some minor requests.

As for calc_tangent_edge_center_diagonal, For triangles this makes sense, for more verts the results may seem haphazard...
Also, this is what BM_face_calc_tangent_edge_pair would be (for triangles), if both edge directions were normalized before adding.

source/blender/bmesh/intern/bmesh_polygon.c
368–382

This can be done with less checking, looping.

Keep the loop from BM_face_find_longest_loop, then walk over the adjacent loops starting at longest_loop->prev->prev, finishing when you hit longest_loop->next.
This way theres no need to check if the edges are adjacent.

400–441

Again, this could use the current loops next/prev to avoid checking against its self.

Also think it might be better to split this into 2 functions (just keep them together and note its nearly the same logic).

This revision now requires changes to proceed.Jun 9 2016, 10:49 AM
Isaac Weaver (wisaac) edited edge metadata.

I made the requested changes (plus a little cleanup)

I also changed BM_face_calc_tangent_edge_pair to normalize the edge directions for triangles. After some more testing, I think the result is a lot more useful this way.

Looks good, think it makes sense if BM_face_calc_plane calls out to these functions (could rename to BM_face_calc_tangent_generic or BM_face_calc_tangent_auto).
Then we don't need to maintain multiple methods.

Isaac Weaver (wisaac) edited edge metadata.

I rewrote BM_face_calc_plane to call BM_face_calc_tangent_edge for ngons and BM_face_calc_tangent_edge_pair for quads. It still calculates the tangent for triangles the same way (uses the shortest edge), not sure if it makes more sense to use BM_face_calc_tangent_edge_pair for that too.

Also, I removed the note from the doc string about the result not being normalized.

Making some updates on this patch (minor changes before accepting).

  • Fix ngon edge-pair bug, edge v1/v2 direction doesn't follow the face

Various tweaks to logic:

  • rename BM_vert_tri_calc_plane -> BM_vert_tri_calc_tangent_edge
  • add BM_vert_tri_calc_tangent_edge_pair which works similar to BM_vert_tri_calc_tangent_edge, however instead of using the unique edge, use the center-line between it and the adjacent vertex.
  • both BM_vert_tri_calc_tangent_edge and BM_vert_tri_calc_tangent_edge_pair normalize their result (so callers don't need to remember which functions do/don't normalize the tangent). but normalizes both edges adjacent to the most unique edge (before adding).
  • Add fallback for when the ngon has 2x edges that are the same length, pointing in the same direction (could give a zero length normal). Even with this change - this part is susceptible to float precision error causing jitter in the result.

BM_face_calc_tangent_edge_diagonal: Use real length between verts and edges

Instead of using the edges center point, calculate closest_to_line_segment_v3 from the vertex to the edge.

Make sure r_tangent is set, even when degenerate faces are passed in.

BM_vert_tri_calc_tangent_edge_pair: Use a better method of calculating the center-line

Own update for this function was incorrect (should have normalized hothe edges before adding).
However this isn't needed if we use the center-point from the edge to the opposite vertex.

  • Optimize BM_vert_tri_calc_tangent_* functions, avoid sqrt, use squared lengths instead.
  • De-duplicate code to find the unique edge