Text input and Inferencing (Draw Tool)

Many of my draw tools for the foundation and truss plugin are very similar. The user picks a number of points which then form the outline of the roof, slab or foundation outline.

Overall my draw tools seem to work as expected. When the user aligns the next segment with the X or Y axis the preview line turns red or green and allows the user to verify this segment is properly aligned (orthogonal).

The problem seems to arise when the user enters (manual text input) a length for the segment (which I allow). The resulting segment length is correctly draw as expected and in the correct direction however when the user goes to draw the next segment the inferencing to the X or Y axis is completely absent.

However, if the user then clicks the next point the following segment is then able to inference the axis again. For some reason text input messes up the inferencing to the X and Y axis. This is probably something simple to fix in my code but I’m at a loss as to how or where or why it is even doing this.

If you need my code to figure out what I’m doing wrong I would prefer to send it via a PM since it is a fairly significant draw tool and I would rather not post it publicly.

Without seeing any of your code…

Your class’s loop needs to set the values of the picked end point at each click - let’s say it’s called @pt1
When the user types in a length you can calculate that new end point [actually you must already do that to draw it !], so substitute the x/y/z as the new point [@pt1 ?], then the next picked or typed point should be relative to that ?

For some reference here is what I currently have for the onUserText method:

def onUserText(text, view)
    # 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
        value = nil
        Sketchup::set_status_text "", SB_VCB_VALUE
    end
    return if !value
    
    case @state
    when STATE_PICK_NEXT
        # update the section length
        vec = @currentpoint - @pts[@lastpointcount]
        if( vec.length > 0.0 )
            vec.length = value
            @pts[@pointcount] = @pts[@lastpointcount].offset(vec)
            self.update_state
        end
    end
end

In your code there will be a reference to the picked point [my @pt1] which you are adding to your @pts as you go.
But if the user types in a length the @pts[@lastpointcount].offset(vec) use to add into @pts is ‘lost’, so use something like…
@pt1=@pts[@lastpointcount].offset(vec)
or even
@pt1=@pts[-1]

Of course you’ll need to adjust my @pt1 to suit your own references !

Note that this method (onUserText) calls the update_state method:

def update_state
  case @state
    when STATE_PICK
        @ip1.copy! @ip
	    @lastpointcount = @pointcount
	    @pointcount = @pointcount + 1
        Sketchup::set_status_text "[#{@tool_name}] Click second point of Polyline"
	    Sketchup::set_status_text "Section Length", SB_VCB_LABEL
        Sketchup::set_status_text "", SB_VCB_VALUE
        @state = STATE_PICK_NEXT
	    # puts "#{@state} #{@pointcount}"
	   @status_text = "[#{@tool_name}] Click second point of Polyline"
    when STATE_PICK_NEXT
        @ip1.copy! @ip
	    @lastpointcount = @pointcount
	    @pointcount = @pointcount + 1
        @nextpointcount = @pointcount + 1
        Sketchup::set_status_text "[#{@tool_name}] Click corner #{@nextpointcount} of Polyline"
	    Sketchup::set_status_text "Section Length", SB_VCB_LABEL
        Sketchup::set_status_text "", SB_VCB_VALUE
	    # puts "#{@state} #{@pointcount}"
	    @status_text = "[#{@tool_name}] Click corner #{@nextpointcount} of Polyline"
    when STATE_PICK_FINAL
	    @ip1.copy! @ip
 	    self.calculate_obj
        # Sketchup.active_model.select_tool(nil)
	    self.reset
	
    end
end

Nothing to out of the ordinary here, in fact most of this code is from the original template I got from a SketchUp example a few years back.

The only other method that calls the update_state method is this one:

def onLButtonDown(flags, x, y, view)
    self.set_current_point(x, y, view)
	case @state
	when STATE_PICK_NEXT
		@pts[@pointcount] = @ip.position
    	end
	self.update_state
end

So something is missing from the algorithm when the user chooses to manually enter the length using the onUserText method.

When the user click the left mouse button the point is added to the array with this statement:
@pts[@pointcount] = @ip.position

When the user keys in a length the new point is added to the array with this statement:
@pts[@pointcount] = @pts[@lastpointcount].offset(vec)

The onLButtonDown method does call the set_current_point method which is not called by the onUserText method:

def set_current_point(x, y, view)
    if (!@ip.pick(view, x, y, @ip1))
        return false
    end
    need_draw = true
    
    # Set the tooltip that will be displayed
    view.tooltip = @ip.tooltip
        
    # Compute points
    case @state
    when STATE_PICK
        @pts[0] = @ip.position
       	# need_draw = @ip.display? || @drawn
    when STATE_PICK_NEXT
	if @Orthomode
		vec1 = @ip.position - @pts[@lastpointcount]
		angle = atan2(vec1.x, vec1.y)
		
		if (angle < 22.5.degrees) && (angle >= -22.5.degrees)
			vec = Geom::Vector3d.new(1,0,0)
		elsif (angle < 67.5.degrees) && (angle >= 22.5.degrees)
			vec = Geom::Vector3d.new(-1,1,0)
		elsif (angle < 112.5.degrees) && (angle >= 67.5.degrees)
			vec = Geom::Vector3d.new(0,1,0)
		elsif (angle < 157.5.degrees) && (angle >= 112.5.degrees)
			vec = Geom::Vector3d.new(1,1,0)
		elsif (angle < -157.5.degrees) && (angle >= 157.5.degrees)
			vec = Geom::Vector3d.new(-1,0,0)
		elsif (angle < -112.5.degrees) && (angle >= -157.5.degrees)
			vec = Geom::Vector3d.new(1,-1,0)
		elsif (angle < -67.5.degrees) && (angle >= -112.5.degrees)
			vec = Geom::Vector3d.new(0,-1,0)
		elsif (angle < -22.5.degrees) && (angle >= -67.5.degrees)
			vec = Geom::Vector3d.new(-1,-1,0)
		else
			vec = Geom::Vector3d.new(1,0,0)
		end

		@currentpoint = @ip.position.project_to_plane(@ip1.position, vec)
	else
        	@currentpoint = @ip.position
	end

	#  @currentpoint = @ip.position
	@sectionlength = @pts[@lastpointcount].distance @currentpoint
	Sketchup::set_status_text @sectionlength.to_s, SB_VCB_VALUE
    when STATE_PICK_FINAL
	# @pts[@pointcount] = @ip.position
	# @sectionlength = @pts[@lastpointcount].distance @pts[@pointcount]
	# Sketchup::set_status_text @sectionlength.to_s, SB_VCB_VALUE
    end   

    view.invalidate if need_draw
end

I know the code is a little ugly in this method, I’ve commented out a bunch of stuff and left it hanging around for now, I need to clean some of it out since I’m no longer using it.

So you need to ‘interface’ the user typed length >>> point to the last picked point that is remembered otherwise…
So not in update_state which doesn’t accept the constant from @state anyway…
but in the onUserText state use something like…
@ip=Sketchup::InputPoint.new(@pts[-1])
@ip1.copy!(@ip)
Then everything is reset as you want ?

1 Like

That seems to have done the trick, I left the update_state method as it is and just added this one line into the onUserText method:

@ip=Sketchup::InputPoint.new(@pts[-1])

I will keep testing it further but it seems like the problem is resolved.