### Line Tool
#
module Tools2D
class LineTool2D

def db(string)
  dir=File.dirname(__FILE__) ### adjust folder to suit tool
  toolname="2Dtools"  ### adjust xxx to suit tool...
  locale=Sketchup.get_locale.upcase
  path=File.join(dir, toolname+locale+".lingvo")
  unless File.exist?(path)
     return string
  else
    deBabelizer(string,path)
  end 
end#def

def getExtents
    bbox=Sketchup.active_model.bounds
    bbox.add(@ip.position)if @ip and @ip.valid?
    bbox.add(@ip1.position)if @ip1 and @ip1.valid?
    bbox.add(@ip2.position)if @ip2 and @ip2.valid?
    bbox.add(@ip3.position)if @ip3 and @ip3.valid?
    return bbox
end

@@cursor=nil
def initialize()
    @ip=nil
    @ip1=nil
    @ip2=nil
    @xdown=0
    @ydown=0
    @@cursor=nil
    curpath=File.join(File.dirname(__FILE__), "Icons", "2Dline_cursor.png")
    @@cursor=UI::create_cursor(curpath, 0, 31) if File.exist?(curpath)
end

# The activate method is called by SketchUp when the tool is first selected.
# it is a good place to put most of your initialization
def activate
    # The Sketchup::InputPoint class is used to get 3D points from screen
    # positions.  It uses the SketchUp inferencing code.
    # In this tool, we will have two points for the endpoints of the line.
    @ip=Sketchup::InputPoint.new
    @ip1=Sketchup::InputPoint.new
    @ip2=Sketchup::InputPoint.new
    @drawn=false
    @ctrl=nil ### clines
    @alt=nil  ### polylines
    @msg=db"[as Lines] Ctrl=Cline, Alt=Polyline"
    ### get/set reference plane 'z'
    @z=Sketchup.active_model.get_attribute("2Dtools","z",nil)
    Sketchup.active_model.set_attribute("2Dtools","z",0.0.mm)if not @z
    @z=Sketchup.active_model.get_attribute("2Dtools","z",nil)
    @zmsg=" [ Z="+@z.to_s+" <Tab> ] "
    @zreset=false
    # This sets the label for the VCB
    Sketchup::set_status_text((db("Length")),SB_VCB_LABEL)
    self.reset(nil)
end

# deactivate is called when the tool is deactivated because
# a different tool was selected
def deactivate(view)
    if not @ctrl and not @alt
        @lines.each{|e|e.erase! if e.valid?}
        0.upto(@points.length-2) do |i|
          view.model.active_entities.add_line(@points[i],@points[i+1])
        end#upto
    elsif @alt
        @lines.each{|e|e.erase! if e.valid?}
        view.model.active_entities.add_curve(@points)if @points[1]
    end#if
    Sketchup::set_status_text("",SB_PROMPT)
    Sketchup::set_status_text("",SB_VCB_LABEL)
    Sketchup::set_status_text("",SB_VCB_VALUE)
    view.invalidate if @drawn
end

def onSetCursor()
    #return nil if RUBY_PLATFORM =~ /darwin/
	UI::set_cursor(@@cursor)if @@cursor
end

