Selecting edge and endpoint

I’m trying to figure out how to let the user of my extension select an edge AND one of the endpoints of the edge in order to construct some geometry with respect to that selected point.

A dialog with 4 options (left / right / top / bottom) would do the trick as I’m working in 2D only. However, I would like to implement a slightly improved variation of that which involves the user selecting the edge and then having a mechanism to color the endpoints. Thus, the dialog would only present the user 2 options instead of 4.

To this end, I was considering adding construction points via the Ruby API but I was unable to customize them via the material and layer attributes. I would ideally like to change the tiny plus that’s barely visible with some sort of filled dot of a given color. Is that even possible?

Or is there a better way to solve my original problem?

Yes, however you should do this the Sketchy way.

This means coding a custom Ruby tool that leverages a PickHelper or InputPoint object. A tool can draw to the viewport (your little endpoint bulbs) or just use SketchUp’s native endpoint inferencing.

You should look at the example tools on GitHub:

1 Like

Thanks Dan! I will study those tomorrow.

Currently I have a Command instead of a Tool as the user is supposed to first select the edge and then invoke the command via a menu item. It sounds like I have to transition all that code into a Tool object instead, right?

1 Like

Yes, because you want both the edge and a specific endpoint, and the GUI selection tool can’t do that. To make a more refined selection you need to create a Tool.

2 Likes

Yup, and it’s actually really simple to detect when the user has clicked on the vertex (or close to it):

def onLButtonDown(flags, x, y, view)
  vertex = view.inputpoint(x, y).vertex
    
  if vertex
    puts "We clicked on a vertex, woo-hoo!"
  end
end

Now I need to figure out how to customize this vertex object to let the user know that they actually clicked on it.

The first thing is to let your tool code know it by setting a state variable. It can be as simply named as @state which would be initialized in the activate() callback to 0 and then after the vertex is selected it would be incremented to 1. (Again see the example line tool.)

Normally your callbacks will have a conditional branching construct based upon the value of @state. Ie, when == 0 then pick a vertex, when 1 do the next step, etc.

To let the user know, the tool can draw virtually to the view and highlight the edge and/or draw a little box around the vertex. See:

Another thing some tools do to indicate tool state is to have different cursors for each state. See:

1 Like

You’ve been super helpful once again! My idea was to create a shape that lives in the z = 0.01 plane to create the illusion that the user has selected their desired point… But the View#draw() method solves this problem a lot more elegantly.

I’ve also leveraged the view.camera.height attribute to maintain the size of the point constant even when the user zooms in and out. The one thing that took me a bit of time to figure out was calling the view.invalidate method inside onLButtonDown(...). Without that the draw function wouldn’t get triggered and thus my vertex marker wouldn’t show up when left-clicking.

Here’s the implementation I came up with:

class PointHighlighter
  NO_POINTS = 24

  def activate
    @vertex = nil 
  end

  def onLButtonDown(flags, x, y, view)
    @vertex = view.inputpoint(x, y).vertex
    view.invalidate
  end

  def highlight_point(view)
    # Display a point that keeps the same size to the user.
    x, y = @vertex.position[0], @vertex.position[1]
    r = view.camera.height / 250.0

    points = []

    (0...NO_POINTS).each do |i|
      angle = (2.0 * Math::PI * i) / NO_POINTS
      points << [x + r * Math.cos(angle), y + r * Math.sin(angle)]
    end

    # Fill the dot.
    view.drawing_color = Sketchup::Color.new(0, 0, 255)
    view.draw(GL_POLYGON, points)
  end

  def draw(view)  
    return if @vertex.nil?
    highlight_point(view)
  end
end

def activate_tool
  Sketchup.active_model.select_tool(PointHighlighter.new)
end
    
activate_tool

What you should do instead is use draw2d.

For example in my move tool I want the arrows to stay the same size relative to the model, but the text and the box around it I want to be a continuous size in 2D. The view.screen_coords method is useful for converting a 3d point in the model to a 2d position on the screen.

      text_center = view.screen_coords(line_center)
        edge_points = [
        Geom::Point3d.new(text_center.x - text_width, text_center.y - 0),
        Geom::Point3d.new(text_center.x + text_width, text_center.y - 0),
        Geom::Point3d.new(text_center.x + text_width, text_center.y - 0),
        Geom::Point3d.new(text_center.x + text_width, text_center.y + 25),
        Geom::Point3d.new(text_center.x + text_width, text_center.y + 25),
        Geom::Point3d.new(text_center.x - text_width, text_center.y + 25),
        Geom::Point3d.new(text_center.x - text_width, text_center.y + 25),
        Geom::Point3d.new(text_center.x - text_width, text_center.y - 0)
      ]
      view.drawing_color = 'Black'
      view.line_width = 1
      view.draw2d(GL_LINES, edge_points)

Move Tool1

2 Likes

Thanks for your input, Neil! What you describe seems to be a generalization of what I did, right? Note that I’m not planning to use my extension in 3D, so I guess that querying the view.camera.height should be just fine. The only thing I really want is to have point markers that have the same size irrespective of how big/small my 2D polygons are.