Get face-area from group within dynamic component

SI have been looking around and tried hard to solve this problem, but its giving me a hard time.

Imagine having a dynamic component. Inside that, you have 2 groups.

In the component option you can choose between 2 lengths: 1000 mm and 2000 mm.

When 2000 mm is chosen, Its obvious that the red face area of group 1 is twice the size than group 2.

But - and thats the question - when trying to iterate through all the entities and measuring the red faces it reads the same area - before and after. I beliebe its because the dynamic component resize by scaling, so that the inner-groups still think it has its original size.

Is there a way to get the “real” face area?

The dynamic component has this attribute: "test_dict", "my_dc_group"
The 2 groups inside has these attributes: "test_dict", "my_group", "1"(or "2")
The red face of each group has the attribute: "test_dict", "face_area"

To test it I have included a sample.

Every time a new LenX is set in the dynamic component, it should measures the areas of the two red faces and write it to the ruby console - when clicked.

I have included the test.rb sample file + test.png
and the test_dynamic_component.rb
test.zip (4.6 KB)
test_dynamic_component.skp (163.4 KB)

test.rb:
``
require ‘sketchup.rb’
require ‘extensions.rb’

     PDB_toolbar = UI::Toolbar.new "test_DC_face_area" 
  
     cmd = UI::Command.new("test_DC_face_area") {  
  
	model = Sketchup.active_model 
	ent = model.active_entities

  	SKETCHUP_CONSOLE.show

	
	ent.each_with_index do |o,i|
		next unless o.is_a?Sketchup::Group

			it_has_my_dc_group_attr = o.get_attribute "test_dict", "my_dc_group"
			n_faces = 1
			if it_has_my_dc_group_attr
				o.entities.each_with_index do |m,n|
					next unless m.is_a?Sketchup::Group
							it_has_component_group_attr = m.get_attribute "test_dict", "my_group"

							if it_has_component_group_attr
								
								m.entities.each_with_index do |q,r|								
									next unless q.is_a?Sketchup::Face

									it_has_face_attr = q.get_attribute "test_dict", "face_area"

									if it_has_face_attr
										face_area = q.area											
										puts 'face number: ' + n_faces.to_s + ' face_area: ' + face_area.to_s
										n_faces = n_faces + 1
									end # it_has_face_attr
								end # m.entities.each_with_index do |q,r|	
							end # if it_has_component_group_attr				
				end # o.entities.each_with_index do |m,n|
			end # if it_has_my_dc_group_attr		
	end # @ent.each_with_index do |o,i|

} 
cmd.small_icon = "test.png" 
cmd.large_icon = "test.png" 
cmd.tooltip = "test_DC_face_area" 
cmd.status_bar_text = "test_DC_face_area" 
cmd.menu_text = "test_DC_face_area" 
PDB_toolbar = PDB_toolbar.add_item cmd 
PDB_toolbar.show

``

(1) Ruby uses a 2 space indent by convention. (Your 8 space indent makes your code almost unreadable here in the forum.)

(2) You just cannot choose any name you wish for an attribute dictionary to work with the Dynamic Components extension.
It must be named “dynamic_attributes”.

(3) Both the component definition AND the component instance can have an attribute dictionary, named “dynamic_attributes”.

  • Sometimes you read the attribute from the component’s definition’s dictionary, other times you might need to read the attributes from the instance’s dictionary.

(4) Most importantly, your code does not even collect dynamic components to iterate:

dcs = ents.grep(Sketchup::ComponentInstance).find_all{|c|
  c.attribute_dictionary("dynamic_components") 
}
if !dcs.empty
  dcs.each {|dc|
    dict = dc.attribute_dictionary("dynamic_components")
    # code here
  end
end

(5) You seem to be tryting to use dynamic groups. The DC extension only supports components not groups.

(6) Be aware that the DC engine will create unique definitions (by copying) and appending “#1”,“#2”, etc. on the end of the component name, whenever a scaling or dimensional option is changed. So all of a sudden, you may be looking at the wrong definition.

(7) You can nest sub-DCs inside other DCs.

(8) You can create hidden attributes that the end users do not see in the dialogs by prepending them with underscores, such as “_area” and you can put into it a formula that calcs upon a redraw, which happens when a DC is scaled. Then later just get the result of the formula from the hidden attribute.

If you do use components you can realise another method, being “scale definition” a stretched component will display the correct area after a this and a redraw. So for a manual operation a component can be made unique so as not to upset others from the same definition, then scale definition, then redraw.This may be the basis for your script.

Thank you both, @DanRathbun and @pcmoor

ok, I’ll fix my IDE. (Using Sublime Text 2 btw - nice editor)

Nice to know. I guess its when you want the dc to write the attributes directly. I have experienced that you actually can use other dictionary names if you set the attributes with your own ruby-program.

Well, you gave me somthing to go futher on with. I will try to make it with components instead of groups, and let the dc set the _area attribute, and then find a way to read that from my ruby code.

I haven’t stumbled upon “scale definition” before? Is it an attribute, or how do you set it?

Solution.

When changing LenX attribute in Dynamic Components its actually scaling the component in the x-direction.

So to find the actual face area of the scaled component you have to calculate a scale factor, using o.scaled_size[0] / o.unscaled_size[0], and multiply it to the desired face. The face area is in fact constant.

``

h = {}
@ents.each_with_index do |o,i|
   next unless o.is_a?(Sketchup::ComponentInstance)
   attr = o.get_attribute("dynamic_components","rs_pdb2016")
   if attr
     o.definition.entities.each do |u|
			
       next unless u.is_a?(Sketchup::Face)
			
         scalefactorx = o.scaled_size[0] / o.unscaled_size[0]				
			
           dc_face = u.get_attribute('dynamic_components', 'area')

             if dc_face
	       face_area = u.area * scalefactorx
	       h[i] = {quantity: face_area}				  	
             end
      end # o.definition.entities.each do 
   end # if attr
end # ents.each_with_index do	

´´