For reference this is the issue that this causes users of OpenStudio:
https://unmethours.com/question/1625/wall-flying-every-where/
https://github.com/NREL/OpenStudio/issues/1350
For reference this is the issue that this causes users of OpenStudio:
https://unmethours.com/question/1625/wall-flying-every-where/
https://github.com/NREL/OpenStudio/issues/1350
I have played around with dacastror's solution and I think there is still an underlying issue in SketchUp when the active_path changes more than one level at a time. I extended dacastror's code to include additional groups and components for testing. I also added some "handle" groups under each group and component that can be selected in the outliner to immediately make the parent context active. Under most situations find_instances_and_print_global_positions returns the correct value:
@unknownuser said:
Group 1 global origin -> [100.0, 0.0, 0.0]
Group 2 global origin -> [100.0, 100.0, 0.0]
Instance 3 global origin -> [100.0, -100.0, 0.0]
Group 4 global origin -> [-100.0, 0.0, 0.0]
Group 5 global origin -> [-100.0, 100.0, 0.0]
Instance 6 global origin -> [-100.0, -100.0, 0.0]
ok
However, if I start at the top level of the model, then make Instance 3 active by selecting the handle group 'Group 1->Instance 3 <Definition 3>->handle' in the outliner, find_instances_and_print_global_positions returns:
@unknownuser said:
Group 1 global origin -> [0.0, 0.0, 0.0]
Group 2 global origin -> [100.0, 100.0, 0.0]
Instance 3 global origin -> [100.0, -100.0, 0.0]
Group 4 global origin -> [-100.0, 0.0, 0.0]
Group 5 global origin -> [-100.0, 100.0, 0.0]
Instance 6 global origin -> [-100.0, -100.0, 0.0]
ok
If I start at the top level, then enter Group 1, then enter Instance 3, I get the correct result.
@unknownuser said:
when entering Group 1, then Instance 3, local_transformation of Group 1 is correct
Sketchup.active_model.active_path
[#Sketchup::Group:0x0000000d876d80, #Sketchup::ComponentInstance:0x0000000d8756d8]
Sketchup.active_model.active_path[0].local_transformation.to_a.inspect
[1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 100.0, 0.0, 0.0, 1.0]when jumping from top level to Instance 3, local_transformation of Group 1 is incorrect
Sketchup.active_model.active_path
[#Sketchup::Group:0x0000000d876d80, #Sketchup::ComponentInstance:0x0000000d8756d8]
Sketchup.active_model.active_path[0].local_transformation.to_a.inspect
[1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]
I get similar results if I switch between other levels of the hierarchy in odd ways. I wonder if the implementation of local_transformation depends on the active path observer? I only seem to get these issues if I change the active path multiple elements at time.
def overall_transformation(instance)
model = Sketchup.active_model
path = model.active_path ? model.active_path ; []
parents = []
parent = instance.parent
while (parent.class != Sketchup;;Model)
parents << parent
if parent.class == Sketchup;;ComponentDefinition
parent = parent.instances[0]
else
parent = parent.parent
end
end
indi = path.index(instance)
# if instance is in path
if indi
# if instance is last entry in path
return model.edit_transform if indi == path.size-1
# compute transformation up to and including instance
tr = path[0].local_transformation
for i in 1..indi
tr = path[i].local_transformation * tr
end
else
# initialize with instance transformation
tr = instance.transformation
while (instance.parent.class != Sketchup;;Model)
# get parent
if instance.parent.class == Sketchup;;ComponentDefinition
instance = instance.parent.instances[0]
else
instance = instance.parent
end
# is parent in path
indi = path.index(instance)
if indi
# when parent is in active path, it's transformation is just identity right?
#puts "parent found #{instance.transformation.to_a.inspect}"
tr = instance.transformation * tr
else
tr = instance.transformation * tr
end
end
end
return tr
end
def find_instances
instancias = []; instancias.clear
mod = Sketchup.active_model
definitions = mod.definitions
for definition in definitions
instances = definition.instances
if instances.length > 0
entity = instances[0]
attr = entity.get_attribute "dic", "key"
instancias << [attr, entity] if attr
end
end
# sort by key
instancias.sort {|x,y| x[0] <=> y[0]}
return instancias
end
def make_group(entities,transformation,group_name)
p1 = [0,0,0]; p2 = [50,50,50]
group = entities.add_group
group.name = group_name
group.entities.add_line p1, p2
group.transform! transformation
return group
end
# this group is not really of interest, just gives us a handle in the outliner to make parent active
def make_handle_group(entities)
p1 = [0,0,0]; p2 = [50,50,50]
group = entities.add_group
group.name = "handle"
edge = group.entities.add_line p1, p2
edge.visible = false
return group
end
def make_instance(entities,transformation,definition_name,instance_name)
p1 = [0,0,0]; p2 = [50,50,50]
definition = Sketchup.active_model.definitions.add(definition_name)
definition.entities.add_line p1, p2
make_handle_group(definition.entities)
instance = entities.add_instance(definition, transformation)
instance.name = instance_name
return instance
end
def make_instances
model = Sketchup.active_model
return "had been previously created" if find_instances.size>0
model.definitions.purge_unused
tr1 = Geom;;Transformation.translation [100,0,0]
tr2 = Geom;;Transformation.translation [0,100,0]
tr3 = Geom;;Transformation.translation [0,-100,0]
group1 = make_group(model.entities,tr1,"Group 1"); make_handle_group(group1.entities)
group2 = make_group(group1.entities,tr2,"Group 2"); make_handle_group(group2.entities)
instance3 = make_instance(group1.entities,tr3,"Definition 3","Instance 3")
group1.set_attribute "dic", "key", "Group 1"
group2.set_attribute "dic", "key", "Group 2"
instance3.set_attribute "dic", "key", "Instance 3"
tr4 = Geom;;Transformation.translation [-100,0,0]
tr5 = Geom;;Transformation.translation [0,100,0]
tr6 = Geom;;Transformation.translation [0,-100,0]
group4 = make_group(model.entities,tr4,"Group 4"); make_handle_group(group4.entities)
group5 = make_group(group4.entities,tr5,"Group 5"); make_handle_group(group5.entities)
instance6 = make_instance(group4.entities,tr6,"Definition 6","Instance 6")
group4.set_attribute "dic", "key", "Group 4"
group5.set_attribute "dic", "key", "Group 5"
instance6.set_attribute "dic", "key", "Instance 6"
"ok"
end
def find_instances_and_print_global_positions
instances = find_instances
instances.each do |instance|
origin = overall_transformation(instance[1]).origin
puts "#{instance[0]} global origin -> " + origin.to_a.inspect
end
"ok"
end
#make_instances
#find_instances_and_print_global_positions
Thank you dacastror, local_transformation seems to work. Does anyone have any information about local_transformation since it does not seem to be in the docs, http://www.sketchup.com/intl/en/developer/docs/methods#index_l? This post indicates that this method is provided by the Dynamic Components extension, http://sketchucation.com/forums/viewtopic.php?f=180%26amp;t=33182%26amp;start=15#p293542. Is there any documentation for the Dynamic Components extension?
In our plug-in, we only ever have two levels of nested groups and all groups are unique instances. So I guess this should work for us? As long as the SketchUp team doesn't remove the method?
Here is my best attempt to clearly illustrate my problem. Load the Ruby code attached at the bottom of this post. Run the method make_geometry
, this will clean everything in your model and create two groups Group 1 and Group 2, Group 2 is nested inside of Group 1. Group 1 is translated 10 m in the X direction, Group 2 is translated 10 m in the Y direction. Run the print_geometry
method from the top level, then from within Group 1, then from within Group 2. You will see this output:
` print_geometry
active_path = []
Group 1 transformation = (393.700787", 0", 0")
Group 2 transformation = (0", 393.700787", 0")
Edit transformation = (0", 0", 0")
print_geometry
active_path = [Group 1]
Group 1 transformation = (0", 0", 0")
Group 2 transformation = (393.700787", 393.700787", 0")
Edit transformation = (393.700787", 0", 0")
print_geometry
active_path = [Group 1, Group 2]
Group 1 transformation = (0", 0", 0")
Group 2 transformation = (0", 0", 0")
Edit transformation = (393.700787", 393.700787", 0")`
At the top level, I can figure out the local transformation for both Group 1 and Group 2. At the second level (inside Group 1), I can get Group 1's transformation from the edit transform and I can get Group 2's transformation by applying the inverse of Group 1's transformation. However, from the third level (inside Group 2) there is information lost. I cannot figure out which part of the edit transform comes from Group 1's transformation and which part comes from Group 2's. Is there a solution to get transformations for both groups when we are inside of Group 2? I had been storing data in my own array in an onActivePathChanged observer. However, this falls apart when the active path changes by more than a single step.
I have not seen a good answer to this question in any other post: http://sketchucation.com/forums/viewtopic.php?f=323%26amp;t=30067, http://sketchucation.com/forums/viewtopic.php?f=180%26amp;t=26212. Is there just no solution for this?
` def make_geometry
Sketchup.active_model.entities.clear!
$group1 = Sketchup.active_model.entities.add_group
$group1.name = "Group 1"
$group1.transformation = Geom::Transformation.translation(Geom::Point3d.new(10.m, 0, 0))
$group1.entities.add_cpoint(Geom::Point3d.new(0.m, 0.m, 0.m))
$group1.entities.add_cpoint(Geom::Point3d.new(5.m, 5.m, 3.m))
$group2 = $group1.entities.add_group
$group2.name = "Group 2"
$group2.transformation = Geom::Transformation.translation(Geom::Point3d.new(0, 10.m, 0))
$group2.entities.add_cpoint(Geom::Point3d.new(0.m, 0.m, 0.m))
$group2.entities.add_cpoint(Geom::Point3d.new(5.m, 5.m, 3.m))
end
def print_active_path()
result = []
active_path = Sketchup.active_model.active_path
active_path.each {|p| result << p.name} if active_path
puts "active_path = [#{result.join(', ')}]"
end
def print_transformation(name, t)
puts "#{name} = #{t.origin}"
end
def print_geometry
print_active_path()
print_transformation("Group 1 transformation", $group1.transformation)
print_transformation("Group 2 transformation", $group2.transformation)
print_transformation(" Edit transformation", Sketchup.active_model.edit_transform)
nil
end`
Thanks ThomThom, let me know what you find out.
Dan
Thanks ThomThom, I still don't quite see how to solve my problem. When I am editing a group, the transformation returned for each group (current group or any parent) in the active_path is identity. So if I am editing a group how can I get its parent's transformation?
I have some questions about edit_transform that I have never been able to
fully figure out. I work on a plug-in that uses multiply nested groups. I
keep track of the edit_transform each time the active_path changes
using ModelObserver.onActivePathChanged.
Initially, I assumed that the active path could only change one path at a
time (e.g. either a push or a pop relative to the current active path).
However, I have found that sometimes the active path can change multiple
steps at a times with no intermediate calls to
ModelObserver.onActivePathChanged. For example:
[Group1, SubGroup1] to []
or
[Group1, SubGroup1] to [Group2, SubGroup2]
I keep track of an array of edit transforms and would really like to be
able to recreate the edit transform given the active path. Is that
possible? If so is there a way to get an array of edit transforms
correspond to the active path array (i.e. the edit transform for each entry
in the active path)?
In case I am asking too much, any better description of what the edit_transform is would be very helpful. The SketchUp API documentation is not really complete enough for me especially with respect to nested groups.
Thanks a lot,
Dan