I had a problem with importing Dynamic Components and looks like quite a few others have hit the same problem. @dougward articulated it really clearly here:
I was drawing kitchen cabinets and was trying to re-use quite a few of the subcomponents (e.g. panel door) in the dynamic components I built. So I have one DC for wall cabinets, another for regular base cabinets, and another for corner base cabinets. All worked fine on their own in their own files. But then when trying to use them together in a model, they go haywire. It appears the cause is that the shared subcomponents have a unique identifier that causes a clash deep in the bowels of Sketchup. It looks fine on the surface because Sketchup automatically makes the names unique, but the formulae get messed up.
I wrote some (ugly) code which is working for me. Feel free to use it but make sure you save a backup or two before using it because I’m no software engineer and the code is doubtless bug ridden. The method it uses is to make copies of each component, then make them unique, then relink them back to each other as they were before. I couldn’t find a good way of making copies in Ruby (tried marshal but it didn’t work) so ended up with lots of ugly copying-across of attributedictionaries and layer and material references.
#---------------------------------------------------------------------------------------------
#Method to reinitiate component
def reinitiate_component(original_defn, entity_list_str)
mod = Sketchup.active_model # Open model
defs = mod.definitions #All component definition objects
ents = mod.entities # All entities in model
original_instances = []
new_instances = []
inst_names = []
dict_names = []
dict_keys_array = []
dict_values_array = []
#populate the original list of instances
inst_count = 0
original_defn.instances.each{ |inst|
original_instances << inst
inst_count = inst_count + 1
}
if !original_instances[0].get_attribute('dynamic_attributes', 'copies').nil?
#if the component has DC formula driven instances, then use the DC copies attribute
#to cut back to zero, copy, the original instance, and then reinstate the copies
#(that way they stay linked to each other properly):
for i in 0...inst_count
inst = original_instances[i]
if inst.get_attribute('dynamic_attributes', 'copy').nil?
break
end
end
original_def = inst.definition
original_def_name = original_def.name
original_copies = inst.get_attribute('dynamic_attributes', 'copies')
original_copies_formula = inst.get_attribute('dynamic_attributes', '_copies_formula')
#Make a copy of the dictionaries
dict_names.clear
dict_keys_array.clear
dict_values_array.clear
inst.attribute_dictionaries.each{ |dict|
dict_names << dict.name
dict_keys = []
dict_values = []
dict.each{ |key, value|
dict_keys << key
dict_values << value
}
dict_keys_array << dict_keys
dict_values_array << dict_values
}
#Take a reference of the material and tags (layers)
material_buffer = nil
layer_buffer = nil
if !inst.material.nil?
material_buffer = mod.materials[inst.material.name]
end
if !inst.layer.nil?
layer_buffer = mod.layers[inst.layer.name]
end
#cut back to zero copies
inst.set_attribute 'dynamic_attributes', '_copies_formula', '=0'
inst.set_attribute 'dynamic_attributes', 'copies', 0
#copy the original definition, make unique, delete the old one
new_ent = inst.parent.entities.add_instance(original_def, inst.transformation)
new_ent.make_unique
defs.remove(original_def)
defs.purge_unused
#Refill the material and tags (layers)
if !material_buffer.nil?
new_ent.material = material_buffer
end
if !layer_buffer.nil?
new_ent.layer = layer_buffer
end
#Refill the dictionaries into the new component
for j in 0...dict_names.count
for k in 0...dict_keys_array[j].count
new_ent.set_attribute dict_names[j], dict_keys_array[j][k], dict_values_array[j][k]
end
end
#Reinstate the original name and copies formula and value
new_ent.definition.name = original_def_name
new_ent.set_attribute 'dynamic_attributes', '_copies_formula', original_copies_formula
new_ent.set_attribute 'dynamic_attributes', 'copies', original_copies
#Update the entity list string
if new_ent.definition.entities.nil?
entity_list_str = entity_list_str + " | " + new_ent.definition.name +
", " + new_ent.name + ", " + new_ent.entityID.to_s + " |"
else
new_ent.definition.entities.each{|ent|
entity_list_str = entity_list_str + " | " + ent.definition.name +
", " + ent.name + ", " + ent.entityID.to_s + " |"
}
end
#=====================================================================================================================================
else
#if the component has non-dynamic instances, then copy each individually
#and store into a new array, saving a reference to the original definition
inst_names.clear
for i in 0...inst_count
inst = original_instances[i]
inst_names << inst.name
if i==0
master_def_name = inst.definition.name
original_def = defs[master_def_name]
end
#Make a copy of the dictionaries
dict_names.clear
dict_keys_array.clear
dict_values_array.clear
inst.attribute_dictionaries.each{ |dict|
dict_names << dict.name
dict_keys = []
dict_values = []
dict.each{|key, value|
dict_keys << key.to_s
dict_values << value.to_s
}
dict_keys_array << dict_keys
dict_values_array << dict_values
}
#Take a reference of the material and tags (layers)
material_buffer = nil
layer_buffer = nil
if !inst.material.nil?
material_buffer = mod.materials[inst.material.name]
end
if !inst.layer.nil?
layer_buffer = mod.layers[inst.layer.name]
end
#Copy the component instance
new_ent = inst.parent.entities.add_instance(inst.definition, inst.transformation)
new_instances << new_ent
new_ent.make_unique
#Refill the dictionaries into the new component
for j in 0...dict_names.count
for k in 0...dict_keys_array[j].count
new_ent.set_attribute dict_names[j], dict_keys_array[j][k], dict_values_array[j][k]
end
end
#Refill the material and tags (layers)
if !material_buffer.nil?
new_ent.material = material_buffer
end
if !layer_buffer.nil?
new_ent.layer = layer_buffer
end
#Keep a record of the master instance's definition
if i==0
masterdef = defs[new_ent.definition.name]
end
end #for
#Relink the new copies
for i in 1...inst_count
inst = new_instances[i]
inst.definition = masterdef
end
#Delete the old definition and purge it
defs.remove(original_def)
defs.purge_unused
#Put the name back to what it was
masterdef.name = master_def_name
#Replace names and append the entity list string
for i in 0...inst_count
new_instances[i].name = inst_names[i]
entity_list_str = entity_list_str + " | " + new_instances[i].definition.name +
", " + new_instances[i].name + ", " + new_instances[i].entityID.to_s + " |"
end
end #if
entity_list_str
end
#end of copy definition method-----------------------------------------------------
#---------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------
#Program launch
mod = Sketchup.active_model # Open model
ents = mod.entities # All entities in model
sel = mod.selection # Current selection
defs = mod.definitions #All component definition objects
entity_list_str = "."
def_list = []
#Purge unused definitions
defs.purge_unused
defs.each{|defn|
def_list << defn
}
def_list.each{ |defn|
entity_list_str = reinitiate_component(defn, entity_list_str)
}