# The onMouseMove method is called whenever the user moves the mouse.
# because it is called so often, it is important to try to make it efficient.
# In a lot of tools, your main interaction will occur in this method.
def onMouseMove(flags, x, y, view)
    if @zreset
      view.lock_inference
      @ip.pick view, x, y
      if @ip.valid?
        view.invalidate if @ip.display?
        @ip1.copy! @ip
        view.tooltip=(db("Z-height: "))+@ip1.tooltip
        Sketchup::set_status_text("Z=#{@z.to_l} ",SB_VCB_LABEL)
        Sketchup::set_status_text(@ip1.position.z.to_l.to_s, SB_VCB_VALUE)
        Sketchup::set_status_text((db("RESET Z-plane: Pick 3D-point, Type absolute Z-height or +NN/--NN=relative...")), SB_PROMPT)
      end#if
      return nil
    end#if
    if @state == 0
        # We are getting the first end of the line.  Call the pick method
        # on the InputPoint to get a 3D position from the 2D screen position
        # that is bassed as an argument to this method.
        @ip.pick view, x, y
        if @ip != @ip1
            # if the point has changed from the last one we got, then
            # see if we need to display the point.  We need to display it
            # if it has a display representation or if the previous point
            # was displayed.  The invalidate method on the view is used
            # to tell the view that something has changed so that you need
            # to refresh the view.
            view.invalidate if( @ip.display? or @ip1.display? )
            @ip1.copy! @ip
            # set the tooltip that should be displayed to this point
            view.tooltip=@ip1.tooltip
            Sketchup::set_status_text((db("Start-Point")),SB_VCB_LABEL)
            Sketchup::set_status_text((db("2D Line: "))+@zmsg+@msg+(db(" - Pick Start Point...")),SB_PROMPT)
            if @ip1.valid?
              px=@ip1.position.x.to_s
              py=@ip1.position.y.to_s
              pz=@z.to_s
              msg="["+px+","+py+","+pz+"]"
              Sketchup::set_status_text(msg,SB_VCB_VALUE)
            end#if
        end
    else
        # Getting the second end of the line
        # If you pass in another InputPoint on the pick method of InputPoint
        # it uses that second point to do additional inferencing such as
        # parallel to an axis.
        @ip2.pick view, x, y, @ip1
        view.tooltip=@ip2.tooltip if @ip2.valid?
        view.invalidate
        # Update the length displayed in the VCB
        if @ip2.valid?
            p1=@ip1.position;p1.z=@z
            p2=@ip2.position;p2.z=@z
            length=p1.distance(p2)
            Sketchup::set_status_text(length.to_s,SB_VCB_VALUE)
        end
        Sketchup::set_status_text((db("Length")),SB_VCB_LABEL)
        Sketchup::set_status_text((db("2D Line: "))+@zmsg+@msg+(db(" - Pick End Point [Double-click the Last-Point to End this Line-Set]")),SB_PROMPT)
        # Check to see if the mouse was moved far enough to create a line.
        # This is used so that you can create a line by either draggin
        # or doing click-move-click
        if (x-@xdown).abs > 10 || (y-@ydown).abs > 10
            @dragging=true
        end
    end
end

# The onLButtonDOwn method is called when the user presses the left mouse button.
def onLButtonDown(flags, x, y, view)
    # When the user clicks the first time, we switch to getting the
    # second point.  When they click a second time we create the line 
    # and make the second point the first point etc
    if @zreset
      view.lock_inference
      @ip.pick view, x, y
      if @ip.valid?
        Sketchup::set_status_text("",SB_VCB_LABEL)
        Sketchup::set_status_text("",SB_PROMPT)
        self.set_zplane(@ip.position.z)
      end#if
      @ip.clear
      return nil
    end#if
    if @state == 0
        @ip1.pick view, x, y
        if @ip1.valid?
            @state=1
            Sketchup::set_status_text((db("Length")),SB_VCB_LABEL)
            Sketchup::set_status_text((db("2D Line: "))+@zmsg+@msg+(db(" - Pick End Point [Double-click the Last-Point to End this Line-Set]")),SB_PROMPT)
            @xdown=x
            @ydown=y
            p1=@ip1.position;p1.z=@z
            @points<<p1
        end
    else
        # create the line on the second click
        if @ip2.valid?
            view.lock_inference
            p1=@ip1.position;p1.z=@z
            @ip1.copy!(@ip2)
            p2=@ip2.position;p2.z=@z
            self.create_geometry(p1,p2,view)
            @points<<p2
            @ip2.clear
            @state=1
        end
    end
    # Clear any inference lock
    view.lock_inference
end

