i have a model and i need to detect which face contains a door, i managed to detect windows using the outer and inner loops but it doesn’t work on doors as the door has an edge that is common between the face and the floor.
this is the code i use to detect windows and find to which face they belong:
model = Sketchup.active_model
ents= model.active_entities
ents.each do |f|
if f.is_a? Sketchup::Face
inner_loops = (f.loops - [f.outer_loop])
nested_faces = []
inner_loops.each{|loop|
loop.edges[0].faces.each{|e|
nested_faces << e unless e == f
}
}
if nested_faces.empty?
UI.messagebox("Face: #{f.material.display_name} has no windows ")
else
nested_faces.each{|ff|
UI.messagebox("Face: #{f.material.display_name} contains the window #{ff.material.display_name} ")
}
end
end
end
how can i change my code so that it detects doors also ?
thnx a lot.
that’s the model where i’m testing the windows’ detection. windows model.skp (723.1 KB)
hello sir thank you so much for your advice, i did upload on which i’m testing the windows and doors detection, i tryed to solve this problem but it seems to show the door as an independent face and not as a part of an other face.
it gets the windows and show to which face they belong but not the door it shows it as a face with no windows.
thank you for your answer and sorry for any disturbance.
I had the same problem once and detecting doors is a bit icky. Specifically, because “door” is quite hard to define in terms of faces shared edges with some outer wall. For example corner-windows, doors drawn with an offset (now they look exactly like windows), divided walls etc.
I kinda solved it by grouping faces belonging to a wall (connected and same plane), find the outer edges to this group (1, 2), use the face which shares the most edges as my BaseWall. From that, I use the inner-loops to get the windows and then only Doors should be left. But since there are quite a few special cases I denote them as Subwalls and let the user select doors by hand. If you can be certain in how a model is created, it could be enough for you.
can you explain the part “grouping faces belonging to a wall (connected and same plane)” also yes there may be some walls that are divided into two subwalls so will that cause a problem ?
def group_by_walls(faces)
result = []
queue = faces
# Uses a queue to remove utilized faces
until queue.empty?
curr = queue.pop
same = get_connected_faces_on_plane(curr)
result << Wall.new(same)
queue -= same
end
result
end
# Return list of connected faces that have the same plane
def get_connected_faces_on_plane(face)
result = {}
queue = [face]
until queue.empty?
curr = queue.pop
next if result.key? curr
result[curr] = curr
same = get_connected_faces(curr).select { |x| same_plane?(curr, x) }
same.each do |f|
queue << f
end
end
result.keys
end
# Return list of faces that share the same edges
def get_connected_faces(face)
edges = face.loops.map(&:edges).flatten.uniq
edges.map(&:faces).flatten.uniq - [face]
end
# True if a and b share the same plane
def same_plane?(a, b)
a.plane[1..-1].zip(b.plane[1..-1]).select do |x, y|
(x - y).abs > 0.001
end.empty?
end
than you so much.
What does Wall.new(same) do ?
there is also a case like the model bellow, will it cause a problem as there are 2 windows linked to a door is it possible to detect that the 3 faces (2 windows and 1 door) belong to the green wall ?
Wall.new(same) creates a Wall object that has a few more methods and stores the faces associated with a specific wall. You’d need to modify that or use your own implementation. Please note that “Wall” is loosely defined too, if your gable were on the same plane, it too would be part of the “Wall”.
With my algorithm the two bottom windows and door would be Subwalls initially and would need to be assigned Window and Door respectively by the user. You can do this by using a specific material for those entities or add a key to its dictionary.
There are a few more special cases and I didn’t want to invest more time in solving the issue completely, so I left it to the user.
i separate between walls and windows by material types so how can change the “Wall.new(same)” as when i store the “same” directly it shows an error as it’s an array ? how is your “Wall” represented so as to know what is stored in the “result” variable ?