Selecting multiple faces by area




Is it possible to use Ruby to select faces that have a similar area based on a selection of faces? For example, I have selected an object that has 16 faces. With the 16 selected faces, I loop through each selected face to determine the area for each. Then using that area value for each face, I want to select the faces that have the same area with the rest of my model. Essentially, it would be as if I’m selecting similar objects with the rest of model.

My programming in Ruby is at a beginner level and I’ve only been able to print the area value for each selected face:

model = Sketchup.active_model()
sel = model.selection()
ents = model.entities()
sel_ents = []
for e in sel do sel_ents.push( e )
area = e.area*0.00064516
area2 = area.round(2)
puts area2

Thank you


Please wrap code in lexing delimit lines (of triple backticks.) Add the coding language name to the first line:

# code here

You can filter Ruby collections using the grep() method:

faces = model.selection.grep(Sketchup::Face)

Further filtering using the Array class’ methods (or the Enumerable mixin module’s methods, which are mixed into most all Ruby and SketchUp API collections classes.)

min = 23.75
max = 25.25
nice_size = faces.find_all {|face| 
  about = face.area.round(2)
  about >= min && about <= max

Then you can clear the GUI selection set, and push your “nicely sized” faces back into it:

model.selection.add( nice_size )


Thanks for the reply Danrathbun, I tested out the code above and it works well if I select the entire model and run the ruby code with the parameters set (min, max). I read further into the documentation and with a lot of trial and error, I’m almost to what I want to accomplish.

 model = Sketchup.active_model
 sel = model.selection
 ents = model.active_entities
 edges = []
ents.each do |e|
   if e.is_a? Sketchup::Face
     edges << e
for e in sel do edges.push ( e )
  about = e.area*0.00064516
    for e in sel do edges.each do |e|
       if e.area*0.00064516 <= about+0.01 && e.area*0.00064516 >= about-0.01
         sel.add e

In SketchUp, I would begin by selecting several faces (e.g. 3 faces selected), I run the code in ruby but only the first face that was selected works. I can see that the rest of the model has faces selected with the similar area of the first selected face. However, the other 2 selected faces did not work. I think I need to somehow loop a counter so that it proceeds onto the next selected face.


[ rhetorical ] Why push faces into an array referenced as “edges” ?
(That violates the “keep it readable” rule of good coding.)

Secondly, I already told you of the Enumerable#grep() method. It is a very fast method, and faster than the interpretive loop you programmed manually.

Just use:

faces = ents.grep(Sketchup::Face)

… instead of the slow:

ents.each do |e|
  if e.is_a? Sketchup::Face
    faces << e

… or even this:

ents.each do |e|
  faces << e if e.is_a? Sketchup::Face

(ie, conditional if, unless, until, or while expressions can be used “in modifier position”, ie, following a previous expression so as to modify it.)

Okay, in any programming language, you cannot modify the same collection that you are in the middle of iterating. The target pointer sort of loses it’s way, ie, members of the collection get skipped, or processed more than once.

(Above you are doubly iterating the selection collection and changing it inside the inner loop. I’m not surprised your code is wacky.)

So make an array copy of any collection that you need to modify whilst stepping through it. OR,… save up the changes in a “to do” array, and then apply them to the collection afterward, outside your decision iterative loop.

I showed doing this in the example above where I saved up the faces to be selected afterward. Then cleared the selection set, and lastly pushed the “nice_size” array of faces into the selection set.

I really suggest (strongly) you use Ruby’s built-in iterator methods, rather than for loops.


This topic was automatically closed after 91 days. New replies are no longer allowed.