How to get components global coordination

Take care of two things:

  1. You need to define which coordinates you are looking for. A component contains drawing elements that extend in space and thus have many coordinates. Most likely you mean the coordinates of the component origin.

  2. You need to distinguish component definitions and component instances. An instance is a “clone” of the definition with a specific transformation and inside a specific parent entities collection. Since the entities collection itself may be contained inside a “parent” component definition (with multiple instances), a single component instance can have many occurences in the model – everywhere where the parent component has an instance. Thus refering to a component instance is ambiguous, we need to consider the whole instance path (array of nested parent component instances).

In your test you were probably calling bounds.center on the component definition, or you were comparing two occurences of the same component instance (inside two different instances of the parent component, like:
(component definition A contains an instance of definition B)
[Model, A#1, B#1]
[Model, A#2, B#1]
Then for both occurences of B#1, B#1 has the same transformation and origin. To get the global coordinates, you have to accumulate the transformations of all instances in the instance path.

Example to collect instance paths:

def collect_all_instance_paths(model, &condition)
  instance_paths = []
  contained_instances = {}
  model.definitions.each{ |definition|
    instances = definition.entities.grep(Sketchup::Group).concat(definition.entities.grep(Sketchup::ComponentInstance))
    instances = instances.select(&condition) if block_given?
    contained_instances[definition] = instances
  }
  instances = model.entities.grep(Sketchup::Group).concat(model.entities.grep(Sketchup::ComponentInstance))
  instances = instances.select(&condition) if block_given?
  queue = instances.map{ |instance| [instance] }
  until queue.empty?
    path = *(queue.shift)
    instance = path.last
    next if instance.nil?
    contained = contained_instances[instance.definition]
    instance_paths.push(path)
    unless contained.empty?
      contained.each{ |instance2|
        queue.push(path + [instance2])
      }
    end
  end
  return instance_paths
end

# or

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

instance_paths = collect_occurences(some_instance)

Once you have an instance path, aggregate the transformations:

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

global_transformation = get_transformation_for_instance_path(instance_path)

Then apply the transformation to vertex positions of the geometry inside the most inner instance, or get the global position of the origin (which (0,0,0) when seen from inside the component definition):

global_transformation.origin
3 Likes