Why am I inconsistently getting my outer faces going the right direction

I’m not really understanding why sometimes the faces are correct like picture 1 and sometimes they are not correct like picture 2.

pic 1

pic2

example of the issue

code

SKETCHUP_CONSOLE.clear
Sketchup.file_new

# Default code, use or delete...
mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
sel = mod.selection # Current selection

group = ent.add_group
trimmer_group = ent.add_group
face = group.entities.add_face [0,0,0],[0,10,0],[10,10,0],[10,0,0]
face.reverse! unless face.normal.z == 1
edges_to_follow = face.edges
e1_dir = edges_to_follow[0].line[1]

min_v = [0,0,0]
d = 1
z = 1
o = 0

cut_vector = [-d,d,-z-o]
up_vector = [d,d,z-o]
down_vector = [d,d,-z-o]
trim_vector = [-d,d,z-o]
p " e1 direction = #{e1_dir}"
trimmer_pts = [ min_v.offset(cut_vector),min_v.offset(trim_vector),min_v.offset(up_vector),min_v.offset(down_vector)]
trim_face = trimmer_group.entities.add_face trimmer_pts


p " trim_face direction = #{trim_face.normal}"
trim_face.reverse! unless trim_face.normal.samedirection?(e1_dir)
p " trim_face direction = #{trim_face.normal}"
trim_face.followme edges_to_follow

pts = [ min_v.offset(cut_vector),min_v.offset(down_vector),min_v.offset(up_vector)] 

# add cut face
cut = group.entities.add_face pts
p " cut direction = #{cut.normal}"
# make cut face follow edges
cut.reverse! unless cut.normal.samedirection?(e1_dir)
p " cut direction = #{cut.normal}"

cut.followme edges_to_follow

The winding order of vertices matter.

Also the ground plane may have special behavior. It is expected that faces drawn on the ground plane will be pushpulled upward, so these faces are made to originally have their normal facing downwards so that the face will be facing outward in the resultant 3d solid.

The winding order of the vertices for the face being used for followme or the ones for the bottom face making up the edges that I am following?

Perhaps better as …

face.reverse! unless face.normal == Z_AXIS

If you wanted to test facing downwards …

face.reverse! unless face.normal == Z_AXIS.reverse

If you look at the API doc for the Toplevel namespace you’ll see the API defines
X_AXIS, Y_AXIS, Z_AXIS (vectors), ORIGIN (Point3d) and IDENTITY (Transformation)
that we can use in our code. (Just don’t change any of them as everyone uses them.)

I mean that the direction of the normal (facing direction) will depend upon the order that you give when creating faces.

And that you should beware of the ground plane, because in some situations SketchUp will automatically reverse the face so it faces downward.

I see what you are saying. But I am still getting the issue where the face that I am using for followme produces an inside out solid inconsistently.

With respect to followme, it may depend upon how the winding order / normal direction of the face relates to the follow path.

Think of the first part of follow path as a directional vector.
You’d want the face that follows the path to be facing in the opposite direction (I believe.)

Does it change spontaneously? I thought it depended upon the input for entities.add_face. If I input the face points the same every time shouldn’t it produce a consistent set of vertices.

I doubt it.

See TIG’s words here …

I would seek to be consistent.

But whenever you create something, check it’s validity. That it was actually created.
Many API factory methods will return false and create nothing if there is an error.

Regardless of how and where in 3D you create the face, with regard to followme
ensure that:

  • the face is perpendicular to the path
  • the face normal is pointing in the opposite direction than the followme path vector

EDIT

  • the face is positioned at the beginning of the follow path
    The face actually doesn’t need to be touching or “on” the path.
    But it may be best if the beginning of the path and the face are on the same plane.
    If not, the followme will move the face to the plane where the path begins.

I’ve found that by doing that you sometimes create a new edge that nulls out part of the edges being followed.

Example of issue

Relevant code

# Default code, use or delete...
mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
sel = mod.selection # Current selection

group = ent.add_group
trimmer_group = ent.add_group
face = group.entities.add_face [0,0,0],[0,10,0],[10,10,0],[10,0,0]
face.reverse! unless face.normal.z == 1
edges_to_follow = face.edges
e1_dir = edges_to_follow[0].line[1]

min_v = [0,0,0]
d = 1
z = 1

cut_vector = [-d,0,0]
up_vector = [d,0,2*z]
down_vector = [d,0,0]

p " e1 direction = #{e1_dir}"

pts = [ min_v.offset(cut_vector),min_v.offset(down_vector),min_v.offset(up_vector)] 

# add cut face
cut = group.entities.add_face pts
p " cut direction = #{cut.normal}"
# make cut face follow edges
cut.reverse! if cut.normal.samedirection?(e1_dir)
p " cut direction = #{cut.normal}"

cut.followme edges_to_follow

The face#edges method gives you the edges in random order.

The
face#outer_loop
with
loop#edges
Get an array of the edges that define the loop in an ordered sequence.

edges_to_follow = face.outer_loop.edges
e1_dir = edges_to_follow[0].line[1]