# The onLButtonUp method is called when the user releases the left mouse button.
def onLButtonUp(flags, x, y, view)
    # If we are doing a drag, then create the line on the mouse up event
    view.lock_inference
    if @dragging && @ip2.valid?
        p1=@ip1.position;p1.z=@z
        @ip1.copy!(@ip2)
        p2=@ip2.position;p2.z=@z
        self.create_geometry(p1,p2,view)
        view.invalidate
        @points<<p2
        @ip2.clear
        @state=1
    end
end

# onKeyDown is called when the user presses a key on the keyboard.
# We are checking it here to see if the user pressed the shift key
# so that we can do inference locking
def onKeyDown(key, repeat, flags, view)
    if( key == CONSTRAIN_MODIFIER_KEY && repeat == 1 )
        @shift_down_time=Time.now
        # if we already have an inference lock, then unlock it
        if( view.inference_locked? )
            # calling lock_inference with no arguments actually unlocks
            view.lock_inference
        elsif( @state == 0 && @ip1.valid? )
            view.lock_inference @ip1
        elsif( @state == 1 && @ip2.valid? )
            view.lock_inference @ip2, @ip1
        end
    end
    if key==VK_CONTROL || key==VK_COMMAND  ### Ctrl toggles lines / clines
      if @ctrl
        @ctrl=false; @msg=db(" [as Lines] Ctrl=Cline, Alt=Polyline ")
      else
        @ctrl=true; @msg=db(" [as Clines], Ctrl=Line, Alt=Polyline ")
        @alt=false
      end#if
      self.reset(view)
    end#if
    if key==VK_ALT  ### Alt toggles lines / polylines
      if @alt
        @alt=false; @msg=db(" [as Lines] Ctrl=Cline, Alt=Polyline ")
      else
        @alt=true; @msg=db(" [as PolyLines], Ctrl=Cline, Alt=Line ")
        @ctrl=false
      end#if
      self.reset(view)
    end#if
    ### 
end

# onKeyUp is called when the user releases the key
# We use this to unlock the interence
# If the user holds down the shift key for more than 1/2 second, then we
# unlock the inference on the release.  Otherwise, the user presses shift
# once to lock and a second time to unlock.
def onKeyUp(key, repeat, flags, view)
    if( key == CONSTRAIN_MODIFIER_KEY &&
        view.inference_locked? &&
        (Time.now - @shift_down_time) > 0.5 )
        view.lock_inference
    end
    if key == 9 or key == 15 #or key == 48### TAB sets Z, 9 for PC 15 for MAC ?
      view.tooltip=(db("Z-height: "))+@ip1.tooltip
      Sketchup::set_status_text("Z=#{@z.to_l} ",SB_VCB_LABEL)
      Sketchup::set_status_text(@ip1.position.z.to_l.to_s, SB_VCB_VALUE)
      Sketchup::set_status_text((db("RESET Z-plane: Pick 3D-point, Type absolute Z-height or +NN/--NN=relative...")), SB_PROMPT)
      @zreset=true
      UI.beep
      view.invalidate
    end#if
end

def enableVCB?
   return true
end

