Determining orientation of Boxes & Cylinders

Making my own GrassHopepr components with VB.NET I will be gradually translating and tilting Brep boxes and cylinders “in blind code”, and need a way to determine their orientation when a number of iterations are done.

I’ve tried examining the orientation of Brep boxes and cylinders in different ways using surface Normals and corners, but it seems that neither Box corners nor surfaces comes in fixated order. For example, if I have a Box with the (bottom) corners in a certain order (say CCW around the bottom) and then generate a BoundingBox around the first Box and examine the corners of the BoundingBox, then it doesn’t nessecarily return the corners in the same order. (also, if exploding a Box and joining it again the corners and surfaces ends up in arbitrary order).

I had planned to use either the corners, or the surfaces (normals) to determine the inclination degrees of Boxes and Cylinders, but if both the surfaces and the corners are returned in arbitrary order (for example using GetCorners()), how can I determine the oreintation of an object? **

I will need to know the orientation of Box relative to its start orientation** since the Box may have other objects associated with it, which means that even a perfect cube Box - if tilted 180 degrees - will be considered “upside down”. But how can I know that it’s upside down by examining the cube if it isn’t given which surface is considered being the original “top” surface?

I conclude that I will have to rethink the entire strategy for determining 3D orientation, so I am listening carefully to advice from experience.

// Rolf

You will always have issues trying to reconstruct a (set of) transformation(s) by looking only at the end result, especially if the object in question is a Brep. It’s somewhat easier for Meshes because their vertices aren’t reordered when transforming.

The best way to keep track of transformations in my opinion is to add custom user data to the object. This user data will be transformed when the parent object is transformed, allowing you to record all changes over time. It’s a fairly involved solution, but the only one that will really work if people other than you might transform the geometry.

If you want to know more, press 1. If you’d rather explore alternative solutions, press 2 now.

Ooo! Wait! Stop! This may not be possible from within a GHA project because custom user data requires that it is associated with a specific Rhino plug-in.

@stevebaer will this work with UserDictionary?


Edit: probably yes. It seems UserDictionary in fact has build-in support for transformations, so you may not even need to do any of that yourself.

Notice that I’m creating the geometry in GH and then iteratively translate it an tilt it, so if individual surfaces can be tagged on creation even if they are joined into a solid Brep (…?), then I would for sure be able to keep track of it’s orientation… Hm.

Great idea.

// Rolf

@DavidRutten, @stevebaer,

i am interested in that too. As far as i have tried, geometry eg. a Point3d added to an ArchivableDictionary and then added that as Geometry.UserDictionary all transforms done on the object do not propagate to the stored point geometry automatically. The help states that this propagation does happen by default, but i do not see that, maybe i am not setting it up properly. It is a pain to do this using events via python and a source for errors. An example would really be great.

c.

@clement, haven’t tested this yet, but I think the point is that the Transform property on UserDictionary keeps track of transformations, provided you do not short-circuit the OnTransform method.

Modifying data in your own dictionary (such as points, vectors, etc.) is not automatic, but the Transform property will keep track of all transformations applied to that userdata. Since @RIL is only interested in the final transform, that’s all he should need to look at.

I’m not sure of that I will need only the final transform, since I will iterate through a whole set of different transforms, using different methods (Galapagos, or For-loops in various patterns) and evaluating result-parameters after each transform.

I suspect that I will not always have full control over input data for each individual transform (using increments rather than absolute values) and for this reason I would like my components to be self-aware regarding their orientation so they can output their positions, rotations and “tilt” angles after each iteration (for evaluation of output parameters before deciding to iterate yet another round etc). But, I may have to rethink this if it gets too complicated.

I also need to be able to redraw the transformed objects after each iteration, which in for GH components is best done between solver executions (if I have understood this correctly).

// Rolf

@DavidRutten thanks for that hint. I’ve made a tiny example to explain and tried to access the user data transform. Indeed it only keeps track of the very last transform.

To test below example, please create a circle at the origin and run the script once. This will add a point at 0,0,0 to the UserDictionary of the circle. If you make now only 1 transform, eg. move the circle and run the script again, the point in the UserDictionary gets extracted properly, it moved with the circle as i have applied the transform found in the UserData. So far this is exactly what i want. My problem is, if i do 2-3 transforms of the circle, it fails as the user data only holds the last transform made as you say.

UserDataTransform.py (584 Bytes)

The question is, is it possible to make it automatically update the point (or geometry) stored in the UserDictionary without setting up events ?

c.

I must say that I don’t fully understand the “last transform” part. I must be missing something essential about transforms, but I expected “last transform” to represent the delta between previous location/orientation and the last movement.

