Non-unique Group.guid in grouped entities after copying a group

bug
components

#1

I have noticed that copying a group does not cause generation of new GUIDs (returned by Group.guid) for the new group entities. I am wondering if it is a bug or if it is an expected behavior.

Steps to reproduce:

  1. Create a group. Let’s call it A.
  2. Create a group containing A. Let’s call it B. (It’s not possible to create a group with only a group inside through UI, so you have to include something else in the group).
  3. Copy B. Let’s call the new group B2 and the group inside it (that is a copy of A) A2.
  4. Now, in Ruby Console:
  5. Check for guid of A: Select A and evaluate Sketchup.active_model.selection[0].guid
  6. Check for guid of A2: Select A2 and evaluate Sketchup.active_model.selection[0].guid
  7. The GUIDs are the same.

It also applies to making components unique. If B is created as a component instead of a group and B2 is created by copying B and then making it unique, the GUIDs of A and A2 will also be the same.

This issue exists both in SU 2015 and SU 2016.


#2

This strikes me as a bug. I don’t know whether it is a known one or new.


#3

To my knowledge, that has always been the way Sketchup has worked. I do use my own home grown plugin for obtaining the entities paths to drawing elements. My tool says that A and A2 are instances of the same group. In addition, the edges and faces in that group are exactly the same drawing elements (which is what I expect when you first copy a group). What is a bit weird is that I followed your instructions, and named one “A” and one “A2”. You would think that having done that would make each group object unique. Groups can behave a lot like components.

If I take and copy and past the original group again, and don’t try to rename anything, then the embedded group “A” is duplicated. I don’t expect it to be a unique group until it is edited.


#4

A Group is an instance class. Under the hood it is actually a special kind of ComponentInstance class with the name “Group”. As groups are derived from components, they also have a definition, whose group? method returns true. (This method returns false for definitions of images and components.)

It is the definition that has an entities collection, and therefore “owns” drawing objects. A ComponentInstance or a Group does not actually “have” a collection of drawing objects.

The way @wmatyjewicz explains things, makes an assumption that the “visible” end-user instance objects “have” collections of Entities. End-users often fall into that trap, especially with Group objects because their definitions are hidden from the Component Manager interface. (I wish there was a UI toggle that we could make them visible. Because of this, and other quirks, some ppl avoid using groups and only use component instances.)


#5

I went back and rethought this.

What’s causing the confusion here is the way that nested Groups and Components are managed in SketchUp.

A Group has an associated ComponentDefinition that owns its geometry (Groups are implemented as a special case of Component, using the same infrastructure). When a Group is nested inside another Group, what really happens is that an instance is nested inside the outer Group’s ComponentDefinition.

When you copy the outer Group, the copy continues to point at the same CD, which still contains the instance of the nested Group. That is, despite there being visually two instances of the nested Group in the model, data-wise there is only one. So A’s object id and GUID remain the same. On the other hand, the copy of the outer Group gets a new object id and GUID when you open it for edit to change the nested Group’s name.

Most manipulations of a Group, including moving, rotating, and renaming, affect the placement of the Group in the model but do not require breaking the relationship between the Group and its CD. So, the Group retains its object id and GUID under these operations. Only when you open the Group for edit does SketchUp decide it must be made unique. Then it clones the CD. That’s why renaming the nested Group does not cause it to get a new object id or GUID.

Now here’s the really confusing part: when SketchUp clones the outer Group’s CD to make it unique, it is still possible and legal for the instances of the nested Group to remain tied to the same CD themselves. They are just like the original copies of the outer Group in the model context; having two placements in the model does not require them to be made unique. Only when/if you open the nested Group for edit will SketchUp be forced to make the copies unique.

While I was typing this I see that @DanRathbun posted much the same info.


#6

Yea, but you explained it better,… more detailed.


#7

Thank you all for very detailed explanations. I have experimented with the idea of cloning the ComponentDefinition in a lazy fashion (ie. do not clone it until it’s really necessary), but as far as I understand it this is not what happens under the hood.

Let’s assume A is the Group object. Then A.definition is the ComponentDefinition object linked to this group, and A.definition.entities the collection of entities of A. Similarly, for B, B.definition and B.definition.entities.

Now, we copy B to obtain B2. If we test for equality of B and B2 we get B != B2 which is expected.

