Apply a material with onComponentInstanceAdded

Hello and happy new year everyone. :cowboy_hat_face:

I want to recover the material used by a subcomponent to apply it on these premitive faces.

Here is an example:

class MyDefObserver < Sketchup::DefinitionObserver
  def onComponentInstanceAdded(definition, instance)
    definition.entities.grep(Sketchup::ComponentInstance).each do |e|
      if e.definition.name.include?("EXPLOSER")
        e.definition.entities.grep(Sketchup::Face).each do |f| 
          f.material = e.material
          p f.material
        end 
      end        
    end
  end
end

# Attach the observer
Sketchup.active_model.definitions[0].add_observer(MyDefObserver.new)

Result of the puts:

true
nil
nil

I use the onComponentInstanceAdded method for the method to trigger for each component manually placed in SketchUp.

For what reason the material does not want to apply on the faces?

Thank you in advance for your help.

David

A test model would help.

  1. Is there a material on the subcomponent? Check it and print it out.

  2. This function needs a completely elaborated specification. At the moment there exist edge cases that seem not yet thought-out. When you insert the second instance, the code would do the same work again on the same definition. Another issue is a possible conflict between instances of the subcomponent. When you take the material of one instance and apply it to its definition’s faces you might overwrite the material that another instance applied to the definition’s faces.

1 Like

You seem to be misunderstanding some processes/methods etc…

A component’s definition never has a material assigned to it.
A component’s definition’s instance can have a material assigned to it, but it’s not automatic.

BUT, at the very moment that you add a component definition’s instance it will have NO material assigned to it…

So, what are you actually trying to do ???

1 Like
Actually, a component definition CAN have a material assigned to it, ...

Actually, a component definition CAN have a material assigned to it, which is used only for color highlighting during placement.

IT IS BUGGED. See post below.

In the example below, I’ve made a Box component out of the white geometry, and the selection was replaced with an instance, at the origin. (The white box.)
Then at the console, I assigned the material "Red" to this instance’s definition …

mod = Sketchup.active_model
#<Sketchup::Model:0x0000000bac5868>
ss = mod.selection
#<Sketchup::Selection:0x0000000bac4698>
ci = ss[0]
#<Sketchup::ComponentInstance:0x0000000b805b00>
cd = ci.definition
#<Sketchup::ComponentDefinition:0x0000000b805bc8>
cd.material = "Red"
#=> Red

Then, I insert a new instance by choosing the “Box” component from the “In Model” components collection.
You can see that it is highlighted with the definition’s material, whilst attached to the cursor … (which is drawn in as it doesn’t appear in the screen grab.)

As soon as the new instance is placed, it no longer shows the “highlight material”.

Specific face materials override this “material highlighting”. (Ie, it is not a way of assigning a default material to all new instances of a definition.)

That looks like a bug. ComponentDefinition probably inherits the behavior of having a material from DrawingElement but is in itself not supposed to have a material. I wouldn’t rely on this behavior in a plugin.

That said, if the material was applied to the instance when it got placed this could be a quite useful behavior. I prefer to paint components from the outside so they can easily be re-painted without requiring separate definitions. However that has the side effect of the components all having white previews in the component browser and when placed while I often want a different material. having a material defined for the definition that is by default applied to newly crated instances and used for the previews could perhaps give us the benefits of external and internal painting at the same time.

Yes of course ! the component has a material applied with the dynamic material attributes.

It is planned to make each instance unique.

Very good question!

I want to apply the materials used by my dynamic components on the premitic faces.

This will solve the problem with rendering engines that are unable to read the materials on the components.

You can make a rendering on the furniture of Click-Kitchen 1, to understand.

So there are two options:

1 - Explode the components so that the material comes to apply on the faces.

2 - Recover the material of the component and apply it on these faces.

I wish that the code is triggered at the insertion of the furniture so that the procedure is invisible to the user.

You are going to ask me, why not apply the materials on the faces from the beginning?

The furniture has dynamic hardware attributes that point to existing materials in SketchUp.

This is the best solution I know not to create duplicate hardware at each furniture insertion.

Here is a simple example, which retrieves the material of a selected component to apply it to these faces.

mod = Sketchup.active_model
sel = mod.selection
  sel.grep(Sketchup::ComponentInstance).each do |s|
    s.definition.entities.grep(Sketchup::Face).each do |f|
      f.material = s.material
    end
  end

Is there a solution to perform this method when inserting the components?

The answer to this question will allow me to solve all my problems.

Thank you

Are there still renderers out there that fails with this basic concept? I’m not rendering much myself so I don’t know.

If you plan to make all instances unique anyway, why not just explode the whole thing? All data in the model hierarchy and re-used parts will be lost anyway if everything is made unique.

I don’t know about your use case but in general it’s a bad idea to keep secrets from the user. Also, why modify a component on insertion? Why not modify the component, save it and let the user insert the modified version in the first place.

