Measure units of 3dPoint


#1

Hi everyone!
i try to devep plugin and have some problem with it.

The plug-in implements the following functional:

  • fixing the position of points and normal vectors on model surfaces;
  • set sphere(circuit);
  • calculation of the point of intersection of the vector of the normal and the sphere;
  • visualization of the trajectory from the point on the sphere to the point on the surface of the model;

The problem in the calculated value of the intersection point:

radius and center point refers to sphere’ parameters.

sphere pos (same as point2) - intesection point.

Z value of point bigger then sphere (at picture). I think this may depend on the units of measurement but I don’t know how to check it.

point_tool.rb

require 'sketchup.rb'

module Examples
  module CustomTool
    class PointTool

      def activate
        puts('Activate vector')
        @mouse_ip = Sketchup::InputPoint.new
        update_ui
      end

      def deactivate(view)
        puts('Deactivate vector')
        view.invalidate
      end

      def resume(view)
        update_ui
        view.invalidate
      end

      def onCancel(_reason, view)
        puts('Canceled vector')
        view.invalidate
      end

      # If you are on a face, then the degrees_of_freedom will be 2 meaning that you can only move on the plane of the
      # face.
      # If you are on an Edge or an axis, then the degrees_of_freedom will be 1 meaning that you can only move in the
      # direction of the edge or axis.
      # If you get an end point of an Edge, or an intersection point, then the degrees_of_freedom will be 0.

      def onMouseMove(_flags, x, y, view)
        @mouse_ip.pick(view, x, y, @mouse_ip)
        dof = @mouse_ip.degrees_of_freedom
        view.tooltip = @mouse_ip.tooltip if @mouse_ip.valid? && dof < 3
        view.invalidate
      end

      def onLButtonDown(_flags, _x, _y, view)
        puts('OnLButtonDown vector')
        dof = @mouse_ip.degrees_of_freedom
        get_vector_normal if @mouse_ip.valid? && dof < 3
        update_ui
        view.invalidate
      end

      # Here we have hard coded a special ID for the pencil cursor in SketchUp.
      # Normally you would use `UI.create_cursor(cursor_path, 0, 0)` instead
      # with your own custom cursor bitmap:
      #
      #   CURSOR_PENCIL = UI.create_cursor(cursor_path, 0, 0)
      CURSOR_POINT = 632
      def onSetCursor
        UI.set_cursor(CURSOR_POINT)
      end

      def draw(view)
        draw_preview(view)
        @mouse_ip.draw(view) if @mouse_ip.display?
      end

      private

      def update_ui
        Sketchup.status_text = 'Select point on plane.'
      end

      def draw_preview(view)
        view.draw(GL_POINTS, @mouse_ip)
      end

      def calc_intersection_point(pos, normal)
        r = SafeSphere.radius
        return nil if r == 0
        xc, yc, zc = SafeSphere.center_point.to_a
        a, b, g = normal.to_a
        x, y, z = pos

        puts "===========calculation============"
        puts "radius = #{r}"
        puts "center point = #{xc} #{yc} #{zc}"
        puts "line vector = #{normal.to_a}"
        puts "point = #{pos}"
        puts "=================================="
        root = root(a,b,g,xc,yc,zc,x,y,z,r)
        coef = coefficient(a, b, g)


        t = coef*(-a*x + a*xc - b*y + b*yc + g*z + 0.5 * root - g*zc)
        puts "t = #{t}"

        if t < 0
          t = coef*(-a*x + a*xc - b*y + b*yc + g*z - 0.5 * root - g*zc)
          puts "t = #{t}"
        end

        xs = x + a*t
        ys = y + b*t
        zs = z + g*t

        puts "xs = #{xs} ys = #{ys} zs = #{zs}"
        Geom::Point3d.new(xs, ys, zs)

      end

      def root(a,b,g,xc,yc,zc,x,y,z,r)
        (4*(a*(x - xc) + b*(y - yc) + g*((z - zc))**2) -
            4*((a*2) + (b**2)+ (g**2))*(-(r**2) + ((x - xc)**2) + ((y - yc)**2) + ((z - zc)**2)))**0.5
      end

      def coefficient(a, b, g)
        (1/((a**2) + (b**2) + (g**2)))
      end

      def visualize(point1, point2)
        puts 'visualize'
        puts "=====visualize_begin====="
        puts "point1 = #{point1}"
        puts "point2 = #{point2}"
        puts "========================="
        model = Sketchup.active_model
        model.start_operation('Edge', true)
        edge = model.active_entities.add_line(point1, point2)
        model.commit_operation
        puts "=====visualize_end====="
        puts "point1 = #{point1}"
        puts "point2 = #{point2}"
        puts "======================="
      end


      def get_vector_normal
        puts('Get vector normal')
        face = @mouse_ip.face
        normal = face.normal
        # vector3d.axes
        # The axes method is used to compute an arbitrary set of axes with the given vector as the z-axis direction.
        # Returns an Array of three vectors [xaxis, yaxis, zaxis].
        #
        # The position method is used to get the 3D point from the input point.
        # The values are specified as [x,y,z].
        axes = normal.axes
        pos = @mouse_ip.position
        puts "pose on surface = #{pos.to_a}"
        sphere_pos = calc_intersection_point(pos.to_a, normal)
        return 0 if sphere_pos.nil?
        puts "sphere pos = #{sphere_pos.to_a}"
        visualize(pos, sphere_pos)
        puts("#{pos[0]},#{pos[1]},#{pos[2]},#{normal[0]},#{normal[1]},#{normal[2]}")
        puts("#{sphere_pos[0]},#{sphere_pos[1]},#{sphere_pos[2]},#{normal[0]},#{normal[1]},#{normal[2]}")
        file = File.open(ENV['HOME'] + '/Desktop/points.txt', 'a')
        file.puts("Vector\n#{pos[0]},#{pos[1]},#{pos[2]},#{normal[0]},#{normal[1]},#{normal[2]}")
        file.puts("#{sphere_pos[0].to_mm}, #{sphere_pos[1].to_mm}, #{sphere_pos[2].to_mm}, #{normal[0].to_mm}, #{normal[1].to_mm},#{normal[2].to_mm}")
        file.close
      end

      def draw_safe_circle
        puts('draw_safe_circle')
      end


    end # class PointTool
  end
