Script for converting DXF level data into Construction Points (for Fredo Toposhaper)

I get my survey data as 2D DXF files, and I sculpt the topography of a site using Fredo’s Toposhaper ( TopoShaper | SketchUcation ). It takes an input of construction points as nodes for the topography. I find it VERY useful.

You need the construction points though in the first place, and I have automated the process of taking the DXF data and converting them into construction points at the correct Z-level.

Here is my process:

  1. After importing the DXF file (which will be imported without text), import the text from the DXF file as Sketchup Text Labels. I use this plugin that I made to import text: SketchUp Plugins | PluginStore | SketchUcation . The model now looks like this:

  2. Select the text labels that have the levels data in them (hopefully they are in their own layers/tags for each selection), and the points (crosses) that represent the point next to them. Group them to isolate them. The points (crosses) in my case are component instances, with its axis in the centre of the cross (this will be important later).

  3. In the group, select all of the text labels with the level data and the points (crosses, which are component instances). Then run this script in the Ruby Console:

### SCRIPT for converting levels in text label to construction points.
### by TommyK 2022
### Use, copy, adapt, sell, however you like.
def is_number? string
  true if Float(string) rescue false
end

# Make separate lists of Text and ComponentInstance entities
instances = []
texts = []
Sketchup.active_model.selection.each{ |ent|
    if (ent.class == Sketchup::Text)
        texts << ent
    elsif (ent.class == Sketchup::ComponentInstance)
        instances << ent
    end
}
Sketchup.active_model.start_operation("Levels from Text")
# Run through all of the text labels to try to extract level data, and
texts.each{ |txt|
    puts "adding point " + txt.text
    # Find the instance representing the point on the survey which is closest to the text
    instances_sorted = instances.sort_by{|inst| inst.transformation.origin.distance(txt.point)} # Sorts instances in order of distance to the text label.  Uses the origin of the instance only
    inst_point = instances_sorted[0].transformation.origin  # Select the closest point to the text label.
    if is_number?(txt.text)
      height = Float(txt.text).m  # <= assumes input of level is in metres. See https://ruby.sketchup.com/Numeric.html for other units: .cm .feet .inch. .km .m .mile .mm .yard  can be used in its place.
      point = Geom::Point3d.new(inst_point.x, inst_point.y, height)
      #Sketchup.active_model.active_entities.add_cline(inst_point, point) # <= optional, if you like to see the lines from the instance to the new construction point
      Sketchup.active_model.active_entities.add_cpoint(point) # adds the construction point at the correct level
    end
}
Sketchup.active_model.commit_operation

If you’re lucky, you should have something like this:

… and it is ready for Fredo’s Toposhaper.

You can adapt the script to your specific situation.

You might find that you only need to take the x and y coordinates directly from the Text labels’ coordinates, so you might prefer to use this script instead (takes it straight from text label):

### SCRIPT for converting levels in text label to construction points.
### by TommyK 2022
### Use, copy, adapt, sell, however you like.
def is_number? string
  true if Float(string) rescue false
end

texts = []
Sketchup.active_model.selection.each{ |ent|
    if (ent.class == Sketchup::Text)
        texts << ent
    end
}
Sketchup.active_model.start_operation("Levels from Text")
# Run through all of the text labels to try to extract level data, and
texts.each{ |txt|
    puts "adding point " + txt.text
    if is_number?(txt.text)
      height = Float(txt.text).m  # <= assumes input of level is in metres. See https://ruby.sketchup.com/Numeric.html for other units: .cm .feet .inch. .km .m .mile .mm .yard  can be used in its place.
      point = Geom::Point3d.new(txt.point.x, txt.point.y, height)
      #Sketchup.active_model.active_entities.add_cline(inst_point, point) # <= optional, if you like to see the lines from the instance to the new construction point
      Sketchup.active_model.active_entities.add_cpoint(point) # adds the construction point at the correct level
    end
}
Sketchup.active_model.commit_operation

Let me know if you end up using this script :slight_smile:

1 Like

To prepare files like this in AutoCad I wrote an AutoLisp script that read the contents of text labels and drew a vertical line from the text insertion point up to the designated height. When imported into SketchUp this worked quite well with Sandbox from Contours.

1 Like

Does this have the makings of a very useful extension?

Haha, yes, potentially. It is such a small script though, and the circumstances of the survey file may be different to mine, so some adapting may be needed. For budding scripters, working with other people’s scripts I think are a great way to learn about the Ruby API. This is certainly how I started scripting in Sketchup.

I guess I meant an extension that combines what seem to be two operations: firstly getting the text data and then creating the construction points. Could that work?

I have just detected a bug in SU2020 which means that the heights of the construction points created will be relative to the model axes, not relative to the axes of your current context. If the group or component in which your spot levels are given are not at the zero height relative to the model axes, then the heights of the spots may seem off.

This only applies to the heights (z values), not the x/y coordinates. Very strange indeed.