In the lazy cloning approach at this point we should have B.definition == B2.definition. However, it is not the case, which means B and B2 have different collections of entities. Then, A and A2 must be different Group instances, because otherwise there would be one Group instance belonging to two different collections. It can be verified by testing A and A2 equality: we get A != A2. However, A.guid == A2.guid which is strange.

What is even more strange with this GUIDs is that if we paint some faces inside A2 (inside A2.definition.entities strictly speaking) we should definitely have A2 different from A, but the GUIDs are still the same: A2.guid == A.guid.

So, in summary, it seems to me that the problem lies in not generating new GUIDs for Groups and ComponentInstances when ComponentDefinition containing them is cloned (for example when component is made unique).


#8

I think I know what is going on, but I haven’t tried looking at the drawing elements during each step.

Step 1: A selection of drawing elements are grouped and named “A”.

Step 2: A second set of drawing element(s) are selected with group A and grouped together and named “B”. Group object “B” is an outer entity.

Step 3: The B group is copied and pasted, creating a second outer entity group object that has the same name “B” and shares the same definition as the original “B”. At this point, I think there would be two different instances of “B”. Each instance has a unique transformation defining its orientation and position.

Step 4: Renamed the copy version of “B” to “B2” making it a bit more different. I think they would still share the same definition, but now they have different names as well.
I also think B2’s “A” group object is the same object as B’s with not only one definition but one instance as well.

Step 5: Double click object B2 putting it into edit mode and change the name of its “A” group object to “A2”. At this point, I think that the “A” group object separates into another instance of “A”, having a different name and transformation (although its transformation’s orientation and position should be the same as the original). It is now named “A2”, but it and “A” are two instances of the same definition.

Step 6: Finally, exit the editing mode of object B2: Putting object B2 into edit mode, making a change and exiting has resulted in a unique grouped entity that has a different definition object now (but its definition references the common drawing elements that were not modified).

If those statements are true, then I expect to see the two A objects have the same definition, but the two B objects do not.


#9

I have checked Ruby identities of the objects you mention and the difference is already visible in step 4:

B.definition != B2.definition

and also:

A != A2

but, as you said:

A.definition == A2.definition

Taking into account the above, what I would expect is that:

A.guid != A2.guid
A.definition.guid == A2.definition.guid

however only the latter is true.

By the way, there seems to be some lazy ComponentDefinition duplication happening for groups. When i painted one of the faces in A2, I got:

A.definition != A2.defintion

but still:

A.guid == A2.guid

(In my case the new ComponentDefinition was linked to A, that is A2.definition stayed the same, but the value of A.definition changed.)


#10

Are you aware of any workarounds for this issue with guid? What I need are unique and persistent identifiers for groups and component instances (not for component definitions). Now, entityID gives unique identifiers that are not persistent and guid gives persistent identifiers that are not unique.

To give some background on my use case: I am writing a plugin to export a SketchUp model to an external program I am developing. The external program allows to attach some properties to objects corresponding to SketchUp groups and components. Now, if I re-export the model from SketchUp, I would like to be able to automatically reattach the properties to the newly imported objects based on their identifiers. If the exported identifiers are not unique the reattachment may not work properly.


#11

The SketchUp API also allows this via Sketchup::AttributeDictionaries and Sketchup::AttributeDictionary classes.

In the past, before the supposedly unique and persistent guid for group and component instances, developers would generate their own and save properties and this plugin specific guid into a uniquely named attribute dictionary. A thus named dictionary would be attached to whatever objects you needed to track.

When I say “uniquely named attribute dictionary”, I mean a name that is prepended with your company (or toplevel module name,) followed by an underscore character, and the specific extension name (which should be wrapped within a similar named sub-module inside your company toplevel module,) and then if need be, to add another underscore and some descriptive name to differentiate this dictionary from any others that THIS plugin also may use. (If this the only dictionary name that your plugin will use, then your company and plugin name would suffice.)


#13

Hmm… are you able to repo this in a small snippet?


#14

Okay, I duplicated your results (I realize that the statements associated with my steps are not quite correct). What happened to me is that I assigned variable A to group A at step 3, and then I completed the remaining steps. When I typed

p A.name        into the Ruby console, I see  "A2"   and not "A".

It indicates that first occurrence of A in the entities chaining was turned into a new instance, and the A group inside of B2 retained it group identifier.

