Attributes that Auto Update

I’ve been investigating attributes today trying to get a better handle on this aspect of SketchUp. I’ve noticed that the housebuilder plugin creates a category called “einfo” and stores a number of variables one of which is the “origin”. If you manually move the wall group the origin attribute will automatically reflect this change in location. This is exactly the feature I need for my various assemblies (groups) so that I can add an edit feature into my own plugin. Howevever, I can’t seem to figure out how this attribute is automatically updating. When I create an “origin” attribute under a “einfo” category it does not update. I must be missing something.

You use the word “category”.

I do not know of any such word in the Ruby core nor the SketchUp Ruby API.

You need to be careful of using the word “attribute”.

It has meaning in several ways in SketchUp programming.

(1) In the Ruby core programming language, “attribute” means a standard use of instance variables (@variable_name,) in which the variable name is also a getter method name for that variable, and optionally also has a setter method that is named “variable_name=”.
These variables, and the accessor methods can be automatically generated by the methods attr(), attr_accessor(), attr_reader() and attr_writer().
Since class Class is the direct subclass of class Module, these methods are inherited by classes.

(2) In the SketchUp Ruby API, is defined a collection class named Sketchup::AttributeDictionary that contains attributes in the form of key + value pairs. This API class is very similar to the Ruby core Hash class.
You can attach a custom attribute dictionary to any model entity that is a subclass of Sketchup::Entity.
Be sure to name your attribute dictionaries in a unique way that mimics the way your name your modules, to prevent attribute dictionary name clashes.
Ie: “Medeek_SomePlugin_ThisDictionary” and "Medeek_OtherPlugin_ThatDictionary"
Within your dictionaries, you decide what the attribute names will be.

(3) In programming SketchUp API class UI::WebDialog objects, you must create HTML pages. HTML elements have “attributes” which most of us would more often call “properties.” But all the documentation you will find refers to them as “attributes” because this is what the W3C decided to call them.

(4) Also in programming SketchUp API class UI::WebDialog objects, you often create CSS files (or inline <style> elements,) for styling the dialog webpages. CSS rule-sets also have “properties” that often people also call “attributes”, but the W3C decided to call them “properties.” CSS2 has a function attr() that returns the value of an attribute of a selected HTML element. (These are usually used with pseudo-elements like ::after.)

ADD (5): Forgot,… the Dynamic Components extension uses the Ruby API attribute dictionaries to save and display parameters to the user, in the Component Options dialog.

Lastly, you use the “housebuilder” as an example, but provide no code examples.
I will not go and install a plugin just to attempt to understand what you are describing badly.

So I am going to ignore all that, and tell you to also ignore this “housebuilder”.

(… next …)

I will zero in on this:

Okay, the origin is a property of the Geom::Transformation class.
Once you have a reference to a transformation object, you can get it’s origin via the instance method: origin()
It returns the position as an instance of Geom::Point3d.

Each Sketchup::ComponentInstance or Sketchup::Group object, has a geometric transformation. SketchUp keeps track of these, and updates them whenever these kinds of objects are transformed (ie, moved, scaled, rotated, skewed, or any combination of these.)
It can be accessed via the instance methods: Sketchup::ComponentInstance.transformation() or Sketchup::Group.transformation()

So for example:

where_its_at = my_grp.transformation.origin

As said, SketchUp keeps track of the transformation “under the hood” in the core, and exposes it to the Ruby API via the object’s transformation() instance method.

You do not need to keep track of it yourself. But you may need to save it’s original state.

Now in another thread (at SketchUcation) I just discussed how to save the transformation, and react if it changes using an observer. See:

That is very vague.

But it reminds me of the example Parametric class in the SketchUp Team’s Shapes example:

Using this class manages paramteric attributes (automatically saves them into an attribute dictionary attached to your object,) and provides an Edit inputbox so the user can edit these parameters.

To see how it works, install the shapes or window maker extensions for the SketchUp Team:

Just be aware that the latest version of the Parametric class is only in the Shapes extension.

Investigating this a bit more and I’ve found out some more interesting behavior.

