What is coordinates relative to?


#1

What are coordinates in the API really relative to? When I draw a rectangle, group it and run

Sketchup.active_model.entities[0].entities[0].vertices[0].position

it returns the origin point since the first vertex of the first entity happens to be on the group’s local origin. This is the same no matter how I move around the group.

However, if I enter the group it seems like the coordinates are suddenly relative to the global axes, not the local. When I enter a group inside of this group the coordinates are global but when I enter other groups outside it the local coordinates are returned.

This seems very odd to me. Is there an article anywhere regarding what coordinates are used where?


#2

I can reproduce it, but it’s a mystery to me why it is so. With the principle of least surprise one would expect it always gives coordinates relative to the same origin. If it was convenient for developers to have them relative to maybe the active context (it’s not even that), then developers could easily and reliably achieve that in another way.

So when the active context (Sketchup.active_model.active_entities) is in the hierarchy

  • higher than the vertex, vertex.position gives coordinates in the local coordinate system.
  • equal or deeper than the context of the vertex (vertex.parent.entities), it gives coordinates always to the model origin (Sketchup.active_model).

This sounds like it could affect many plugins. Though raytest (have to test inputpoint) give the right position, only reading position from entities and creating entities are affected.


#3

When you open a group or component the coordinates in SketchUp is being adjusted. To get the real local coordinates you must take into account Model.edit_transform.


#4

Now I remember edit_transform.

I have just been searching one hour through my Console’s HighlightEntity tool, and was wondering why it works! It draws entities by their vertices and the vertex positions change with the active context as Christina describes. But I didn’t need to use edit_transform.

The reasons is, not only does SketchUp return different coordinates for an entity when the active context is equal or deeper, but it also gives different transformations for component instances. This compensates it, but I’m still worried that this behavior is so unobvious.


#5

edit_transforms doesn’t seem to help me since it returns a transformation from the transformation of each opened group/component while the coordinates I have are always relative to the context they are drawn in no matter current drawing context, except when the current drawing context is the same or deeper as the one they are drawn in. If I enter a different group or component edit_transformations would change but not the coordinates I’ve got.

From what I understand here this behavior isn’t documented :S . For my implementation I can probably assume the coordinates are in the local axes since the user shouldn’t be inside the group/component that is edited but it’s still a bit annoying to not fully understand what is going in.


#6

I got the global coordinates by walking recursively up the instance path from the entity (eg. edge) to the model, and multiplying all transformations. This appears to give always the correct result if you wanted the global coordinates. But the inverse should also be possible to turn the (falsely) global coordinates back into local coordinates.

# hierarchy:
# model → group1 → group2 → group3 → group4
#                         ↳ edge

# edge_instance_path = [group1, group2, edge]
# model.active_path = [group1, group2]

points = edge.vertices.map{ |v| v.position }
# These points are local coordinates, except if the active context is the context
# of the edge or deeper (group2, 3, 4); that means if the active path is equal or 
# longer than the edge's instance path (we use length 1 to ignore the edge).
if (edge_instance_path - model.active_path).length == 1
  # Then we get the following instances between the edge until the active context.
  delta_tr = (model.active_path - edge_instance_path).
  # and multiply their transformations.
  inject(IDENTITY){ |tr, instance| tr *= instance.transformation }
  # This is the factor by how much the edit_transform is too big 
  # (the transformations it should not include). 
  # So we multiply with the inverse transformation to remove it:
  # transformation = model.edit_transform * delta_tr.inverse
  # This would be the global transformation in the context of the edge.
  # Since we have the global coordinates of the edge but want local ones, 
  # we use the inverse.
  transformation = delta_tr * model.edit_transform.inverse
  points.map!{ |p| p.transform(transformation) }
end