Finding all connected groups

My goal is make two (or more) connected groups as one group.

There are dozens of possibilities for groups to be connected.
Can you tell us your interpretation of “connected groups”? Please explain in sufficient detail!

I guess there will be no solution for your request as “two-line” code…

In picture you can see some connected groups that I wish to find.

. I wish to solve this problem in different steps. In first step I need to know name of other group that is connected to my group.

An idea.
You have to get an array of all vertices position of all the faces of “my group”. Transformation of group must be taken into consideration when you retrieving the position. You will get Point3d array. You may need to make it uniq!.

You have to get an array of all group in a model excluding “my group”.
You have to iterate through each of this group entities and check the faces for all above mentioned points with .classify_point method if it is on it or not. (This time you heve to use both PointInside , PointOnEdge, PointOnVertex for.classify_point condition). Transformation of group must be taken into consideration when you make the compassion.
If you get ‘hit’ you can store the group to an array for later processing…check the names, etc.

Lets have a simple 2D example. Following we have face1 (one point in common), face2 (One line in common), face3 (an area in common), face4 (Nothing in common) and face_main. If we have only face_main, how can we find face1, face2 and face3?

mod = Sketchup.active_model  # Open model
ent = mod.entities  # All entities in model
sel = mod.selection  # Current selection

pt1 = [20, 20, 0]
pt2 = [15, 30, 0]
pt3 = [25, 30, 0]
face1 = ent.add_face pt1, pt2, pt3
pt1 = [13, 10, 0]
pt2 = [17, 10, 0]
pt3 = [15, 5, 0]
face2 = ent.add_face pt1, pt2, pt3
pt1 = [15, 15, 0]
pt2 = [30, 10, 0]
pt3 = [30, 20, 0]
face3 = ent.add_face pt1, pt2, pt3
pt1 = [0, 0, 0]
pt2 = [10, 0, 0]
pt3 = [0, 10, 0]
face4 = ent.add_face pt1, pt2, pt3
pt1 = [10, 10, 0]
pt2 = [10, 20, 0]
pt3 = [20, 20, 0]
pt4 = [20, 10, 0]
face_main = ent.add_face pt1, pt2, pt3, pt4

!! dirty & cheap !!

#the snippet of your previous post continued like:

all_faces = ent.grep(Sketchup::Face).to_a
other_faces = all_faces - [face_main]
p_v = Sketchup::Face::PointOnVertex 
p_e = Sketchup::Face::PointOnEdge 
p_i = Sketchup::Face::PointInside
faces_con = []
all_faces.each{|face_a|
  face_a.vertices.each{|vert|
    if face_a == face_main
      other_faces.each{|face_o|
        result = face_o.classify_point(vert.position)
        if result == p_v || result == p_e || result == p_i 
          faces_con<<face_o
        end
      }
    else
      result = face_main.classify_point(vert.position)
      if result == p_v || result == p_e || result == p_i 
        faces_con<<face_a
      end
    end
  }
}
puts "------"
puts "found total #{faces_con.size} connected face(s)..."
faces_con.uniq!
puts "...and : #{faces_con.size} of them are unique"
puts "The face(s) connected to face_main (#{face_main}) are:#{faces_con} "
sel.clear
puts "The number of face(s) in selection:"
puts "------"
sel.add(faces_con)
1 Like

I wish to merge face2 and face3 to face_main if faces are in same height. Can you also help me for it?

There are dozens of possibilities for “merge” and “same height”.
Please explain in sufficient detail!

Same as picture

!! MORE dirty & cheap !!

#the snippet of my previous snippet continued like:

faces_con.each{|f|
  gr_temp = ent.add_group
  gr_temp.entities.add_face f.vertices
  gr_temp.explode
}
2.times{
  ent.grep(Sketchup::Edge).to_a.each{|e|
    next unless e.valid?
    if e.start.edges.size == 1 || e.end.edges.size == 1 
      e.erase!
      next
    end
    next if e.faces.size != 2
    e.erase!
  }
}
1 Like

If you take at look at the top right example, you’ll see that it has no vertices lying on or in the other group.
Also, none of the bounding box corners would be within the other group’s bounds.

For this scenario we have suggested in the past that a boolean intersect be done on two copies of the groups. And then the result examined to see if it has an length or volume. (Lastly the temporary result is deleted.)