end

safe_sphere.rb

require 'sketchup.rb'

module Examples
  module CustomTool
    class SafeSphere
      @@radius = 0

      def self.radius
        @@radius
      end

      def self.center_point
        @@center_point
      end

      def activate

        @mouse_ip = Sketchup::InputPoint.new
        @picked_first_ip = Sketchup::InputPoint.new
        update_ui
      end

      def deactivate(view)
        view.invalidate
      end

      def resume(view)
        update_ui
        view.invalidate
      end

      def onCancel(_reason, view)
        reset_tool
        view.invalidate
      end

      def onMouseMove(_flags, x, y, view)

        if picked_first_point?
          puts 'picked_first_point? valid'
          @mouse_ip.pick(view, x, y, @picked_first_ip)
          puts @mouse_ip
        else
          @mouse_ip.pick(view, x, y)
        end
        view.tooltip = @mouse_ip.tooltip if @mouse_ip.valid?
        view.invalidate
      end

      def onLButtonDown(_flags, _x, _y, view)
        puts 'onLButtonDown'
        num_new_faces = 0
        if picked_first_point?
          puts("num_new_faces = create_edge")
          num_new_faces = create_edge
        end
        if num_new_faces > 0
          puts("reset_tool")
        else
          puts("@picked_first_ip.copy!(@mouse_ip)")
          @picked_first_ip.copy!(@mouse_ip)
        end
        update_ui
        view.invalidate
      end

      # Here we have hard coded a special ID for the pencil cursor in SketchUp.
      # Normally you would use `UI.create_cursor(cursor_path, 0, 0)` instead
      # with your own custom cursor bitmap:
      #
      #   CURSOR_PENCIL = UI.create_cursor(cursor_path, 0, 0)
      CURSOR_PENCIL = 632
      def onSetCursor
        # Note that `onSetCursor` is called frequently so you should not do much
        # work here. At most you switch between different cursors representing
        # the state of the tool.
        UI.set_cursor(CURSOR_PENCIL)
      end

      # The `draw` method is called every time SketchUp updates the viewport.
      # You should take care to do as little work in this method as possible.
      # If you need to calculate things to draw it is best to cache the data in
      # order to get better frame rates.
      def draw(view)
        draw_preview(view)
        @mouse_ip.draw(view) if @mouse_ip.display?
      end

      # When you use `view.draw` and draw things outside the boundingbox of
      # the existing model geometry you will see that things get clipped.
      # In order to make sure everything you draw is visible you must return
      # a boundingbox here which defines the 3d model space you draw to.
      # def getExtents
      #   bb = Geom::BoundingBox.new
      #   bb.add(picked_points)
      #   bb
      # end

      # In this example we put all the logic in the tool class itself. For more
      # complex tools you probably want to move that logic into its own class
      # in order to reduce complexity. If you are familiar with the MVC pattern
      # then consider a tool class a controller - you want to keep it short and
      # simple.

      private

      def update_ui
        Sketchup.status_text = if picked_first_point?
                                 'Select radius.'
                               else
                                 'Select сenter point.'
                               end
      end

      def reset_tool
        @picked_first_ip.clear
        update_ui
      end

      def picked_first_point?
        @picked_first_ip.valid?

      end

      def picked_points
        points = []
        points << @picked_first_ip.position if picked_first_point?
        points << @mouse_ip.position if @mouse_ip.valid?
        points
      end

      def draw_preview(view)
        points = picked_points
        return unless points.size == 2
        view.set_color_from_line(*points)
        view.line_width = 1
        view.line_stipple = ''
        view.draw(GL_LINE_LOOP, points)
      end

      # start_operation(op_name, disable_ui = false, next_transparent = false, transparent = false) ⇒ Boolean

      def create_edge
        model = Sketchup.active_model
        model.start_operation('Edge', true)
        @@center_point = picked_points[0]
        normal = @@center_point.vector_to(picked_points[1])
        normal.length = 1
        @@radius = @@center_point.vector_to(picked_points[1]).length
        puts("Center: #{@@center_point}\nRadius: #{@@radius}")
        edge = model.active_entities.add_circle(@@center_point, normal, @@radius, 100)
        file = File.open(ENV['HOME'] + '/Desktop/points.txt', 'w')
        file.puts("Sphere\n#{@@center_point[0].to_mm},#{@@center_point[1].to_mm},#{@@center_point[2].to_mm}\n#{@@radius.to_mm}\n")
        file.close
        model.commit_operation
        1
      end
    end # class SafeSphere
  end
end

#2

SketchUp uses inches internally. String interpolation can call Length#to_s which can convert to model units.
So you are getting a mixture of mm and inches in your output.

See this please …


#3

Please one more question, method normal from Class: Sketchup::Face returns a normalized 3d vector isn’t it?


#4

I believe it is, … if the length() of the Geom::Vector3d object is 1.0 then it is normalized.

But you can always use the Geom::Vector3d#normalize method of a vector object to return a new normalized vector.


#5

Is this vector an axis-angle or another transformation is needed for that?


#6

I don’t know what that means. The normal is a Vector3d oriented perpendicular to the Face. Like any Vector3d is has x,y,and z component values (not to be confused with SketchUp’s Component Entities!). In the figure below I drew a 10x10 square, rotated it up by some angle, and then constructed an Edge from one corner to a point offset from it by the normal vector of the Face.


#7

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.