PickHelper.window_pick bug inside a group/component

Hi, I am trying to write a selection tool and when using the window_pick method, there was a problem.
The PickHelper.window_pick method works well outside the model but when I use it inside an object it works wrong. I’m using it the wrong way?
Check out the code below in the onLButtonUp method.
Thanks

class SelectTool
  def initialize(*_args)
    @model = Sketchup.active_model
    @selection = Sketchup.active_model.selection
    @ip_mouse = Sketchup::InputPoint.new
  end

  def activate
    reset
  end

  def deactivate(view)
    view.invalidate
  end

  def reset; end

  def onCancel(reason, _view)
    if reason == 0
      @model.close_active if @model.active_path
    end
  end

  def onMouseMove(_flags, x, y, view)
    @mouse = [x, y]
    onSetCursor
    set_statusbar
    view.invalidate
  end

  def onLButtonDown(_flags, x, y, _view)
    @click_times = 1
    @drag = true
    @mouse_down = [x, y]
  end

  def dragging?
    @drag && @mouse_down && @mouse.distance(@mouse_down) > 10
  end

  def onLButtonUp(_flags, x, y, view)
    ph = view.pick_helper
    ph.do_pick(x, y)
    @best_entity = ph.best_picked

    if dragging?
      pick_type = if @mouse.x > @mouse_down.x
                    Sketchup::PickHelper::PICK_INSIDE
                  else
                    Sketchup::PickHelper::PICK_CROSSING
                  end

      num_picked = ph.window_pick(@mouse_down, @mouse, pick_type)
      if num_picked > 0
        @selection.clear
        @selection.add(ph.all_picked)
      end
    else
      @selection.clear
      @selection.add(@best_entity) if @best_entity
    end

    @drag = false
    @mouse_down = nil

    view.invalidate
  end

  def onLButtonDoubleClick(_flags, x, y, view)
    @click_times += 1
    p "onLButtonDoubleClick: @click_times = #{@click_times}"

    ph = view.pick_helper
    ph.do_pick(x, y)
    @best_entity = ph.best_picked

    if object?(@best_entity)
      active_path = @model.active_path || []
      unless @best_entity.locked?
        @model.active_path = active_path + [@best_entity]
      end
    elsif @best_entity
      if geometry?(@best_entity)
        # select connected
        if @best_entity.is_a?(Sketchup::Edge)
          @selection.add(@best_entity.faces)
        else
          # Face
          @selection.add(@best_entity.edges)
        end

        @selection.add(@best_entity.all_connected) if @click_times > 2
      else
        # API cant edit Dimension and Text
        # Sketchup.send_action('selectSelectionTool:')
      end
    elsif @model.active_path
      @model.close_active 
    end
  end

  def object?(entity)
    return unless entity

    entity.is_a?(Sketchup::Group) ||
      entity.is_a?(Sketchup::ComponentInstance)
  end 
  
  def geometry?(entity)
    return unless entity

    entity.is_a?(Sketchup::Edge) ||
      entity.is_a?(Sketchup::Face)
  end

  def onSetCursor
    # UI.set_cursor(@cursor)
  end

  def set_statusbar
    status_text = 'Select objects. Shift to extend select. Drag mouse to select multiple.'
    Sketchup.set_status_text(status_text)
  end

  def draw(view)
    if dragging?
      rect = [
        @mouse_down,
        [@mouse_down.x, @mouse.y],
        @mouse,
        [@mouse.x, @mouse_down.y]
      ]

      inside = @mouse.x > @mouse_down.x

      view.line_width = 1
      view.line_stipple = inside ? '' : '_'
      view.drawing_color = 'black'
      view.draw2d(GL_LINE_LOOP, rect)
    end
  end
end

Sketchup.active_model.select_tool(SelectTool.new)

Yes I see it acts weird. Does ThomThom’s PickHelper Guide help ?

1 Like

The window_pick method has been added since 2016’s SketchUp, Thom Thom’s document has not added this method yet.
The boundingbox_pick method has the same problem.

Okay I got it picking from the active entities …

… but it can’t tell the difference from a crossing and an inside pick.

EDIT: Actually that was my error. I had messed with the if statement just after the if dragging? statement and I’ve since fixed that.

