Su How to determine if a face contains another face?

I would like to know if ruby has a similar api interface to call directly when I want to determine if a face contains another face.

There are several topics here with the similar discussion with a similar conclusion : Not directly.

Some Ideas:


You can use bounding box of faces (The method is inherited from Drawingelement).
The Drawingelement #bounds method is used to retrieve the Geom::BoundingBox bounding a Sketchup::Drawingelement.

Then use BoundingBox #contains? method to determine if a bounding box contains a specific Point3d or the other face BoundingBox object.


You can use The Face #classify_point method to determine
if a given Point3d, aka the other Face #vertices - Vertex#position
is on the referenced Face.


For faces on a base plane, you can think about :The Geom .point_in_polygon_2D method which is used to determine whether a point is inside a polygon.


Edit:
In fact, the face object cannot contain another face object, because the other face object is another object. :wink:
It is possible that the vertices of inner loop of one face is the same position as the vertices of outer loop of the other face…

Can you post a screenshot or model of the scenario you have?

Do you have a face that contains another face in the same entities collection? Or are you trying to check two faces that are in different entities collections. The solution will depend on what scenario you have.


For example, if plane A contains plane B, how can we determine whether it is contained or not?

You mean “face” instead of “plane”, right? (Plane and face are different concepts in the API)

If you have face A, then you can check its inner loops define another face:

def contains_faces?(face)
  face.loops.any? { |loop|
    next false if loop.outer? # Ignore the outer loop.
    # Take a random edge from the loop and check if it
    # connects to another face on the same plane.
    edge = loop.edges.first
    edge.faces.find { |f| f != face && f.normal.parallel?(face.normal) }
  }
end

p contains_faces?(Sketchup.active_model.selection.first)

I know it is late but I think it worth noting.
Get the outer loop of a face and get edgeuses from there.

Check each edgeuse whether it has partners. if all edgeuses has at least 1 partner then this face is contained in another (or other faces. A face can be enclosed by more than one face. You can find that by checking the faces of the edge use).

I was struggling with the same issue and this is what I did.

Please note that this is valid for co-planar faces.

def self.delete_contained_faces(container_group)
    faces = container_group.entities.grep(Sketchup::Face)
    contained_faces = faces.select { |face|
      edge_uses = face.outer_loop.edgeuses
      # here checking for partners.size==1 just for safety. 
      # if size > 1 you may need to check more details based on your use case. 
      # if size > 1 generally it will mean there are >2 faces, where one of them is not merged automatically (in my case I am creating faces and exploding the group to intersect them automatically)
     # where all edgeuse objects has at least 1 partner it means this face is completely contained. 
     # if any edge use has 0 partners it means that edge is outside and the face is not contained
      edge_uses.all? { |euse| euse.partners.size == 1 }
    }
    p contained_faces.size
    contained_faces.each do |face|
      if face.valid? # because a face could be deleted in previous loops
        #of course delete edges instead of face to merge the face with the containing parent (this is my purpose)
        container_group.entities.erase_entities face.edges 
      end
    end
  end

with this approach you don’t need to loop exponentially and you can also find some other details quickly

Are you aware that the Face#loops method returns all of the loops of a face, and any except the outer loop will be faces or holes that are inside the given face? The shared edgeuses will usually be with adjacent faces that share an edge with the given one.

Yes.

We are getting the outer_loop and from there the edgeuses. So if all edgeuse objects has at least 1 partner then this face is contained in an/other(s).

This is much faster than comparing if an outer_loop of face is an inner_loop of another face because this will require comparing each outer_loop to all other faces’ inner_loops

Getting outer_loop.edges instead of edgeuses and then doing

contained_faces = faces.select { |face|
edges = face.outer_loop.edges
edges.all? { |edge| edge.faces.size == 2 }
}

should give the same result