The drawback to this approach is that it makes modifications to the model, so some have used this approach within an undo operation and then call Sketchup.undo or abort the operation after doing the determination of intersect.

1 Like

Thank you so much. I will back to this codes many times…

Thank you for advice. I will check it carefully.

Dear Dezmo,
Lets make it more interesting… In following example we have faces in different groups. Can you help me to solve problem for this scenario?

mod = Sketchup.active_model  # Open model
ent = mod.entities  # All entities in model
sel = mod.selection  # Current selection

pt1 = [20, 20, 0]
pt2 = [15, 30, 0]
pt3 = [25, 30, 0]
grp1 = Sketchup.active_model.active_entities.add_group
face1 = grp1.entities.add_face pt1, pt2, pt3
pt1 = [13, 10, 0]
pt2 = [17, 10, 0]
pt3 = [15, 5, 0]
grp2 = Sketchup.active_model.active_entities.add_group
face2 = grp2.entities.add_face pt1, pt2, pt3
pt1 = [15, 15, 0]
pt2 = [30, 10, 0]
pt3 = [30, 20, 0]
grp3 = Sketchup.active_model.active_entities.add_group
face3 = grp3.entities.add_face pt1, pt2, pt3
pt1 = [0, 0, 0]
pt2 = [10, 0, 0]
pt3 = [0, 10, 0]
grp4 = Sketchup.active_model.active_entities.add_group
face4 = grp4.entities.add_face pt1, pt2, pt3
pt1 = [0, 0, 0]
pt2 = [0, 10, 0]
pt3 = [10, 10, 0]
pt4 = [10, 0, 0]
grp_main = Sketchup.active_model.active_entities.add_group
face_main = grp_main.entities.add_face pt1, pt2, pt3, pt4
tr = Geom::Transformation.new [10, 10, 0]
grp_main.transform! tr
ents = Sketchup.active_model.active_entities
all_faces = []
ents.each{ |entity|
  case entity
  when Sketchup::ComponentInstance, Sketchup::Group
  entg = entity.definition.entities
  entg.each{ |entityg|
    case entityg
      when Sketchup::Face
      if entityg.normal == [0, 0, -1] 
        all_faces << entityg 
      end  
    end
  }  
  end  
}      
other_faces = all_faces - [face_main]
p_v = Sketchup::Face::PointOnVertex 
p_e = Sketchup::Face::PointOnEdge 
p_i = Sketchup::Face::PointInside

Thank you in advance.

I asked twice in this topic: “Please explain in sufficient detail!”
I will not ask a third time.

From the randomly copy-pasted code snippest above, I came to the conclusion that the help so far has had no effect.
Instead of sitting down and looking at the basics, you’re waiting for someone else to write it. It won’t be me.
Sorry.

Ruby learning resources

1 Like

Apologize my mistake. In fact after I put faces in groups you codes work but I could not see. After I deleted groups I find one merged face.

I added following codes and deleted connected faces and their parent group. My problem solved.

num = faces_con.size.to_i
loop do  
  face_b = faces_con.pop
  grp_f = face_b.parent.instances[0]
  grp_f.erase!
  num -=1
  break if num == 0
end
main_grp.erase!

For friends whom are interest on this topic… You need to use following code.
" entities.intersect_with recurse, transformation1, entities1, transformation2, hidden, entities2 ".
It looks confusing but you can find clear explanation and simple example in this link. https://sketchucation.com/forums/viewtopic.php?f=180&t=14525. This method will return you an array. If array.size is one means 2 groups has one line in common. if two means one face in common. 3 or more means volume in common.

Dear Dezmo,

I run your following codes for 2 cases. As you can see in photo it works for lower case but cannot work for upper case. Would you please let me know problem. Also I will be highly appreciate if you can explain code a little.

          2.times{
            ent.grep(Sketchup::Edge).to_a.each{|e|
              next unless e.valid?
              if e.start.edges.size == 1 || e.end.edges.size == 1 
                e.erase!
                next
              end
              next if e.faces.size != 2
              e.erase!         
            }

I also do some test on codes. Following codes works but I am not sure it is right or not.

               e.erase! if e.faces.size > 1

Yes this is correct for removing dividing edges. Example …

ent.grep(Sketchup::Edge) do |edge|
  edge.erase if edge.faces.size > 1
end

FYIEnumerable#grep is a block form iterator method. It produces an array so you need not use .to_a and you need not use .each as it is already an Array#each iterator.

1 Like