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.
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 !
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.
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 ?