What is the code for?

Yes all rendering engines!

Oula explode everything is very ugly!

I do not understand you!
Making unique does not change the dynamic component and nothing is lost.

If the secretly launched method changes something in the use of the Plugin you are right!
In my case, nothing changes except the renderings that will be successful without any intervention of the user.

I was sure this question was going to be asked!

I answer in my previous post:

V-Ray and Thea handles this correctly. Pretty sure Podium does as well. (Of the ones I’ve used.)

1 Like

V-Ray there is no worse as rendering engine for dynamic textures.

Make a simple test with Click-Cuisine and you will see the result.

Not only does the rendering not take into account the scale of the textures but it seriously slows the operation of Click-Cuisine.

https://extensions.sketchup.com/en/content/kitchen-sketchup-click-cuisine

What version?

Try any version of V-Ray!

Do you have a test model?

All furniture models of the Click-Kitchen plugin:

https://extensions.sketchup.com/en/content/kitchen-sketchup-click-cuisine

I have tried a few different renderers and never had this problem. Your statement is false.

I’m referring to how SketchUp knows the handle of one drawer is the identical to the handle of another drawer or that two cabinets are of the same type. This information is highly useful when making reports or just to understand the model. Also the file size will increase if all instances are defined separately. If all instances are made unique there is not much point of using components at all.

I would strongly advise you not to build anything around Dynamic Components. It is a very old, badly written and seemingly no longer maintained plugin. Building on it is like building a house on sand.

Also, how were you thinking the user should be able to later change the material from within DC if you apply it to individual faces?

I have no experience in ruby to judge the quality of programming of dynamic components.

However use level, I find the process exceptional and very useful.

All my old plugins are built with dynamic components and have been successful thanks to the dynamic components.

It seems impossible to me again to follow your advice.

I now want to discover the ruby script as I did with dynamic components.

My goal is to combine the advantages of both processes to offer Plugins more and more intuitive and productive.

Thanks to ruby!
I have written a method that changes furniture materials in one click.

I agree and that will not be the case.

In fact all instances must have the materials on the faces at the time of their insertions.
So it’s useless to make it unique!

It’s too complicated to explain all the operating processes of Click-Cuisine 2!

For this reason, my questions are direct to save us time.

I take this opportunity to re-launch my basic question:

Thank you

Yes, You can manipulate the materials of a newly placed component from within the EntitiesObserver, onElementAdded callback. But modifying the component’s face material in this manner breaks some of the Dynamic Component functionality. Tested on SU 2015 and SU 2017. Not tested to work with the Undo/Redo stack

I hope this helps you.

module TNT_ClickCuisineTrial
  class MyEntitiesObserver < Sketchup::EntitiesObserver
    # this the PATTERN to only modify your 01-CABINET
    TNT_COMPONENT_PATTERNS = [/01-CABINET/]
    
    # Custom entities observer
    def onElementAdded(entities, entity)
      #Identify the entity that is being added, exit if it isn't one of yours
      return unless entity.is_a?(Sketchup::ComponentInstance)
      return unless TNT_COMPONENT_PATTERNS.any?{ |pattern| entity.definition.name =~ pattern }
      
      puts "Coloring: #{entity.definition.name}"

      #start a model operation
      entity.model.start_operation('Color Faces', true)
        meubels_verven(entity)
      entity.model.commit_operation
      
    end
    
    #Patterns of the subcomponents to be painted or not painted 
    PAINT_COMPONENT_PATTERNS = [/B-PLINTH/, /B-FACADES/, /WORKTOP/]
    DONT_PAINT_COMPONENT_PATTERNS = [/POIGNEE/]
    
    # Paint the faces of the cabinet's sub-components
    def meubels_verven(entity, paint_subcomponent = false)
      entity.definition.entities.grep(Sketchup::ComponentInstance).each do |instance|
      
        next unless (paint_subcomponent) || PAINT_COMPONENT_PATTERNS.any?{ |pattern| instance.definition.name =~ pattern }
        next if DONT_PAINT_COMPONENT_PATTERNS.any?{ |pattern| instance.definition.name =~ pattern }
        
        puts "Subcomponent name: #{instance.definition.name}" 
        
        #if parent has a material then copy the material to subcomponent
        if entity.material
          instance.material = entity.material
        end
        
        # if the instance has a material then copy  it to the faces
        if instance.material
          instance.definition.entities.grep(Sketchup::Face).each { |f|
            #f.material = material ## the test material
            f.material = instance.material
          }
        end
      #recursively call the method with paint_subcomponent = true
      meubels_verven(instance, true)
      end
    end 
  end

  
  #### Add entities observer for ALL entities
  Sketchup.active_model.entities.add_observer(MyEntitiesObserver.new)

... the remainder of your file here

I’ve uploaded the new logic.rb file here.
logic.rb (19.5 KB)