Why can't I get the material of a face sometimes in ruby console?

When I tried to retrieve the materials of faces in group in my friend’s model, I found out that in some groups, all of their materials of faces were nil and the other groups would not have this problem. But every group indeed had materials. I am very confused and can not figure it out.

Here is my code.

mod = Sketchup.active_model
ent = mod.entities
sel = mod.selection

ent.grep( Sketchup::Group ).each{|e|
  if e.definition.entities.grep( Sketchup::Face ).length==0 then
  UI.messagebox "1"
  else
    for i in 0...e.definition.entities.grep( Sketchup::Face ).length
      if e.definition.entities.grep( Sketchup::Face )[0].back_material.nil? then
      UI.messagebox "nil"
      else
      UI.messagebox e.definition.entities.grep( Sketchup::Face )[0].>material.name    
      end
    end
  end
 }

BTW there is a group that I can’t see or select(by"select all") in the screen. It only exist in ruby console. All the groups I can see have faces and I have already purged unused definitons.

nil is the reference given to the material property of entities, that are do not have a material yet assigned, and therefore will “default” to displaying their parent context’s material (if the parent has a material assigned.)

ADD: Some people refer to nil as the “default material” because it makes the display default to showing the parent’s material upon all nested faces that have no material assigned. (But of course, “under the hood”, nil is the singleton instance of the NilClass, and not really a Sketchup::Material class object.)

The “Select All” command works in the current context only. If there is a sub-group hidden, you must double-click the parent group and then “Select All”.

Also try checking … View > Hidden Geometry item.

I need to tell you that UI.messagebox can fail silently. It is asking for trouble when you use a messagebox for testing.

Instead use puts to send test messages to the console. Something like this:

mod = Sketchup.active_model 
ent = mod.entities
sel = mod.selection

puts # empty line
ent.grep( Sketchup::Group ).each{|e|
  puts "Group #{e.name.empty? ? e.definition.name.inspect : e.name.inspect}:"
  puts "  Material: "<< e.material.nil? ? "(default)" : e.material.name.inspect
  faces = e.definition.entities.grep( Sketchup::Face )
  if faces.empty?
    puts "  +-- No faces in group!"
  else
    for f in faces
      puts "  +-- #{f.inspect}"
      puts "      +-- Material    : "<< f.material.nil? ? "(default)" : f.material.name.inspect
      puts "      +-- BackMaterial: "<< f.back_material.nil? ? "(default)" : f.back_material.name.inspect
    end
  end
  puts # empty line
}
puts # empty line

This:

for i in 0...e.definition.entities.grep( Sketchup::Face ).length

… is a “fence post” error. It will attempt to iterate 1 element beyond the last element in the collection.

Just set an assignment to reference the collection, thus:

faces = e.definition.entities.grep( Sketchup::Face )

… and then iterate like:

for f in faces

You gain nothing by recalling that long string of methods twice. The method calls will eat more time than creating the reference faces.

The part that confused me is that the faces seems to have a material assigned. I can retrieve it with color picker(paint bucket with “Alt”). And if I explode the group, select all the exploded entities and make them a group again, I can retrieve their material with the same code I used before. The name will show on a messagebox. What’s wrong in my situation?

They either DO or DO NOT. If the individual faces within a context, have a specific material assigned, that assignment will override (ie, be displayed instead of,) the “inherited” material of the parent context (either group or component.)

So if you double click into the edit context of the group, select a face, and see if it has a material assigned to either it’s front or back, by looking in the EntityInfo inspector. If not, the swatches will have both the default front/back color with a diagonal separator.

If the parent group is painted, it will show it’s material in the EntityInfo inspector swatch. (There is only one material swatch for a group or component context, and it will “flow” down to both front and back faces.)

Normally this kind of tool samples the display pixels at it’s hotpoint. In this case it set the current material for the Bucket to whatever is displayed under the picker. If that means it’s the parent material, that is what it is.
The tool was never meant to tell you what entity “owned” the material. A simple double-click into the group, selecting a face, and a quick glance at the EntityInfo (which should always be visible,) will tell you the faces have no material assigned.

Well, now in that case, the explode operation paints exploded faces directly, with the parent’s material, if they have none assigned.

Assumptions ?


@Tommy: I could not find a mention on the following two pages, that exploding causes the parent’s material to be painted on unpainted child faces.

User Guide: Applying Colors, Photos, Materials, and Textures

User Guide: Adding Colors and Textures with Materials

Thank you very much !! I do not know that group or component has the material property after I used SU for four years. Thank you ~

Going to bring the big guns — @jody — to take a peek at these changes. Thanks, Dan!

1 Like