Clipping problema with preview wall plugin

I was starting to make a wall plugin, but I’m having a lot of trouble figuring out how to create a preview of the drawing.

Why is the preview disappearing depending on the zoom?
Is there a better way to do this?

I just want to click, choose height and thickness, and then draw the wall, seeing a preview of how it will look as I draw.

Note: Is it possible to lock the axes while drawing? And click on other points, like an imported dwg?

module ExemploWallTool
  extend self

  class WallTool
    def initialize(params)
      @params      = params
      @points      = []
      @mouse_point = nil
      @drawing     = false
    end

    def onLButtonDown(flags, x, y, view)
      ip = view.inputpoint(x, y)
      point_clicked = ip.position

      if !@drawing
        @points = [point_clicked]
        @drawing = true
      else
        @points << point_clicked
      end

      view.invalidate
    end

    def onLButtonDoubleClick(flags, x, y, view)
      if @drawing
        @drawing = false
        create_wall_geometry
        @points.clear
        view.invalidate
      end
    end

    def onMouseMove(flags, x, y, view)
      if @drawing
        ip = view.inputpoint(x, y)
        @mouse_point = ip.position
        view.invalidate
      end
    end

    # ----------------------------

    def draw(view)
      return unless @drawing
      return if @points.empty?

      wall_width = @params["Wall Width"].to_f

      view.drawing_color = "yellow"
      view.line_width = 1

      points_to_draw = @points.clone
      points_to_draw << @mouse_point if @mouse_point

      (points_to_draw.size - 1).times do |i|
        pt1 = points_to_draw[i]
        pt2 = points_to_draw[i + 1]

        vec = pt2 - pt1

        next if vec.length == 0

        vec.normalize!

        perp = Geom::Vector3d.new(-vec.y, vec.x, 0)

        next if perp.length == 0

        perp.length = wall_width / 2.0

        p1 = pt1.offset(perp)
        p2 = pt2.offset(perp)
        p3 = pt2.offset(perp.reverse)
        p4 = pt1.offset(perp.reverse)

        view.draw(GL_LINE_LOOP, [p1, p2, p3, p4])
      end
    end


    # ----------------------------

    def create_wall_geometry
      model = Sketchup.active_model
      ents  = model.active_entities

      return if @points.size < 2

      wall_width  = @params["Wall Width"].to_f
      wall_height = @params["Wall Height"].to_f

      model.start_operation("Criar Parede", true)
      begin
        wall_group = ents.add_group
        group_ents = wall_group.entities


        path_edges = group_ents.add_edges(@points)


        cross_pts = []
        half_w = wall_width / 2.0
        cross_pts << Geom::Point3d.new(-half_w, 0, 0)
        cross_pts << Geom::Point3d.new( half_w, 0, 0)
        cross_pts << Geom::Point3d.new( half_w, 0, wall_height)
        cross_pts << Geom::Point3d.new(-half_w, 0, wall_height)
        face = group_ents.add_face(cross_pts)
        face.reverse! if face.normal.y > 0


        first_segment = @points[1] - @points[0]
        first_segment.normalize!
        neg_y = Geom::Vector3d.new(0, -1, 0)
        angle = neg_y.angle_between(first_segment)
        axis = neg_y * first_segment
        if axis.length > 0
          axis.normalize!
          rot = Geom::Transformation.rotation(ORIGIN, axis, angle)
          group_ents.transform_entities(rot, face)
        end

        move_to_first = Geom::Transformation.translation(@points[0] - ORIGIN)
        group_ents.transform_entities(move_to_first, face)


        face.followme(path_edges)


        path_edges.each(&:erase!)

      rescue => e
        puts "Erro ao criar parede: #{e.message}"
      ensure
        model.commit_operation
      end
    end
  end

  # ----------------------------

  def ativar_ferramenta_parede
    prompts  = ["Wall Width", "Wall Height"]
    defaults = [15.0, 300.0]
    list     = ["", ""]
    input = UI.inputbox(prompts, defaults, list, "Wall Configurations")
    return unless input

    params = {
      "Wall Width"  => input[0],
      "Wall Height" => input[1]
    }
    Sketchup.active_model.select_tool(WallTool.new(params))
  end

  unless file_loaded?(__FILE__)
    menu = UI.menu("Plugins")
    menu.add_item("Wall Preview") {
      ativar_ferramenta_parede
    }
    file_loaded(__FILE__)
  end
end

In order to accurately draw things, SketchUp needs to know the extents of what it is drawing. If the tool is doing its own drawing, it may need to implement #getExtents method to tell SketchUp the extents of what it will be drawing. If you don’t implement this method, you may find that part of what the tool is drawing gets clipped to the extents of the rest of the model.

You can see example - including inference lock to axes - here:

Also, call view.invalidate from a resume and deactivate callback methods.

    def deactivate(view)
      @drawing = false
      view.invalidate
    end

    def resume(view)
      view.invalidate
    end

When the user uses the interrupter tools like Zoom, Orbit and Pan, the view is cleared. When your tool resumes, the resume() callback is called. So define it and call view.invalidate to trigger the draw() callback.

1 Like