Ruby Code for Recreating Native Text Tool

Hi All,

I want to slightly change the behaviour of the text tool so it defaults to showing tags and other info (as opposed to the length of an edge).

I got quite far recreating it from scratch, but I think the native tool has some clever logic where it uses the camera position to determine the placement of the text.

Has anyone successfully implemented something similar before?

Thanks!
Ross

(1) All primitive geometry (edges and faces) should be untagged (using Layer0.)

(2) The native text (callout) tool uses “text always face camera”. When you use API methods to create text callouts, what specific problems do you have. (Please show screen snips or post code.)

Hi Dan,

Thanks for the very fast reply. I’ve uploaded a screenshot that hopefully shows the issue.
The native tool (blue mark up) cleverly infers an insertion point from the camera position that makes sense.

My tool (green mark up) places it the text really far away.

Full code is below. Thanks! Ross

module RTSAPTools
module LabelThermalBridges
class Tool
def initialize
@start_point = nil
@instance_path = nil
@label_text = “”
@label_mode = :unset
end

def activate
    return unless @label_mode == :unset
    show_label_mode_dialog
  end

  def show_label_mode_dialog
    html = <<-HTML
      <html>
      <body style="font-family:sans-serif;padding:10px;">
        <h3>Label Mode</h3>
        <form>
          <input type="radio" name="mode" value="short" checked> Short (e.g. E05)<br>
          <input type="radio" name="mode" value="full"> Full label<br><br>
          <button onclick="submitForm(); return false;">OK</button>
          <button onclick="cancelForm(); return false;">Cancel</button>
        </form>

        <script>
          function submitForm() {
            let selected = document.querySelector('input[name="mode"]:checked').value;
            sketchup.submit_choice(selected);
          }

          function cancelForm() {
            sketchup.cancel();
          }
        </script>
      </body>
      </html>
    HTML

    @dialog = UI::HtmlDialog.new(
      dialog_title: "Label Thermal Bridges",
      preferences_key: "label_thermal_bridges",
      scrollable: true,
      resizable: false,
      width: 320,
      height: 200,
      style: UI::HtmlDialog::STYLE_DIALOG
    )

    @dialog.set_html(html)

    @dialog.add_action_callback("submit_choice") do |_, mode|
      @label_mode = mode.to_sym
      @dialog.close
    end

    @dialog.add_action_callback("cancel") do |_|
      @dialog.close
      Sketchup.active_model.select_tool(nil)
    end

    @dialog.show
  end

  def onMouseMove(flags, x, y, view)
    @mouse_ip = view.inputpoint(x, y)
    @x = x
    @y = y
    view.invalidate
  end

  def onLButtonDown(flags, x, y, view)
    return unless @mouse_ip
    return if @label_mode == :unset

    if @start_point.nil?
      @start_point = @mouse_ip.position
      @instance_path = @mouse_ip.instance_path
      edge = @instance_path.to_a.grep(Sketchup::Edge).first

      if edge && edge.layer
        name = edge.layer.display_name
        @label_text = (@label_mode == :short ? name[0, 3] : name)
      end
    else
      end_point = @mouse_ip.position
      vector = @start_point.vector_to(end_point)
      entities = Sketchup.active_model.active_entities

      if @instance_path&.valid?
        entities.add_text(@label_text, [@instance_path, @start_point.transform(@instance_path.transformation.inverse)], vector)
      end

      @start_point = nil
      @instance_path = nil
    end
  end

  def draw(view)
    return unless @mouse_ip

    @mouse_ip.draw(view) if @mouse_ip.valid? && @mouse_ip.display?

    if @start_point
      view.draw_text(Geom::Point3d.new(@x, @y, 0), @label_text, size: 22)
      view.drawing_color = 'black'
      view.draw_line(@start_point, @mouse_ip.position)
    end
  end

  def getExtents
    bbox = Sketchup.active_model.bounds
    bbox.add(@mouse_ip.position) if @mouse_ip&.valid?
    bbox
  end

  def reset(view)
    @mouse_ip.clear
    @label_text = ""
  end
end

# Entry point
def self.run
  Sketchup.active_model.select_tool(Tool.new)
end

end
end

Well, my first thought is that you are transforming the @start_point but not transforming the end_point used to create the leader vector.

Secondly, I would get the @mouse_ip for the second click within the onKeyDown callback. (Just me I suppose.)

1 Like