Then you will get more consequent consistent result.

1 Like

… yes and consistent also. :wink:

@cwatts, I posted a link above where TIG shows using the #outer_loop method and explains the winding order.

1 Like

Yeah, thanks for that. I noticed however, it still does the flipping back and forth between correct and incorrect even with the outer_loop.edges.

gif

code

# Default code, use or delete...
mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
sel = mod.selection # Current selection

group = ent.add_group
face = group.entities.add_face [0,0,0],[0,10,0],[10,10,0],[10,0,0]
face.reverse! unless face.normal.z == 1
edges_to_follow = face.outer_loop.edges
e1_dir = edges_to_follow[0].line[1]

min_v = [0,0,0]
d = 1
z = 1

cut_vector = [-d,0,0]
up_vector = [d,0,z]
down_vector = [d,0,0]

p " e1 direction = #{e1_dir}"

pts = [ min_v.offset(cut_vector),min_v.offset(down_vector),min_v.offset(up_vector)] 

# add cut face
cut = group.entities.add_face pts
p " cut direction = #{cut.normal}"
# make cut face follow edges
cut.reverse! if cut.normal.samedirection?(e1_dir)
p " cut direction = #{cut.normal}"

cut.followme edges_to_follow

Edit was to change unless to if in cut.reverse! if cut.normal.samedirection?(e1_dir)

I guess when you crate the cut face, it’s bottom edge is overlapping and split the edge of the base face, and destroy the loop. This is preventing the followme to run through on the full path too.

Try with this: move the cut face in direction of y a little

cut_vector = [-d,1,0]
up_vector = [d,1,z]
down_vector = [d,1,0]

Yeah shifting the base fixes that issue (Problem for me is that I collect the followme edges in my big program before I create the cut) but the problem of the faces being inside out still persists. I’m trying to figure it out still but not having a ton of luck.

Okay this example works …

def doit
  mod = Sketchup.active_model # model reference
  mod.start_operation("Follow Me Test",true)
  #
  ###
    #
    ent = mod.entities # All entities in model
    #sel = mod.selection # Current selection

    group = ent.add_group
    face = group.entities.add_face [0,0,0],[0,10,0],[10,10,0],[10,0,0]
    face.reverse! unless face.normal.z == 1
    pts = [[-1,-1,-1], [-1,12,-1], [12,12,-1], [12,-1,-1]]
    face_to_follow  = group.entities.add_face(pts)
    edges_to_follow = face_to_follow.outer_loop.edges
    
    e1_dir = edges_to_follow[0].line[1]

    min_v = [0,0,0]
    d = 1
    z = 1

    cut_vector = [-d,0,0]
    up_vector = [d,0,z]
    down_vector = [d,0,0]

    p " e1 direction = #{e1_dir}"

    #pts = [ min_v.offset(cut_vector),min_v.offset(down_vector),min_v.offset(up_vector)]
    pts = [ [-1,-1,0], [1,1,0], [1,1,1] ]

    # add cut face
    cut = group.entities.add_face pts
    p " cut direction = #{cut.normal}"
    # make cut face follow edges
    cut.reverse! if cut.normal.samedirection?(e1_dir)
    p " cut direction = #{cut.normal}"

    cut.followme edges_to_follow
    
    group.entities.erase_entities(edges_to_follow)
    #
  ###
  #
  mod.commit_operation
  #
end

The lesson is that when we use a loose collection of edges, the final vertex is not merged, so followme doesn’t work well.

So instead create a followme face (which merges the beginning and end vertices, and specifically use that face’s outer_loop.edges array, which is returned in the correct order.

Also the follow path apparently needs to be the same size as that which the outermost vertex of the cut face will sweep. This bevels the final result at the start and end of the follow loop.

Lastly cleanup the followme face by deleting it’s edges.

This is part of a larger program I am making. Does this mean that I shouldn’t use vectors? It seems like this solution works for this case but it isn’t translating correctly for some reason to my full program.
I will release it at some point soon (planning on it being open source, need to get rid of ip).

You need to be more specific with this question. Use vectors for what ?
(And FTR, I don’t think I said anywhere above not to use vectors, … specifically or generally.)

(EDIT: Answered below.)

You showed an example with 2 issues. The faces were backside out … and the extrusion face was not forming a correct bevel at the start and end of the follow path. You gave the code that produced the problems.

I edited it so it worked. And noted a few things.

Yes, I will say that API coders have many issues using the API equivalents of PushPull and FollowMe tools. The forums have a lot of good advice. Search the main SketchUp category for using these tools manually, and you’ll learn of tricks and quirks that also apply to coding.
Likewise search this category for the same key words. Many code snippets are likely to be found.

Oh, … I see why you asked this. I commented out the line where you offset a point (that equals the predefined ORIGIN,) with 3 arrays acting as vectors.

I just didn’t care why you were doing this (as it likely comes from somewhere else in your code.)

I only wished to create the cut face where it needed to be for the example. So I commented your line, and just used a literal array of point arrays to put cut face where it needed to be.