View.screen_coords(Point) returns different results based on opened entities


#1

I have a model with 3 groups nested in each other, each translated by a different axis (the innermost 10 mm on the red axis, . I have a vertex of the inner most.

I open the innermost group and run in the console:

view.screen_coords(vertex.position)
(29555.15246mm, 7802.605234mm, 12.098059mm)

I close it and run again:
view.screen_coords(vertex.position)
(28756.582618mm, 8377.890821mm, 12.487469mm)

I close that one and again:
view.screen_coords(vertex.position)
(28756.582618mm, 8377.890821mm, 12.022149mm)

I close the outermost and again:
view.screen_coords(vertex.position)
(28756.582618mm, 8377.890821mm, 11.769223mm)

As you can see, even all are translated with respect to the axis of each, the result when the inner most open is different than the other cases.

This phenomena appears again. For example, given g1 for the inner most, this are the results when it is open and then each of the groups is closed (#to_matrix is a method I added to format the transformation nicely, i print the group’s transformation and then the model.edit_transform)

puts g.transformation.to_matrix; puts model.edit_transform.to_matrix
   1.000    0.000    0.000    0.000
   0.000    1.000    0.000    0.000
   0.000    0.000    1.000    0.000
     0mm      0mm      0mm    1.000
   1.000    0.000    0.000    0.000
   0.000    1.000    0.000    0.000
   0.000    0.000    1.000    0.000
    10mm     10mm     10mm    1.000

puts g.transformation.to_matrix; puts model.edit_transform.to_matrix
   1.000    0.000    0.000    0.000
   0.000    1.000    0.000    0.000
   0.000    0.000    1.000    0.000
    10mm     10mm     10mm    1.000
   1.000    0.000    0.000    0.000
   0.000    1.000    0.000    0.000
   0.000    0.000    1.000    0.000
     0mm     10mm     10mm    1.000

puts g.transformation.to_matrix; puts model.edit_transform.to_matrix
   1.000    0.000    0.000    0.000
   0.000    1.000    0.000    0.000
   0.000    0.000    1.000    0.000
    10mm      0mm      0mm    1.000
   1.000    0.000    0.000    0.000
   0.000    1.000    0.000    0.000
   0.000    0.000    1.000    0.000
    10mm     10mm     10mm    1.000

puts g.transformation.to_matrix; puts model.edit_transform.to_matrix
   1.000    0.000    0.000    0.000
   0.000    1.000    0.000    0.000
   0.000    0.000    1.000    0.000
    10mm      0mm      0mm    1.000
   1.000    0.000    0.000    0.000
   0.000    1.000    0.000    0.000
   0.000    0.000    1.000    0.000
     0mm      0mm      0mm    1.000

These methods are not consistent. This also applies to getting the bounding box and calling #contains? with a model space position: it returns true or false depending on if a group is opened or not.


#2

Yes it is annoying. Sometimes local coordinates are returned and other times model coordinates.

Unexpected because the docs do not explain it. (However it is by design.)


Also, the docs do not say that this method accepts a Sketchup::Vertex object. Are you saying it does ? (or do you actually mean Geom::Point3d object ? They are not the same. The former is a model object, the latter is a virtual helper object of the Geom module.)


#3

Yes, the docs do not say that. I’ve lost the examples, so probably just made an edit error. I edited the original post to have vertex.position now


#4

Okay, I edited the topic title.


#5

I think it’s mentioned somewhere in the docs that the returned positions change when an instance is opened. However, that info would probably be applicable to mention several places.
Any suggestions to where you would find it useful to see it mentioned?

I’m wondering if we should be adding some pages to the documentation with general information. As something, like this, doesn’t have a single obvious place to be described.


#6

This applies to all methods taking and returning coordinates as either Point3ds, Vector3ds or Transformations (did I miss any?). I think a separate article with good example would be suitable and links to it in the 3 classes mentioned.


#7

The API docs have a files section, that currently only has the Release Notes.
Thomas and I had thought perhaps later on, the API Tutorials repo could be plugged into the documentation either under the “Files” or somehow under it’s own “Tutorials” category.

The repo is here (Julia, if you feel like grabbing some of your previous educational posts in the forums, and stitching them together into the beginnings of a tutorial chapter.)


#8

Sketchup::InputPoint.transformation() says:

Note that the position method on a input point always returns a point that is transformed into model space. If you are using the edge, face or vertex method on the InputPoint though, you will probably need to use the transformation method to transform the data that you get back from the selected entity.

And then fails to actually give an example …

Sketchup::Model.edit_transform() very vaguely alludes to the coder needing to do some transform calculations, but falls very short.

Again Sketchup::PickHelper.transformation_at() vaguely alludes to translation of coordinates, but does not actually give an example.

I’d prefer Julia’s idea to a separate “white page” on dealing with edit transforms, that various methods in the API can link to. (I’ve seen both new Ruby and C-side SDK coders recently asking for help on this, so linked examples need to be in all API languages.)

We need to teach the old “manual” way, and the new 2017+ use of the new InstancePath class.
Andreas has just recently posted (here or in the SDK category) some examples of combining and using transforms of an edit path.

# From the current editing context:
epath = Sketchup::active_model.active_path
editing = epath.last
leaf = editing.definition.entities.grep(Sketchup::Drawingelement).first
epath << leaf
ipath = Sketchup::InstancePath::new(epath)
combined_transform = ipath.transformation(-1)

# From a PickHelper object:
ph = view.pick_helper
ph.do_pick(x, y)
pickpath = ph.path_at(-1)
ipath = Sketchup::InstancePath::new(pickpath)
combined_transform = ipath.transformation(-1)
# The PickHelper class has had a #transformation_at() method, but the new
# InstancePath class has extra features over just an array, such as access
# to the new persistent IDs.

METHOD MENTIONS THAT SHOULD HAVE LINKS TO A “WHITE PAPER”


#9

Note that I understood positions were given in the context of the active entities, which is why I also added the value of the edit_transform. However, it seems to me, there is no logical combination of them that yields a consistent value. In the first example the group is not translated and the edit transform is translated in 3 axis. In the second the group is translated in all three axis (doesn’t make sense as in this case the middle group is opened, so the inner group is only translated in one axis relative to it), and the edit transform is translated in 3 axis. I can see no combination that produces a consistent result in just these to cases, and similarly in the next two


#10

If you have a little test model, it helps to attach it to the original post.


#11

weird.skp (136.0 KB)

Sure. This is the model I used for my examples


#12

The coordinates are usually given and assumed to be in the local coordinate system (the group/component’s own axes) except when the group/component is opened for editing, then the coordinates are in model space. Only when you are working with coordinates of an Entity in the active Entities collection the model#edit_transform should be taken into account.


#13

In the example I gave, the top matrix is when the innermost group is open. That group’s translation is 0 and the edit transform is 10,10,10. This makes sense.

The matrix below is when the group is closed but its parent is opened. The group’s translation is 10,10,10 and edit transform is 0,10,10. OK, let’s say I ignore it.

But then I close the parent group and now the group’s translation is 10,0,0. What?? And the edit transform doesn’t help me here (even if I find a way to use it although my group is not in the active entities)

Finally, I close the “grandparent” group. Now the innermost group is supposedly transformed by 10,0,0, but the edit_transform is 0,0,0

So I don’t see any way of combining or ignoring these transformations that is consistent. But even viewed individually, they don’t make sense. The group is translated 10,0,0 in relation to its parent and 10,10,0 to its grandparent and 10,10,10 in relation to the model. Similarly, the edit_transform should have been 10,10,10, then 0,10,10, then 0,0,10 and finally 0,0,0


#14

When the group itself is open it seems SU returns an identity transformation for its position. This is an odd and undocumented behavior that I can’t explain.

When the drawing context containing the group is opened for editing the matrix is returned in global coordinates, not the local coordinates its stored as internally. Again this is an undocumented behavior but one that I’ve gotten quite used to.

To get the “real” (aka local) transformation matrix for the group you need to multiply it with the inverse of the edit_transform. Note that this only applies for entities in the active entities collection.

local_transformation = g.transformation * g.model.edit_transform.inverse

This is in my view the first sensible return value. This return value is the actual transformation the group has (relative to it’s container). This is the value that is stored internally, with no extra transformation applied behind the scenes.

edit_transform returns how the current drawing context’s axes relates to the model axes and isn’t relevant here since the group isn’t in the current drawing context.

Again the group’s transformation is returned as it is saved internally, relative to the containing groups axis. No dark sorcery here. The value you get is the value SketchUp stores internally. The edit_transformation is the identity matrix since you are in the model root and not inside a transformed group.


#15

Here’s a short example of how you can get the local coordinates of an Entity (the one SketchUp stores internally).

def normalize_coordinates(entity, method)
  v = entity.send(method)
  
  if entity.parent.entities == entity.model.active_entities
    # Entity belongs to the current/active entities collection.
    # Here SketchUp for some reason transforms the coordinates to model space before returning it.
    # Transform it back to the logical and consistent local coordinate space.
    
    # Transformations and other coordinates (Point3d and Vector3d) requires different methods for applying transformations.
    if v.is_a?(Geom::Transformation)
      v *= entity.model.edit_transform.inverse
    else
      # v is a Vector3d or Point3d.
      v.transform!(entity.model.edit_transform.inverse)
    end
  end
  
  v
end

# Select a group.
g = Sketchup.active_model.selection.first

# Change drawing context to your liking. This method should return the coordinates in local space.
normalize_coordinates(g, :transformation)

It was quite new to me that Entities collections that are parents to the active Entities collection also works differently so the code doesn’t work for those. However you very seldom edit something in a drawing context further up the hierarchy to where the user is. That also explains why I’m not familiar with how coordinates works in these Entities collections.


#16

[quot[quote=“eneroth3, post:15, topic:47272”]
Here’s a short example of how you can get the local coordinates of an Entity (the one SketchUp stores internally).
[/quote]

So for my example, calling this method on g will return (10,0,0) translation except if the group itself is opened in which case the result is (0,0,0). So we’re still at an inconsistency, but a reduced one. Maybe this warrants opening a ticket for Sketchup @tt_su ?


#17

After looking into it more it turns out all parent drawings contexts to the active one also uses global coordinates instead of their own coordinate systems.


#18

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.