Orienting Faces and the API


#1

It seems there is no easy way to orient faces of a solid with a single method within the API.

I’m working with non-orthogonal prismatic solids (6 faces, 12 edges) so nothing too complicated here.

Since I can’t push-pull this geometry into existence I first calculate my vertices (points) and then draw lines between them and finally use the find_faces method to created the faces.

Sometimes all of my faces are reversed and sometimes they are all oriented correctly.

Here is the code:

group1cut = Sketchup.active_model.active_entities.add_group
entities1cut = group1cut.entities

line1 = entities1cut.add_line p1,p2
line2 = entities1cut.add_line p2,p3
line3 = entities1cut.add_line p3,p4
line4 = entities1cut.add_line p4,p1

line1a = entities1cut.add_line p1a,p2a
line2a = entities1cut.add_line p2a,p3a
line3a = entities1cut.add_line p3a,p4a
line4a = entities1cut.add_line p4a,p1a

linea = entities1cut.add_line p1,p1a
lineb = entities1cut.add_line p2,p2a
linec = entities1cut.add_line p3,p3a
lined = entities1cut.add_line p4,p4a

line1.find_faces
linec.find_faces
line4a.find_faces

I’m wondering if anyone has come up with a good algorithm for checking to see if a solid has its faces oriented properly?

I’m thinking I can probably create my own algorithm for this sort of thing, here is my proposed recipe:

1.) Determine a point inside the solid (group or component)

2.) Use the project_to_plane method to cycle through all of the faces of the solid, calculate their planes and then use the projected point to calc the vector then compare this vector to the face normal vector and if they aren’t in the same direction, reverse the face.


#2
 vcp = Geom::Vector3d.new(bounding_box.center.position.to_a)
 fcp = Geom::Vector3d.new(face.bounds.center.to_a)
(vcp - fcp).samedirection? face.normal

or something like that…

I’ll see if I can find some code a bit later…

john


#3

Maybe something along these lines:

# Check that faces are oriented correctly

cp = [((p1.x + p4a.x)*0.5), ((p1.y + p4a.y)*0.5), ((p1.z + p4a.z)*0.5)]
group1faces = group1cut.entities.grep(Sketchup::Face)
	
group1faces.each{|facei|
	normali = facei.normal
	planei = facei.plane 
	projpti = cp.project_to_plane(planei)
	veci = projpti - cp
	unless normali.samedirection?(veci)
		facei.reverse!
	end
}

#4

i’ve never used project_to_plane, but have a few that use

Geom::Point3d.linear_combination(0.5, pt1, 0.5, pt2)

which probably does something similar to yours…

john


#5

A couple of questions:

Why are you creating Edges and then finding their faces rather than using Entities#add_face ? It will automatically create the required Edges to form the outer loop of the face. Furthermore, unless the Face lies on the z==0 plane, the Face will be created so that the Vertices trace in counter-clockwise order when you view the Face along the reverse of the normal vector. In other words, by choosing the ordering of the points, you also choose the direction of the normal.


#6

You do have a good point. However, I can’t rely on the ordering of the points it could flip to CW or CCW, so I have to have a check in place regardless of how I create the solid.


#7

@slbaumgartner makes a good point that I was going to mention, but it;s coming up 02.00 here so I’ll just grab a snippet…

it’s missing some prior bits…

      # ruby handles parallel assignment
      r, g, b, r1, g1, b1 = pt1.x, pt1.y, pt1.z, pt2.x, pt2.y, pt2.z
      
      all = [[[r, g, b], [r1, g, b], [r1, g, b1], [r, g, b1]], # front
             [[r1, g1, b1], [r1, g1, b], [r, g1, b], [r, g1, b1]], # back
             [[r, g, b], [r, g, b1], [r, g1, b1], [r, g1, b]], # left
             [[r1, g1, b1], [r1, g1, b], [r1, g, b], [r1, g, b1]], # right
             [[r, g, b], [r1, g, b], [r1, g1, b], [r, g1, b]], # bottom
             [[r1, g1, b1], [r, g1, b1], [r, g, b1], [r1, g, b1]]] # top
      i = 0
      vcp = Geom::Vector3d.new(cp.position.to_a)
      all.each do |f|
        face = gents.add_face f
        i += 1
        fcp = Geom::Vector3d.new(face.bounds.center.to_a)
        flip = (vcp - fcp).samedirection? face.normal
        # JcB::Report.echo " face norm #{i} #{flip}"
        face.reverse! unless flip
        face.material = frnt
        face.back_material = back
      end

#8

Your right this is cleaner:

cp1 = Geom::Point3d.linear_combination(0.5, p1, 0.5, p3a)

Jeez, I find a new method every day, the API never fails to amaze me.


#9

This could be a good aspect to explore. Why not? Where are you getting the points from? If they are from the vertices of another Face, you can use its normal and the EdgeUse objects from its outer Loop to know which way they trace around the Loop hence which way the new Face’s normal will face. I don’t claim that would necessarily be shorter code than what @john_drivenupthewall showed, but it might be less vulnerable to edge cases and broken assumptions.


#10

Ultimately the user can select three points in any order which then defines the plane (and point) which I am calculating my points from. If the user selects the points in a certain order it will flip the orientation and the resulting solid I create could have reversed faces. Hence the need to run a check on the solids face orientations.


#11

This problem can not be solved with a single API call. Faces on a solid are oriented consistently if for a given edge the two attached faces use the edge in opposite directions in their faceloops.

Given a face (face0) which has correct orientation, an edge (egde) of face0 and and another face (face1) also connected to the edge, we can orient face1 with the following code:

face1.reverse! if edge.reversed_in?(face0) == edge.reversed_in?(face1)

So, given a correctly oriented start face we can orient all other faces consistently.

This is done in an extension like SolidInspector2 which builds on an old extension called Shellify (attached below). A modified version of Shellify which does not remove faces but only orients them correctly provided the input is a solid is also attached.

To try out the script, run it on a solid with one or more groups selected.

shellify.rb (4.7 KB)
solid_face_orienter.rb (3.1 KB)


#12

There is an Orient Faces function in SketchUp. Works best on manifolds. We could expose that to the API.
File an feature request if it would be of interest: https://github.com/SketchUp/api-issue-tracker/issues