Hey all! I’ve seen a couple of different threads and discussions about the best way to make copies of Sketchup::Face
s using the Ruby API. Most of the advice seems to be to create the new face using the outer loop, and then punch holes using the inner loop. I tried playing around with various versions of that, including adding all the inner loop and outer loop edges (not using add_face
) and then invoking Edge.find_faces
.
None of them worked well with some of my faces, which had several holes punched in them. (And to be fair to the API, the Sketchup UI itself often struggles to find and punch them when I try to do so manually.)
Anyway, I digress: after playing around with a bunch of different methods, I came up with this one, which is based on using the face’s polygon mesh.
# Returns an array with the given edge's points sorted in a standard order for easy equality checks
# @param edge [Sketchup::Edge] the edge to normalize
# @return [Array<Geom::Point3d>] the start and edge points of the edge in normalized order
def self.normalized_edge(edge)
[edge.start.position, edge.end.position].sort_by { |pt| pt.x + pt.y + pt.z }
end
# Copies the edges and faces for the given face element to the given group
# @param face [Sketchup::Face] the face to clone
# @param entities [Sketchup::Entities] the entities list to clone the face into
# @return [Array<Sketchup::Face>] the cloned face(s) within `entities`; will generally be a single face
def self.clone_face(face, entities)
# Create a temporary group
temp_group = entities.add_group
# Remember the original edges so we can compare the new mesh edges with them later
original_edges = face.edges.map { |edge| normalized_edge(edge) }
# Use the face's mesh to add a bunch of connected polygons (triangles) to the temporary group
mesh = face.mesh
mesh.polygons.each do |polygon|
temp_group.entities.add_face(*(polygon.map { |index| mesh.point_at index }))
end
# Now we've got a whole bunch of interior edges for the polygons that don't belong on the new face. So we'll
# delete all the excess edges and call the remaining face(s) the result. There should generally only be one face
# left over, but it depends on lots of things going right ;)
(entities.grep(Sketchup::Edge).find_all { |edge| !(original_edges.include? normalized_edge(edge)) }).each do |edge|
edge.erase! unless edge.deleted?
end
new_faces = temp_group.entities.grep(Sketchup::Face)
# Explode the group to drop all the faces into the desired entity list
temp_group.explode
new_faces
end
So, as both a Ruby and a Sketchup API plugin n00b, my question is: does this make sense? Am I missing some significant gotchas?
Thanks for any insights y’all can provide