But when I tested your script, it seems to me that the transform represents the delta between the inital location/orientation and the last landing place, which causes the transform to make “exponential” size movements, starting from the last (current) position.

None of that makes any sense to me.

// Rolf

Hi Rolf,

create the circle at the origin, run the script. Move the circle 10 units in positive x axis direction, then move it again 10 units in positive y axis direction.

If you then run the script again, the point gets extracted at 0,10,0, which shows that it only found the last transform made. What i am trying to achive is that the point in the dictionary gets updated whenever the circle gets moved, without using events and using python.

c.

UserDictionary holds onto the total combined transformation information through the Transform property. I don’t think I understand the request in this post enough because this sounds like you don’t need to track any transformations if all of the transformation is happening inside of a component. I guess the “iterative” part is what is throwing me off.

User dictionaries hold onto their combined transforms. They do not apply this transformation to items inside of the dictionary.

Yup, that’s how I perceived it, saying that the transform is based on the delta between the intial position and the last landing ditto. Therefore one cannot use the transform multiple times, only once, when arrived at a final satisfactory position.

It would be perfect if that behaviour could be altered - either

  1. From intital position to last, or
  2. from previous position to last

In the latter case it could be used for moving around clusters of objects in automatic iterative stepwise “form-finding” algorthims etc. I regret that that is not possible since it supports only alternative 1… :frowning:


Edit: But, perhaps its possible to “hack” the storing of the transform to store only the delta between the last and the previous orientation?

// Rolf

If you are performing iterative transforms on the geometry, I would recommend just writing this information yourself into the dictionary.

Yes, why not? But I see no example code for how to handle this… :telescope: :microscope: :thinking:

// Rolf

@stevebaer, i guess i do not understand the “combined” part in your answer. Did you run my script from above and read my reply to Rolf ? Why does it only get the last made transform (along y) if you first move the circle along x axis and then along the y axis ?

How can i apply the transform made to the circle to the geometry stored in the user dictionary ?

c.

It took me a while to figure out what is going on in your script. I believe that the pt variable is being interpreted as an reference to the point (boxed object in .NET terms). I tweaked the script to ensure a separate copy of the point is created and things work as you would expect.

import Rhino
import rhinoscriptsyntax as rs

def DoSomething():
    id = rs.GetObject("Select", 0, True, True)
    if not id: return
    
    obj = rs.coercerhinoobject(id, True, True)
    my_point = Rhino.Geometry.Point3d(0,0,0)
    
    rc, pt = obj.Geometry.UserDictionary.TryGetValue("MyPoint")
    if not rc:
        obj.Geometry.UserDictionary.Set("MyPoint", my_point)
        print "Stored the point"
    else:
        # make sure we are working with a unique instance of the point
        pt = Rhino.Geometry.Point3d(pt)
        # ^^^^^ This is what I aded
        user_data = obj.Geometry.UserDictionary.ParentUserData
        pt.Transform(user_data.Transform)
        rs.AddPoint(pt)
    
DoSomething()

Hi @stevebaer, hm that seems to behave as my script and it does not show the point in the center of the circle after it has been moved 2 times.

Maybe i should explain what i am trying to do again: The example from above using a circle and a point coordinate is just a simplified one. Actually i am trying to store geometry (eg. a point or another curve) into a curve which is in the document. What i want to happen is that when i transform the curve in the document, this transform is applied automatically to the stored geometry in the UserDictionary. So simply speaking, i would like to propagate the transform.

c.

I just tried this in both V5 and V6 and get the same result. I create a circle centered at the origin and run the script. Then I drag the circle and run the script again, repeating… Every time the a new point is created at the center of the new circle location. The dictionary’s transform is always being applied to a point with values of 0,0,0

We would need to add some additional plumbing to have this transformation be automatically applied to items in the dictionary.

Thanks @stevebaer, yes with one single transform it works of course. But if you move the circle around, rotate, scale etc. , save the file and reopen it, transform again and then extract the point from the circle, the transform is not right.

This is what i’ve tried, i’e hooked into various events like BeforeTransformEvent, the ReplaceObjectEvent, set up custom UndoEvent tried to check if the transformation is done with making a copy etc. The problem is, if a user never starts these events, i cannot be sure that the transform is ever updated and when i extract the stored geometry, it is not in the right place. Therefore i wondered if i could just hook on to the OnTransform somehow and define that in the moment of the transform, the transform matrix is just applied to the stored geometry. It would be just a single line of code but i do not know how to access OnTransform via python.

c.

1 Like

@clement did you end up achieving what you were looking for?