BUT: The problems now seem to be that the window pick will ONLY return one pick path for any primitive entities even though more than 1 are window selected and OFTEN only during a crossing select. (Ie, occasionally an inside window select will also grab a random primitive edge or face.) It doesn’t matter how many groups or component instances are also selected (along with the primitives,) the window pick only returns a single pick path for one of the primitives (seemly chosen at random) in the selection along with the pick paths for groups and/or component instances.

Also … we don’t need to do ph.do_pick(x,y) with a window pick pattern so I moved that down into the else (not dragging) clause.

class SelectTool
  def initialize(*_args)
    @model = Sketchup.active_model
    @selection = Sketchup.active_model.selection
    @ip_mouse = Sketchup::InputPoint.new
  end

  def activate
    reset
  end

  def deactivate(view)
    view.invalidate
  end

  def reset
    @drag = false
  end

  def onCancel(reason, _view)
    if reason == 0
      if @model.active_path
        @model.close_active
      else
        reset()
      end
    end
  end

  def onMouseMove(_flags, x, y, view)
    @mouse = [x, y]
    onSetCursor
    set_statusbar
    view.invalidate
  end

  def onLButtonDown(_flags, x, y, _view)
    @click_times = 1
    @drag = true
    @mouse_down = [x, y, 0]
  end

  def dragging?
    @drag && @mouse_down && @mouse.distance(@mouse_down) > 10
  end

  def onLButtonUp(_flags, x, y, view)
    ph = view.pick_helper

    if dragging?
      if x > @mouse_down.x
        pick_type = Sketchup::PickHelper::PICK_INSIDE
      else
        pick_type = Sketchup::PickHelper::PICK_CROSSING
      end

      #num_picked = ph.window_pick(@mouse_down, @mouse, pick_type)
      num_picked = ph.window_pick(@mouse_down, [x,y,0], pick_type)
      if num_picked > 0
        puts "Number picked: #{num_picked}"
        picked = []
        context = @model.active_entities
        ph.count.times { |pick_path_index|
          path = ph.path_at(pick_path_index)
          found = path.find { |ent| context == ent.parent.entities }
          picked << found if found
        }
        picked.uniq!
        puts "Picked: "<<picked.inspect
        @selection.clear
        @selection.add(picked)
      else
        UI.beep
      end

      @drag = false

    else
      # Moved these 2 statements inside the else:
      ph.do_pick(x, y)
      @best_entity = ph.best_picked
      #
      @selection.clear
      @selection.add(@best_entity) if @best_entity
    end

    @mouse_down = nil

    view.invalidate
  end

  def onLButtonDoubleClick(_flags, x, y, view)
    @click_times += 1
    p "onLButtonDoubleClick: @click_times = #{@click_times}"

    ph = view.pick_helper
    ph.do_pick(x, y)
    @best_entity = ph.best_picked

    if object?(@best_entity)
      active_path = @model.active_path || []
      unless @best_entity.locked?
        @model.active_path = active_path + [@best_entity]
      end
    elsif @best_entity
      if geometry?(@best_entity)
        # select connected
        if @best_entity.is_a?(Sketchup::Edge)
          @selection.add(@best_entity.faces)
        else
          # Face
          @selection.add(@best_entity.edges)
        end

        @selection.add(@best_entity.all_connected) if @click_times > 2
      else
        # API cant edit Dimension and Text
        # Sketchup.send_action('selectSelectionTool:')
      end
    elsif @model.active_path
      @model.close_active 
    end
  end

  def object?(entity)
    return unless entity

    entity.is_a?(Sketchup::Group) ||
      entity.is_a?(Sketchup::ComponentInstance)
  end 
  
  def geometry?(entity)
    return unless entity

    entity.is_a?(Sketchup::Edge) ||
      entity.is_a?(Sketchup::Face)
  end

  def onSetCursor
    # UI.set_cursor(@cursor)
  end

  def set_statusbar
    status_text = 'Select objects. Shift to extend select. Drag mouse to select multiple.'
    Sketchup.set_status_text(status_text)
  end

  def draw(view)
    if dragging?
      rect = [
        @mouse_down,
        [@mouse_down.x, @mouse.y],
        @mouse,
        [@mouse.x, @mouse_down.y]
      ]

      inside = @mouse.x > @mouse_down.x

      view.line_width = 1
      view.line_stipple = inside ? '' : '_'
      view.drawing_color = 'black'
      view.draw2d(GL_LINE_LOOP, rect)
    end
  end
end
1 Like
1 Like