I duplicated my steps a second time with more intermediate output, restarting Sketchup with a new model from scratch:
a) Delete the startup Sketchup person component;
b) Draw a rectangle into a cube, triple click it, group it, and name it “A”.
At the console enter:

  entities = Sketchup.active_model.entities
  entities[0]                  ==>  #<Sketchup::Group:0x0000000b95c670>
   (I will be comparing object ID's instead of GUIDs. Each instance of
     the Sketchup application cranks out its own ID numbers,
     results will differ each time.)

c) I drew a 2d square, selected everything, grouped them and named it “B”. The ordering of my selection was such that the grouped object is “entities[5]”. Results may vary.
At the console:

   A = entities[0].entities[5] ==>  #<Sketchup::Group:0x0000000b95c670>
   B = entities[0]             ==>  #<Sketchup::Group:0x0000000b965748>
   So far so good, the first group hasn't changed it's ID number.

d) I copied and pasted object “B”.
At the console:

   B2 = entities[1]            ==> #<Sketchup::Group:0x0000000b9c6408>
   p B2.name                   ==> "B"

I expect its Group ID number to be unique from the other one because it has a different transformation (i.e. position is different).

e) I renamed the copied entity from B to B2.
At the console:

  p B2.name    ==> "B2"
  A2 = B2.entities[5]          ==> #<Sketchup::Group:0x0000000b95c670>
  p A2.name                    ==> "A"

It can be seen that it is the exact same group ID as we originally started with. It even has the same name and transformation. For something like a hundred leaves on a hundred trees, it not only saves memory in the file, but in the graphics card memory as well. But it sure makes everything else confusing.

f) Double click B2 to enter edit mode on it, select the A group object and rename it to A2.
Giving it a unique name will result in Sketchup having to make it a separate instance.
At the console:

  p A, A.name                   ==> [#<Sketchup::Group:0x0000000b95c670>, "A2"]
  p A2, A2.name                 ==> [#<Sketchup::Group:0x0000000b95c670>, "A2"]

Whoops, I seemed to have changed the name of group “…b95c670”, which is referenced by both A and A2. Steps “a” to “e” are standard Sketchup, I haven’t explored this deep before to know if this step “f” is.

I now know that I need to update the object that “A” references:

  A = entities[0].entities[5]   ==> #<Sketchup::Group:0x0000000ba86730>
  p A.name, A==A2               ==> ["A", false]

At first I said it was expected behaviour, but now I can’t say I ever tracked the operations in such detail to know. I can’t call it a bug, but a feature that saves memory, but there is a gotcha when modifying a duplicate enitity and the internal modifications create an instance in another entity.
I do know that the copy behaviour exists with components as well if it is nested inside of another grouped object. Similar results should occur with GUIDs.


#15

Yes, I thought about it, but unfortunately the attribute dictionaries are copied when a group is copied. If I assign an ID inside my own attribute dictionary for a group and clone the group, the new group will have the same ID.

Which part would you like me to repo? An example of lazy group definition duplication? Or the GUID problem?

You can check lazy group definition duplication opening lazy_group_definition_duplication.skp (15.7 KB) and doing the below:

  1. Check the initial group definitions in the Ruby Console:
    B = Sketchup.active_model.entities[0] B2 = Sketchup.active_model.entities[1] B.definition B2.definition
  2. Open and close B2, that is the group on the right.
  3. Check the current definitions in the Ruby Console:
    B.definition B2.definition

In my case B.definition in step 3. is different than in step 1 so the definition of the left group must have been duplicated (even though it was the right group I interacted with).

To be clear, the lazy group definition duplication is not a problem for me. It seems acceptable that copied and not yet modified group behaves as if it was a second instance of a component. Maybe the lazy group definition duplication could create a new group definition for the group being actually modified and not the original one, but it is a minor issue.

The real problem for me is the non-uniqueness of GUIDs. I have created a gist https://gist.github.com/wmatyjewicz/e28a3a116f311d383ed9 that reproduces the problem in Ruby. It contains two files:

  1. guid_issue_groups.rb shows an example of non-unique GUIDs for different groups.
  2. guid_issue_components.rb shows an example of non-unique GUIDs for different components.

Thank you for your effort. I agree that that duplicating a group definition only when it is really necessary is not a bug. As far as the gotcha is concerned, it is a minor thing in my opinion. But still, for me the bug is that, at the end:
p A.guid p A2.guid
shows the same even though A and A2 are different.