# add a face
face = ents.add_face(cir)
# collect the end position of each edge
verts = []
# these will be in a useable order
face.outer_loop.vertices.each{|v| verts << v.position}
That does appear to get them into an ordered array but now I’ve got to find the correct starting point of the array so that transform_by_vectors method applies the correct vector to the correct point:
In the image below the right side of the footing has been properly modified by the transform whereas the left side is jumbled up because the incorrect vectors are being applied to the specific points. Both the points and vectors are ordered in this case but they are not synchronized, or more correctly clocked to the same starting point. The other thing I didn’t think about is that the order may be also reversed even though it is ordered.
It sure would be nice if I had an easier way to modify these faces for an angled cut but unfortunately I can’t rely on a boolean operation in this instance since the two end of the footings may actually contact or overlap each other and then the cut operation would inadvertently cut the other end and vice versa.
Somehow I manage to get myself into some real messy topological problems. I guess this is the plight of a plugin developer.
So now I need to figure out which point in the array to search for and then once I find it I need to reorder the array so that the order is maintained but the starting point is the correct point that I have identified.
Amazing, it actually seems to be working, this is what I have so far:
ftg_start_vertices.clone.each do |vertex|
if vertex == testpt1
break
else
ftg_start_vertices.push(ftg_start_vertices.shift)
end
end
Now I just need to devise a method to check the order and then reverse it if needed. To do this I will probably need to ascertain another test point to compare against.
# Returns a new array (regardless)
def reorder_array(ary, testpt)
i = ary.index(testpt)
return [] if i.nil? # not found
i > 0 ? ary.values_at(i..-1, 0..i-1) : ary.clone
end
You are attempting a multistep process of creating and modifying geometry where some of the steps use complex methods (like follow me or booleans). The problem is that those methods don’t return sufficiently detailed info about the geometry they create so that you cannot easily select only a subset for the next step. To find the end face of a follow me, you have 2 options:
create a bounding box to select the end face at the last vertex of the edge array.
Code your own “follow me” and return the end face.
Code your own “follow me” which handles the case where the edge array is a closed loop. No need to alter the end face to miter it.
Often we code to create geometry, then modify in the same steps that a user would. But modifying geometry is risky. Sometimes it’s better to query the geometry, then delete it and recreate it in modified form. Users, of course, don’t like to do that because it’s often easier for them to modify and they can adjust their steps based on the feedback they get. But for coders, deletion and recreation is sometimes the best strategy.
I have no idea how to create a follow me method nor do I have the programming chops to pull it off.
My best bet is to try and use the built in methods in the API and then cobble together something that can modify the resulting geometry in a fairly predictable and stable fashion.
loops = faces.loops # all of the face’s loops oloop = face.outer_loop # outermost perimeter iloops = loops - [oloop]#other inner loops [aka holes]
oloop.vertices are arranged ccw around the face.normal, unless the face is flat and it is exactly at Z=0 and then face.normal==Z_AXIS.reverse just like manually drawing the face. So trap for that.
each of the iloop’s loop.vertices are arranged cw around the face.normal [see above about exception]
face loops are always ordered one after the other… even if its starting vertex might seem random…
Seems to be working. My final code after some corrections:
testpt1 = Geom::Point3d.new(xvalue, @Ftgx1, @Ftgy1)
testpt1.transform!(tr2s)
testpt1.transform!(tr1_ftg)
testpt2 = Geom::Point3d.new(xvalue, @Ftgx2, @Ftgy2)
testpt2.transform!(tr2s)
testpt2.transform!(tr1_ftg)
startloop = new_face2.outer_loop
ftg_start_vertices = startloop.vertices
ftg_start_vertices.clone.each do |vertex|
if vertex.position == testpt1
break
else
ftg_start_vertices.push(ftg_start_vertices.shift)
end
end
if ftg_start_vertices[1].position != testpt2
ftg_start_vertices.push(ftg_start_vertices.shift)
ftg_start_vertices.reverse!
end
entities2.transform_by_vectors(ftg_start_vertices, ftg_start_vecs)
I would use the rotate method but the push-shift method is more intuitive for me and when I come back to this code at a later date I will be able to follow it better.
Thank-you to everyone who contributed and help me work my way through this, for a while there I was beginning to think that there might not be a solution.
I will say that transform_by_vectors method is a very hand tool to have in one’s toolbox however my problem has been figuring out the vertices of the face so they properly sync with the vectors. This bit of code discovered today will find its way into many more places, I am sure.