# onUserText is called when the user enters something into the VCB
# In this implementation, we create a line of the entered length if
# the user types a length while selecting the second point
def onUserText(text, view)
    ### 
  if @zreset
    ### check for relative value + or --
    if text=~/^\+/
      text=text[1..-1]
      add=true
    elsif text=~/^--/
      text=text[2..-1]
      sub=true
    else
      add=false
      sub=false
    end#if
    ###
    begin
        value=text.to_l
    rescue
        # Error parsing the text
        UI.beep
        puts (db("Cannot convert "))+text+(db(" to a Z-height"))
        value=nil
        Sketchup::set_status_text("",SB_VCB_VALUE)
    end
    ###
    return nil if not value
    if add
      value=@z+value
    elsif sub
      value=@z-value
    end
    self.set_zplane(value)
    return nil
  end#if
    # We only accept input when the state is 1 (i.e. getting the second point)
    # This could be enhanced to also modify the last line created if a length
    # is entered after creating a line.
    return if not @state == 1
    return if not @ip2.valid?
    # The user may type in something that we can't parse as a length
    # so we set up some exception handling to trap that
    begin
        value=text.to_l
    rescue
        # Error parsing the text
        UI.beep
        puts (db("Cannot convert "))+text+(db(" to a Length"))
        value=nil
        Sketchup::set_status_text("",SB_VCB_VALUE)
    end
    return if !value
    # Compute the direction and the second point
    pt1=@ip1.position
    pt1.z=@z
    pt2=@ip2.position
    pt2.z=@z
    vec=pt2 - pt1
    if vec.length == 0.0
        UI.beep
        return
    end
    vec.length=value
    pt2=pt1 + vec
    @points<<pt2 ###
    self.create_geometry(pt1,pt2,view)# Create a line
    #self.reset(view)
    @state=1
    #@ip1.copy!(@ip2) ###
    @ip2.clear
    ###
    if not @ctrl and not @alt
      cline=view.model.active_entities.add_cline(pt1,pt2)###
      cline.stipple="_"
      @lines<<cline
    elsif @alt
      cline=view.model.active_entities.add_cline(pt1,pt2)###
      cline.stipple="."
      @lines<<cline
    elsif @ctrl
      cline=view.model.active_entities.add_cline(pt1,pt2)###
    end#if
    view.invalidate
end

def resume(view)
  view.invalidate if view
  if @zreset
    Sketchup::set_status_text("Z=#{@z.to_l} ",SB_VCB_LABEL)
    Sketchup::set_status_text(@ip1.position.z.to_l.to_s, SB_VCB_VALUE)
    Sketchup::set_status_text((db("RESET Z-plane: Pick 3D-point, Type absolute Z-height or +NN/--NN=relative...")), SB_PROMPT)
    return nil
  end#if
  if @state==0
    Sketchup::set_status_text((db("Start-Point")),SB_VCB_LABEL)
    if @ip1.valid?
        px=@ip1.position.x.to_s
        py=@ip1.position.y.to_s
        pz=@z.to_s
        msg="["+px+","+py+","+pz+"]"
        Sketchup::set_status_text(msg,SB_VCB_VALUE)
    end#if
    Sketchup::set_status_text((db("2D Line: "))+@zmsg+@msg+(db(" - Pick Start Point...")),SB_PROMPT)
  else
    Sketchup::set_status_text((db("Length")),SB_VCB_LABEL)
    Sketchup::set_status_text((db("2D Line: "))+@zmsg+@msg+(db(" - Pick End Point [Double-click the Last-Point to End this Line-Set]")),SB_PROMPT)
  end#if
end

# The draw method is called whenever the view is refreshed.  
# It lets the tool draw any temporary geometry that it needs to.
def draw(view)
    if @ip1.valid?
        if @ip1.display?
            @ip1.draw(view)
            @drawn=true
        end
        if @ip2.valid?
            @ip2.draw(view) if @ip2.display?
            # The set_color_from_line method determines what color
            # to use to draw a line based on its direction.
            # For example red, green or blue.
            view.set_color_from_line(@ip1, @ip2)if not @zreset
            view.line_stipple="_" if @ctrl
            p1=@ip1.position;p1.z=@z
            p2=@ip2.position;p2.z=@z
            self.draw_geometry(p1,p2,view)
            @drawn=true
        end
    end
end

# onCancel is called when the user hits the escape key
def onCancel(flag, view)
    @lines.each{|e|e.erase! if e.valid?}
    self.reset(view)
end

# user completes line-set or cancels
#def onReturn(view)### v3.2 removed as VCB clash

# double click end line-set
def onLButtonDoubleClick(flags, x, y, view)
    self.end_geometry(view)
end

