# How to get global coordinates of a Point3D

**URL:** <https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073>\
**Category:** Ruby API\
**Created:** [September 1, 2020, 12:22pm UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073 "2020-09-01T12:22:38Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![cristian.banan96](https://avatars.discourse-cdn.com/v4/letter/c/e274bd/32.png) [@cristian.banan96](https://forums.sketchup.com/u/cristian.banan96)\
**Post date:** [September 1, 2020, 12:22pm UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073/1 "2020-09-01T12:22:38Z")

</div>

Hello, I try to get global coordinates of an Point3D. I was looking for this method in documentation, but I did not found anything to match.  
So, is it possible in SketchUp to get somehow global coordinates of an Point3D?  
Thanks for your answers!

---

<div class="post-metadata">

**Author:** ![slbaumgartner](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/slbaumgartner/32/7302_2.png) [@slbaumgartner](https://forums.sketchup.com/u/slbaumgartner)\
**Post date:** [September 1, 2020, 2:55pm UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073/3 "2020-09-01T14:55:07Z")

</div>

The answer relies heavily on your understanding of coordinate systems and the RubyAPI’s Geom::Transformation. If these are not familiar concepts you would be well advised to do some study before proceeding, as you are very likely to get confused otherwise.

At the broadest level, you must determine the Transformation that places the Point3D in the model and apply it using Point3D#transform or Point3D#transform!

There are various situations that need to be handled differently.

In SketchUp, there is always an active editing context (Entities collection) which the the user has open for drawing. This collection may belong to the model (if the user is drawing “loose geometry”) or to a ComponentDefinition (if the user has opened a Group or ComponentInstance for edit).

You can obtain the user’s editing context Entities via model#active\_entities. You can determine whether the user accessed it by opening a specific nest of entities via model#active\_path. You have to build the transformation back to global (model) coordinates by concatenating the transformations for each step of the active path.

On the other hand, if you obtained an object using either InputPoint or PickHelper, they provide methods to determine the Transformation for the picked object. If you obtained an object using model#raytest, along with the Point3D it returns an Array of the nest of objects the ray penetrated to reach the object containing the Point3D. You need to concatenate the Transformations of these to get the global coordinates.

Finally, if your code accessed an Entities collection owned by a ComponentDefinition by some other means (e.g. by #parent applied to some object), the question may be unanswerable. A ComponentDefinition’s coordinates are “abstract” or “local”. They are placed in the model when an instance (Group or ComponentInstance) is created, in which case the Transformation of that instance is needed. But a ComponentDefinition may have many instances, each with its own different Transformation. You have to choose one instance to make the question have meaning.

---

<div class="post-metadata">

**Author:** ![cristian.banan96](https://avatars.discourse-cdn.com/v4/letter/c/e274bd/32.png) [@cristian.banan96](https://forums.sketchup.com/u/cristian.banan96)\
**Post date:** [September 1, 2020, 2:55pm UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073/4 "2020-09-01T14:55:31Z")

</div>

If it helps, I got the Point3D from a (Sketchup::Edge).start.position, which is Point3d of returned Sketchup::Vertex. And the problem it is that this edge is part of an component, which also is part of another component. And I probably will have cases when edge can be encapsulated in less or more components.

---

<div class="post-metadata">

**Author:** ![slbaumgartner](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/slbaumgartner/32/7302_2.png) [@slbaumgartner](https://forums.sketchup.com/u/slbaumgartner)\
**Post date:** [September 1, 2020, 2:59pm UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073/5 "2020-09-01T14:59:40Z")

</div>

So, what did you do to obtain the Edge?

---

<div class="post-metadata">

**Author:** ![cristian.banan96](https://avatars.discourse-cdn.com/v4/letter/c/e274bd/32.png) [@cristian.banan96](https://forums.sketchup.com/u/cristian.banan96)\
**Post date:** [September 1, 2020, 3:09pm UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073/6 "2020-09-01T15:09:25Z")

</div>

First of all I got array of all components definitions which was added after importing main component.

```auto
     before = model.definitions.to_a
     status = model.import("tile_2.skp")
     added_comps = model.definitions.to_a - before
     p "component was imported?: #{status}"
     p added_comps

```

After that I search for needed component by it’s definition name, and after I use grep to get all it’s edges.

```auto
added_comps.each { |comp|
          if comp.name == "Component#2"
            tile_comp_def = comp
            break
          end      
            } 

edges_of_tiles_comp = tile_comp_def.entities.grep(Sketchup::Edge)

```

And in this edges array I found needed edge by it’s length, because it has unique length.

---

<div class="post-metadata">

**Author:** ![slbaumgartner](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/slbaumgartner/32/7302_2.png) [@slbaumgartner](https://forums.sketchup.com/u/slbaumgartner)\
**Post date:** [September 1, 2020, 3:20pm UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073/7 "2020-09-01T15:20:54Z")

</div>

I’ll describe the simplest case first: assume all ComponentDefinitions involved have just one instance. In that case you can access that ComponentInstance or Group via

```ruby
instance = definition.instances[0]

```

Then check instance.parent. If it is the model, just apply the instance.transformation.

If it is not the model, it will be another ComponentDefinition. Save instance.transformation and recurse the line above to find the instance’s parent. Keep recursing until you reach the model. Concatenate all the Transformations to get back to the model coordinates.

If some ComponentDefinitions have more than one instance, the question is fundamentally ambiguous. The Component or Group has been placed multiple times in the model. You have to decide which of them you want to use.

---

<div class="post-metadata">

**Author:** ![JPCodaLot](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/jpcodalot/32/141183_2.png) [@JPCodaLot](https://forums.sketchup.com/u/JPCodaLot)\
**Post date:** [September 1, 2020, 3:22pm UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073/8 "2020-09-01T15:22:56Z")

</div>

Wow,

I had the same problem just the other day.  
I was referencing 4 axes at once!  
@slbaumgartner has got this down flat. 🙂

---

<div class="post-metadata">

**Author:** ![dezmo](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/dezmo/32/51058_2.png) [@dezmo](https://forums.sketchup.com/u/dezmo)\
**Post date:** [September 1, 2020, 4:09pm UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073/9 "2020-09-01T16:09:09Z")

</div>

> [@cristian.banan96](#):
>
> And in this edges array I found needed edge by it’s length, because it has unique length.

I dealt with this accidentally a bit as I trying to learned how hash works … maybe it can be useful.  
(I don’t swear it’s 100% good …)

```ruby
  # Based on: Julia Christina Eneroth
  # Eneroth Material Area Counter
  # Thanks
  #
  #Get all edges and its lengths to hash, 
  # considering all nested transformation
  #ToDo: Copying a group using the copy tool in SketchUp
  # will create copies of the group that share a common definition
  # until an instance is edited manually. 
  # If multiple copies are made, all copies share a definition
  # until all copies are edited manually, or use #make_unique method
  # So in this case the common def will counted once.
  def iterate_entities(entities, transf = IDENTITY, lengths = Hash.new(0))
    entities.each{|entity|
      case entity
      when Sketchup::Edge
        vert_tr = entity.vertices.map{|p| p.position.transform(transf)}
        len_tr = vert_tr.first.distance(vert_tr.last)
        len = entity.length
        scale = len_tr/len
        len = len * scale
        lengths[entity] = len 
      when Sketchup::ComponentInstance, Sketchup::Group
        iterate_entities(
          entity.definition.entities,
          transf * entity.transformation,
          lengths
        )
      end
    }
    lengths
  end
  edges_and_lengths_hsh = iterate_entities Sketchup.active_model.entities
  len = 100
  e_len_is_100_hsh = edges_and_lengths_hsh.select{|k,v| v == len}
  edges_are_100_ary = e_len_is_100_hsh.keys

```

---

<div class="post-metadata">

**Author:** ![DanRathbun](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/danrathbun/32/500_2.png) [@DanRathbun](https://forums.sketchup.com/u/DanRathbun)\
**Post date:** [September 2, 2020, 3:07am UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073/10 "2020-09-02T03:07:14Z")

</div>

> [@cristian.banan96](#):
>
> After that I search for needed component by it’s definition name, and after I use grep to get all it’s edges.
> 
> ```auto
> added_comps.each { |comp|
> if comp.name == "Component#2"
> tile_comp_def = comp
> break
> end      
> } 
> 
> ```

Better to use one of the [`Enumerable`](https://ruby-doc.org/core-2.5.5/Enumerable.html) methods that are mixed into arrays …

```ruby
tile_comp_def = added_comps.find { |comp| comp.name == "Component#2" }

# Always test the result ...
if tile_comp_def # nil if not found
  edges_of_tiles_comp = tile_comp_def.entities.grep(Sketchup::Edge)
end

```

It also would be good if you are making the tile component definition(s), that you give the definition a nice name for finding, rather than have SketchUp throw a vague `"Component#2"` into the `name` property.

> [@slbaumgartner](#):
>
> Concatenate all the Transformations to get back to the model coordinates.

There is not a method named “concatenate” for the [`Transformation`](https://ruby.sketchup.com/Geom/Transformation.html) class, so Steve is talking about matrix multiplication using the [`#*`](https://ruby.sketchup.com/Geom/Transformation.html#*-instance_method) method.

> [@slbaumgartner](#):
>
> In SketchUp, there is always an active editing context (Entities collection) which the the user has open for drawing. This collection may belong to the model (if the user is drawing “loose geometry”) or to a ComponentDefinition (if the user has opened a Group or ComponentInstance for edit).
> 
> You can obtain the user’s editing context Entities via model#active\_entities. You can determine whether the user accessed it by opening a specific nest of entities via model#active\_path. You have to build the transformation back to global (model) coordinates by concatenating the transformations for each step of the active path.

Except that the API has a strange behavior with editing contexts. And this is that any API method returns global/world coordinates when the user is in editing context.

An example, using the DC Sampler “Bench” component. If I double-click in the Bench component, and then into the 3rd back stile from the left, and choose the front bottom edge …

```ruby
ss = Sketchup.active_model.selection
#=> <Sketchup::Selection:0x0000019c46636d28>
e = ss[0]
#=> <Sketchup::Edge:0x0000019c46c4cc30>
e.start.position
#=>(15", 26.5", 20.135822")

```

These are global coordinates.

If I **exit** the back stile instance’s edit context …

```ruby
e.start.position
#=> (0", 0", 0.129727")

```

I get **local** coordinates.

Exit the bench instance’s edit context and the coordinates are the same **local** to the group instance’s definition’s entities collection.

Use the MoveTool to move the bench instance by both x and y.

```ruby
e.start.position
#=> (0", 0", 0.129727")

```

From outside we still get **local** coordinates inside the back stile’s definition’s entities collection.

But double-click into the bench and then into the third back stile again …

```ruby
e.start.position
#=> (75.071", 49.4928", 20.135822")

```

… is now in world (model) coordinates.

* * *

Besides [`model#active_path`](https://ruby.sketchup.com/Sketchup/Model.html#active_path-instance_method) there is also …

> [`Sketchup::Model#edit_transform`](https://ruby.sketchup.com/Sketchup/Model.html#edit_transform-instance_method)
> 
> _Returns the transformation of the current component edit session. If a user has double-clicked to edit a component’s geometry, this will return the transformation of that component, relative to its parent’s origin. This allows one to correctly calculate “local” transformations of a given entity regardless of whether the user is in edit mode._

---

<div class="post-metadata">

**Author:** ![cristian.banan96](https://avatars.discourse-cdn.com/v4/letter/c/e274bd/32.png) [@cristian.banan96](https://forums.sketchup.com/u/cristian.banan96)\
**Post date:** [September 2, 2020, 12:36pm UTC](https://forums.sketchup.com/t/how-to-get-global-coordinates-of-a-point3d/135073/11 "2020-09-02T12:36:53Z")

</div>

Thank you all for your replies, now I understood better how SketchUp works, and what better to use. Based on your answers I searched topics related to how to find instance paths and perform matrix multiplication.  
It worked for me:

```auto
def collect_occurences(instance)
  instance_paths = []
  queue = [[instance] ]
  until queue.empty?
    path = *(queue.shift)
    outer = path.first
    if outer.parent.is_a?(Sketchup::Model)
      instance_paths << path
    else
      outer.parent.instances.each{ |uncle|
        queue << [uncle] + path
      }
    end
  end
  return instance_paths
end

def get_transformation_for_instance_path(instance_path)
  transformation = Geom::Transformation.new() # IDENTITY
  instance_path.reject{ |noninstance| # Model or DrawingElement do not have a transformation.
    !noninstance.respond_to?(:transformation)
  }.each{ |instance|
    transformation *= instance.transformation
  }
  return transformation
end

instance_paths = collect_occurences(edge)
global_transformation = get_transformation_for_instance_path(instance_paths[0])
vert_tr = edge.vertices.map{|p| p.position.transform(global_transformation)}

```