Here is the chunk of code after I create the main group that includes all of the sub-groups and sub-components:

    	# Foundation Assembly Information

    	@maingrouporigin = @maingroup.transformation.origin
    	@maingrouprotz = @maingroup.transformation.rotz
    	# puts @maingrouporigin @maingrouprotz

    	mdkinfo = 'MedeekInfo'
    	@maingroup.set_attribute mdkinfo, 'name', "FOUNDATION_ASSEMBLY_#{formatted_time}"
    	@maingroup.set_attribute mdkinfo, 'original_origin', @maingrouporigin
    	@maingroup.set_attribute mdkinfo, 'original_rotz', @maingrouprotz

I am attempting to store the original origin and Z rotation of the group as some attributes that can be accessed at a later time. When I do this and then manually move the group the attribute “original_origin” updates to the new origin. When I manually rotate the group the attribute “original_rotz” does not update.

I also noticed that the value from transformation.rotz is always rounded to the nearest integer value. So if my group was originally rotate 12.75 degrees, then it will store as 12 degrees.

(1) This really should be a constant defined near the top of your plugin module.

(2) This is a big generic. What happens next year when you write another plugin that also needs a informational dictionary ?
It really should be of form: “Company_PluginName_DictionaryName

It might be safer if the transformation was saved in it’s array compliment, and then converted back into a transformation object later when you need certain properties.

t_ary = @maingroup.transformation.to_a
@maingroup.set_attribute mdkinfo, 'original_transform', t_ary

That is unexpected because it is undocumented behavior. Until it is documented or acknowledged by the SketchUp Team as not some temporary feature, I would not rely upon it.

So, I already answered this, by giving you a link to some example observer code that should detect when an entity changes, and saves the transformation in the observer instance. The observer onChangeEntity() callback method could perhaps also write to your attribute dictionary,… IF it doesn’t crash SketchUp.

IF changing attributes in a callback crashes SketchUp, then use the safe observer event pattern:

That rotz method (and the rotx and roty methods,) of the Geom::Transformation class, are not part of the API.

They were specifically patched by and for the Dynamic Components extension, and were a “naughty monkey patch” to an API base class. The SketchUp Team will not let us do naughty monkey patching to Ruby or API classes, but they themselves have done it.

So, basically, you need to write your own methods that return the rotational properties from the transformation. They should now be written as refinements so that they do not effect anyone else’s code:

@TIG wrote a monkey patch also and posted it over at SketchUcation. But it needs to also be re-wrapped as a refinement.

How do I decode the transformation array?

[0.9138655804147341, 0.4060168727198922, -0.0, 0.0, -0.4060168727198922, 0.9138655804147341, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.5235461230845004, -3.9596473594038883, -30.0, 1.0]

The origin (x,y,z) is the 13th, 14th and 15th items in the array, I have no idea what the other values are and if they are supported or not.

Use ThomThom’s Transform utility when testing plugins & working on code:

There are many places on the web that teach geometry.

Time for some homework of your own. Here are a few Wikipedia articles:

Okay I’m going to assume all of the values in the transformation array are supported and valid. As I suspected and DanRathbun pointed out and confirmed by the TT’s transform utility the transformation array is composed of the transformation matrix.

For my intents and purposes the array values that are useful are the 1st, 2nd, 13th, 14th and 15th. The first two values are the i and j unit vectors which will give me the rotation about the Z axis, to as much precision as I need.

Storing the transformation as the array compliment works great, this avoids the unintended updating of the origin value previously encountered.

group.transformation.origin is convenient but I also noticed with this that it rounds the values to six sig figs and I would rather it not round off my values.

Dan I really appreciate your help on this. I think I now have a path forward for providing an edit feature to an existing truss or foundation assembly. The problem has been what to do if I need to recreate the original assembly (group) but the user has since manually moved or rotated the assembly from its original location.

Again, look at the shapes and window maker examples. (Links above in previous post.)

Sketchup transforms Point3d, Vector3d objects stored in attributes (also those within arrays) when the entity that owns the attributes are transformed. Found this randomly while browsing the code.

I think, that if you store the 3d points/vectors as arrays (triples of floats) then SketchUp will leave them alone.

rotz returns a float. SU have no way of knowing this represent an angle. That’s why it’s not modified. Btw, rotz et al is something Dynamic Component added to the Transformation class. It is unfortunate how it pollute the API namespace like that. Remember that those are not supported API methods - and will not be available when DC is disabled. And that there is no guaranty these will not be removed.

Do you have a snippet or model that show this?

Are you sure this isn’t just the string representation being trimmed? Many of the Ruby classes print a more human friendly presentation of the actual value when you use to_s. inspect is probably the most reliable way to check the values.