# The following methods are not directly called from SketchUp.  They are
# internal methods that are used to support the other methods in this class.
###
def end_geometry(view)
  #view.model.start_operation("2D Line")
    if @points[1]### can make a line
      if not @ctrl and not @alt
        self.create_geometry(@points[-2],@points[-1],view)
        @lines.each{|e|e.erase! if e.valid?}
        0.upto(@points.length-2) do |i|
          view.model.active_entities.add_line(@points[i],@points[i+1])
        end#upto
      elsif @alt
        @lines.each{|e|e.erase! if e.valid?}
        view.model.active_entities.add_curve(@points)
      elsif @ctrl
        self.create_geometry(@points[-2],@points[-1],view)
        @lines.each{|e|e.erase! if e.valid? and e.class==Sketchup::ConstructionPoint}
      end#if
      view.invalidate
      self.reset(view)
    else ### cancelled
      view.invalidate
      Sketchup::set_status_text("",SB_PROMPT)
      Sketchup.send_action("selectSelectionTool:")
    end#if
  #view.model.commit_operation
end#def

# Reset the tool back to its initial state
def reset(view)
    # This variable keeps track of which point we are currently getting
    @state=0
    # clear the InputPoints
    @ip.clear
    @ip1.clear
    @ip2.clear
    if view
        view.tooltip=nil
        view.lock_inference
        view.invalidate if @drawn
    end
    @drawn=false
    @dragging=false
    @lines=[]
    @points=[]
    # Display a prompt on the status bar
    Sketchup::set_status_text((db("2D Line: "))+@zmsg+@msg+(db(" - Pick Start Point...")),SB_PROMPT)
    Sketchup::set_status_text((db("Start-Point")),SB_VCB_LABEL)
    Sketchup::set_status_text("",SB_VCB_VALUE)
end

# Create new geometry when the user has selected two points.
def create_geometry(p1, p2, view)
  @points.each{|p|
    c=view.model.active_entities.add_cpoint(p)
    @lines<<c
  }
  c=view.model.active_entities.add_cpoint(p1)
  @lines<<c
  c=view.model.active_entities.add_cpoint(p2)
  @lines<<c
  if not @ctrl and not @alt
    if @points[0]
      cline=view.model.active_entities.add_cline(@points[-1],p2)### not p1,p2
      cline.stipple="_"
      @lines<<cline
    end#if
  elsif @ctrl
    if @points[0]
      cline=view.model.active_entities.add_cline(@points[-1],p2)### not p1,p2
    end#if
  elsif @alt
    if @points[0]
      cline=view.model.active_entities.add_cline(@points[-1],p2)### not p1,p2
      cline.stipple="."
      @lines<<cline
    end#if
  end#if
  ### clines give something to snap to...
end

# Draw the geometry
def draw_geometry(pt1, pt2, view)
    ###view.draw_line(pt1,pt2)
  if @zreset
      view.line_width=2
      view.draw_polyline(@points)if @points[1]
      view.guess_target
      view.line_width=4
      view.draw_points([@ip.position],10,4,"orangered")
  else
    view.draw_line(@points.last,pt2)if @points[0]###
    if not @ctrl and not @alt
      view.drawing_color="black"
      view.line_width=2
      view.draw_polyline(@points)if @points[1]
    elsif @alt
      view.drawing_color="orange"
      view.line_width=2
      view.draw_polyline(@points)if @points[1]
    end#if
  end#if
end

def set_zplane(z)
    puts "Reset Z="+z.to_l.to_s
    Sketchup.active_model.set_attribute("2Dtools","z",z)
    @z=Sketchup.active_model.get_attribute("2Dtools","z",0.0.mm)
    @zmsg=" [ Z="+@z.to_s+" <Tab> ] "
    @zreset=false
    self.resume(nil)
    return nil
end

end # class

end#mod
#-----------------------------------------------------------------------------
# shortcut
#def linetool2D
    #Sketchup.active_model.select_tool LineTool2D.new
#end
######-----------------------------------------------

