A vector perpendicular to edge

Hello everyone and happy new year!

I have a difficult problem that I cannot find a solution for, but it seems so simple that perhaps i have overlooked something.

I want to make a vector that is perpendicular to an edge. Meaning, the vector should be on the plane of the connected face, going from the edge and perpendicularly out from the face. Does it make sense? Please see this image. The lone edge pointing out from the face should be the vector.


I could take the face edge and rotate it 90 degrees with the plane axis, but that does not ensure that the vector always goes in the right direction. Can I somehow detect which direction is away from the face? Or is there another solution?

I really hope someone can help me. Thank you!

Use the Line tool, click a point within the face [not on an edge].
An inference color will show as you move your cursor up from the face - an axial-color if the face is flat in an axis, or magenta otherwise as ‘perdendicular’ - or black of not near perpendicular.
Click to make the end point of the line.
That line is now perpendicular to the face.
Exit the Line tool and Select the line, click its start-end as your anchor point and drag the line to the edge of the face where you want this to be…

I guess this is nothing else just a normal vector of the face. (Or reverse)


If you are really trying to do this in code then norm = face.normal gives you the [unit] vector - add a new line using that vector to offset a point on an face-edge as the line’s end [ entities.add_line(startpt, startp,offset(norm, length)) ] - the normal it is always ‘away from the face’ - but in your illustration the face is reversed so it’ll be ‘down’, when I expect you want ‘up’…

I am sorry, I was not being clear. I mean to do this in ruby.

The vector I am trying to make is not perpendicular to the face, but perpendicular to an edge of the face. It is on the face plane, going perpendicularly from the face edge and out.

Maybe this illustrates it better:


The only almost fool proof but highly inelegant method I can think of is to offset the edge along the vector and check if the face area increases. If it does, the vector goes in the right direction, else I must reverse it. But that is a very ugly solution I think, is there a way to know what side of the edge has the face on it, or which direction of the vector goes away from the face?

So, you want vector that’s parallel to the plane of the face, and also perpendicular to the edge…
You have the edge from which you can get the start & end and use those to make a vector, current parallel with that edge
You have the face.normal vector.
Make a transformation which rotates -90.degrees about a point on the edge and uses the face.normal vector as its axis of rotation. The -ve angle should move the new edge off face.
You might need to check if the edge is reversed in the face and adjust the angle accordingly…

Thank you TIG. I have been around these forums for more than a decade and very happy and honored to speak with you.

I should have shown you the code I had made which only works half the time.

vector = edge.start.position.vector_to edge.end.position
vector_trans = Geom::Transformation.rotation(edge.bounds.center, edge.faces[0].normal, -90.degrees)

Depending on the direction of the edge this produces a vector that goes either into the face or away from the face. But I would like for it to always go away from the edge. It is for scripting a tool that will do this routinely on many different edges, so I would like for it to do this automatically.

Perhaps you could create a test vector from edge start point to the face bounds center. Then check the angle between this test vector and your vector. If the angle less than 90 degrees you can reverse your vector.

Try this…

face = edge.faces[0]
ang = -90.degrees
ang = -ang if edge.reversed_in?(face)
vector = edge.start.position.vector_to edge.end.position
vector_trans = Geom::Transformation.rotation(edge.bounds.center, face.normal, ang)

I might have got the reversed_in? test backwards… if so, then you just need to change the 2nd line to
ang = 90.degrees
I can’t test it myself right now…

Then add the new edge
entities.add_line(edge.bounds.center, edge.bounds.center.offset(vector)

Thank you for your help, dezmo and TIG!

Oh my, I did not know the reversed_in method, and I am still not sure I understand what it does, but from my testing it seems to work as you wrote it. So this must be the answer! Brilliant!

I wasn’t aware that an edge could be reverse, and I am curious as to what it means. Does it mean that in a face with all unreversed edges, those edges always form a loop that goes counter clockwise around the face? I am sorry if it is a silly question and you don’t need to answer, I am just curious.

Thank you both!

Typically a face has an outer loop of edges which go ccw [relative to the face.normal], conversely any hole-loops in a face should have edges which go cw.
However, when you draw a flat face at z=0, irrespective of the direction of the ‘loop’. the face always faces down - assuming you want to pushpull up [this happens in code and also when you add a face manually]
Also subsequent manipulations of geometry [e.g. face reversals] can also result in situations where a face can have one or more of its edges which ‘rotate’ about its normal in the ‘wrong’ direction…
The reverse_in? test tells you if the edge you are testing is oriented contrary to the ‘proper’ face loop direction of ccw etc…

You can test this yourself by mixing manual operations and some trial code…

An Edge has a start Vertex and and end Vertex. The “direction” of the Edge is from the start to the end.

As @TIG points out, in a Loop that defines the boundary of a Face, the Edges must proceed sequentially around the Loop. But, if a Face is created from pre-existing Edges, they might have their direction the wrong way for the Loop. For example, this always happens if the Face shares an Edge with an adjacent Face with its front side pointing the same way. That is, since each Face must trace its edges (say) ccw, they will go through the shared Edge in opposite directions.

To allow for this, a Loop actually refers to Edges via an intermediary EdgeUse object. One of the things an EdgeUse does is to track whether the Edge must be used in its natural direction or reversed so as to follow sequentially around the Loop. That’s what the reversed? method on an EdgeUse or the reversed_in? method of an Edge tells you.

What about?

center = face.bounds.center
edge_pt = center.project_to_line( edge.line )
vector = center.vector_to( edge_pt )

The vector from the face center to an edge of an irregularly shaped polygon is not guaranteed to be perpendicular to that edge.

Apologies for my late reply.

Thank you so much for the diagram of loops, slbaumgartner. I think this is so essential that it ought to have a place in the ruby documentation. Perhaps if Trimble is listening, though I know the ruby documentation has always had many deficiencies. I never quite understood the concept of edgeuse, but this example brought me closer.

I too at first thought of using the face.bounds.center, but it would not always work, as for example in this case where the center is “in front” of the vector.


TIG’s solution reversing edges works in all situations I have tested.

FYI, I did not use the face’s centroid. The code uses the center of the bounding box surrounding the face.

Also, the Geom::Point3d#project_to_line method picks the closest point on the line (in this case an edge’s line,) which should be guaranteed to use a perpendicular ray to find the point.

Steve, can you show with diagrams a scenario in which this would not be so ?

What you show in your image is an arrow (placed in a certain position) not a vector.

A vector is an object that points in a direction and has magnitude. Such a vector can be placed anywhere (or used in your case to draw an arrow) wherever desired.

But I do see your point in this scenario. Perhaps it would be easier to just use the bounding box of the edge itself ? I was thinking this when I wrote the example above.

Anyway, if you are satisfied with the solution you have, we can move on.

Your assumption is that the center of the bounding box lies on the face. Not necessarily so. Thus cannot be used to determine the direction of the outward normal.

However, my statement in the previous post was not correct, as you pointed out. It will be perpendicular, however not always in the desired direction. But I did like the elegance of your snippet.

Yes the OP pointed this out with weirdly shaped faces.

I suppose we could test the “center” point using Sketchup::Face#classify_point().

But I suppose it’s all moot now.

1 Like