Creating new face from the edges of multiple Faces

face

#1

Hi all,

I can be able to get all edges of the Faces I have in my model. Now, I’m looking for a code helps to create a new Face from the edges of multiple different Faces in my model. So the new Face will be combined from multiple separated faces.

Please let me know if you did it already or any suggestions always be helpful. Thanks so much.


#2

Hi Tin,
it is not exactly clear to me what you are trying to achieve. When one is talking about all (entities) in a model, they can be in all kinds of configurations or topology or connectivity. When selecting them for an operation, one must be sure (validate, not assume) that they fulfill the preconditions for that operation.

The method Sketchup::Entities#add_face(edges) can only create a face from edges that form a connected closed loop and are planar. It cannot create multiple faces for multiple disconnected edge loops and it cannot create a face for edges that contain stray edges (returns nil).

If you want to create multiple faces for multiple planar edge loops, you need to filter all edges that form loops and then apply the method on each, one at a time. You could for example use Sketchup::Edge#find_faces (but it is not predictable which faces are created and they are not returned):

entities_before = edges.map(&:all_connected).flatten.uniq

number_of_faces_created = edges.map{ |edge| edge.find_faces }.reduce(0, &:+)

entities_after = edges.map(&:all_connected).flatten.uniq
faces_created = entities_after - entities_before

For which edges do you want to create faces? Do you want to create a face for an open loop (edges are open on one side)? Do you want to create a face for non-planar loops (close holes in a mesh)? Then you need a skinning algorithm and triangulate the the hole.


#3

Thanks @Aerilius, Please see the screenshot below to get what I’m trying to do

image

I have 4 Faces in the model (same Z axis). I’m trying to create a new Face by Ruby API that’s made by only all outer borders of 4 Faces and remove any inside borders.


#4

One possibility is to query all the edges, and delete those that are shared by more than face ( # all_connected ). Those are the common edges, and removing them will only leave you with single interior and exterior edges.


#5

Finally, I can make it work with the code below

# @return [Array<Sketchup::Edge>]
def self.create_fallzone(selection_faces)
    # Replicate other loops of each face
    loops = Set.new
    selection_faces.each { |face| loops.merge(face.loops) }
    puts "All loops: #{loops.size}"
    loops.each { |loop| puts "Outer? #{loop.outer?}" }

    outer_loops = []
    selection_faces.each { |face| outer_loops << face.outer_loop }
    puts "Outer loops: #{outer_loops.size}"

    # Collect all edges for the faces in the selection set for fast lookup.
    edges = Set.new
    selection_faces.each { |face| edges.merge(face.outer_loop.edges) }

    puts "before size= #{edges.size}"

    edges = edges.to_a.flatten.uniq

    puts "after size= #{edges.size}"

    group = selection_faces.first.parent.entities.add_group
    gents = group.entities

    edges.each{ |e| gents.add_line(e.start, e.end) }

    # Create all faces of edges
    gents.grep(Sketchup::Edge).each{ |e| e.find_faces unless e.faces[0] }

    nonshared_edges = []
    shared_edges = []
    gents.grep(Sketchup::Edge).each do |e|
        if e.faces.size === 1
            nonshared_edges << e 
        else
            shared_edges << e
        end
    end

    puts "Non Shared Edges: #{nonshared_edges.size}"
    puts "Shared Faces: #{shared_edges.size}"

    # Remove all shared edges
    gents.erase_entities(shared_edges) if shared_edges[0]

    # face = gents.grep(Sketchup::Face)[0]
    # verts = face.outer_loop.vertices

    Sketchup.active_model.entities.add_face nonshared_edges

    group.erase!

    # Find the edges that connects to only one of the faces in the selection.
    # border = edges.select { |edge|
    #     num_faces = edge.faces.count { |face| selection_faces.include?(face) }
    #     num_faces == 1
    # }

    verts
end

However there still be some issues

I would like to make the face to be full-filled completely. Not sure how can do it.


#6

once you have all your geometry in a single group…
read the comments in this snippet…

# you need to to two things so make them methods
def fill_voids(edges)
	edges.each {|e| 
		next if e.faces.length != 1
		e.find_faces
	 }
end # fill_voids()

def erase_coplaner(edges)
	# erase hidden or coplaner but avoid others
	edges.each do |e|
	  next if e.deleted?
	  e.erase! if e.faces.length == 0
	  next if e.deleted?
	  e.erase! if e.faces.length == 2 &&
		e.faces[0].normal.dot(e.faces[1].normal) > 0.001
	end
end # erase_coplaner()

# to test, overlap some faces, erase some internal faces,
# make remaining geometry a group and select it...
# collect all edges of interest
# this is simply using current selection 
grp = Sketchup.active_model.selection.grep(Sketchup::Group)[0]
grp_edges = grp.entities.grep(Sketchup::Edge)
# call the methods
fill_voids(grp_edges)
erase_coplaner(grp_edges)


john


#7

It works perfectly. Thanks @john_drivenupthewall