Extract geometrical information from a .skp file

Hi!

I am working in a plugin code to export a sketchup file containing a room to other applications. That requires the creation of a text file with a numerical description of the surfaces contained in the room identified by the position of the vertices (or maybe the edges) defining each of the surfaces. I can find all the information I need in the “entities” (i.e. Sketchup.active_model.entities).

My problem is that in order to access these “entities” I need beforehand to explode all Groups and Components of the room, which I am observing is terribly slow for rooms containing a high number of Groups/Components. I am using this code for that:

   defs = Sketchup.active_model.definitions

    while therearegroupsleft 
       therearegroupsleft = false
       defs.each do |d|
         d.instances.each do |e|
           if (e.layer.visible?)                 # NOTE( In this way I only explode groups and Components belonging to visible layers)
             if (e.is_a? Sketchup::Group)
               e.explode
               therearegroupsleft = true
             elsif (e.is_a? Sketchup::ComponentInstance)
               e.explode
               therearegroupsleft = true
             end
           end
         end
       end
     end

I have the following questions:

1.- Do we need to explode to access the required information? Can I extract somehow from definitions or instances the info I need?
2.- Is this procedure for exploding optimal?, there is a way to use all the computational power of my PC? (maybe parallelize the process or do it in a different way that increases the performance)

Thank you very much for your help, please let me know if you need further information to give me an answer.

Best Regards,
Carlos

  1. Exploding a whole model is not ideal, and for reading information it’s certainly should not be needed. But in order to provide information to what to do instead we need some more information to what the task here is.
    I’m going to make a guess to what is going on, are you using model.entities to read the entities of the model? If so, then note that you only get the root level entities via that. In order to traverse the whole model you need to look for groups and components and recursively dig into them.

Something like this:

module Example

  def self.do_something
    model = Sketchup.active_model
    self.read_entities(model.entities)
  end

  def self.read_entities(entities, transformation = IDENTITY)
    entities.each { |entity|
      case entity
      when Sketchup::Group, Sketchup::ComponentInstance
        # Note: Older versions of the SketchUp API did not have .definition for
        # groups.
        definition = entity.definition
        # When you read the positions of vertices inside component definitions
        # they are local to the definition. In order to get the global
        # coordinates for each vertex in each instance you need to combine
        # the transformation to the instance path.
        tr = entity.transformation * transformation
        # Recurse into child-instances.
        self.read_entities(definition.entities, tr)
      else
        # All other entities...
      end
    }
  end

end
  1. The Ruby API is single process only. You can only call it from the main thread. Explode is in itself very slow. That along with it being a destructive operation makes it something you want to avoid when you really just need to read data from the model. Traversing the model without modifying it will be the fastest you get.
2 Likes

And assume you have a method named export_point() that takes an array of [x,y,z] numerics …
the “all other entities” else clause might look similar to:


      else
        # All other entities...
        case entity
        when Sketchup::Edge
          export_point(entity.start.position.transform(tr).to_a)
          export_point(entity.end.position.transform(tr).to_a)
        when Sketchup::Face
          entity.loops.each {|loop|
            if loop == entity.outer_loop
              # this is the outer bounds of the face
            else
              # this is an inner hole of the face
            end
            loop.vertices.each {|vertex|
              export_point(vertex.position.transform(tr).to_a)
            }
          }
        when Sketchup::Curve, Sketchup::ArcCurve
          if entity.is_polygon? # made with PolygonTool
            if entity.first_edge.start == entity.last_edge.end
              # it is a closed Polygon
            end
          end
          entity.vertices.each {|vertex|
            export_point(vertex.position.transform(tr).to_a)
          }
        else
          # There are some other entities that may appear
          # such as SectionPlane, Dimension, Image, Text,
          # ConstructionLine, ConstructionPoint, etc.
        end
      end

And I would use extend self inside the module (as one of the first few lines,)
so you do not need to qualify each method call with self.method_name, etc.

After exploding all rooms and Components with the code above, I simply look at entities that are Sketchup::Face.
If the face has a single Loop then I write to a file the vertices, If there are several loops then I write to
the file the edges for all loops contained in the face. This is all I need to do.

The problem is that to create the entities in a model containing Groups/Components then I need to explode, is that correct?
In the code you are showing you are reading model.entities, Do I need to explode before reading the entities as you propose in case the room contains Groups/Components?.

Thanks for the help!

Using ThomThom’s recursive ‘read_entities’, you don’t have to explode anything. Applying the transformation to the vertex.position will give you the ‘real world’ value for that point.

1 Like

NO

NO

… and entities of groups and components. We did not show any exploding. Exploding is not needed!

Thank you for being so clear!, I manage to do what I need using read_entities and without exploiding :slight_smile:

Actually, I had some problems with the transformation thing, sometimes I observed that some of the groups
where not exported to the right place. I tried Thomthom’s self.read_entities using

tr = transformation * entity.transformation
instead of
tr = entity.transformation * transformation

And things looks nice now (transformations are not always commutative).

Other thing suggested by Dan is the use of the method .to_a when exporting the points. I observed that the resulting
output do not show the unities symbol after each component of the point (which is very convenient when writting all the points
of a room in a text file). How can I know the length unities of exported points? can I choose the unities to be meters?

Sketchup stores all data as decimal inches. However you can convert your data to current units by using a_var.to_l where a_var is your variable. You can then set units and turn on or off units from within model info \ units.

1 Like

Whoops! Thanks for posting back the correction to that. Got my wires crossed when I typed up that example.

To add to that, the SketchUp API will return a Length type for most things representing units. When you do Length#to_s (explicitly or implicitly) it’ll be formatted according to the model units. To serialize coordinates you want to extract the underlying raw value (Length#to_f) which is always represented in inches.

For more details: Dealing with Units in SketchUp | Procrastinators Revolt!

1 Like

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