1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
""" A point-to-point drawn polygon. """
# Enthought library imports.
from enable.primitives.api import Polygon
from traits.api import Int, Instance
from drawing_tool import DrawingTool
class PointPolygon(DrawingTool):
""" A point-to-point drawn polygon. """
# The actual polygon primitive we are editing.
polygon = Instance(Polygon, args=())
# The pixel distance from a vertex that is considered 'on' the vertex.
proximity_distance = Int(4)
# Override the default value of this inherited trait
draw_mode = "overlay"
# The index of the vertex being dragged, if any.
_dragged = Int
def reset(self):
self.polygon.model.reset()
self.event_state = "normal"
return
#------------------------------------------------------------------------
# "complete" state
#------------------------------------------------------------------------
def complete_draw(self, gc):
""" Draw a closed polygon. """
self.polygon.border_dash = None
self.polygon._draw_closed(gc)
return
def complete_left_down(self, event):
""" Handle the left mouse button coming up in the 'complete' state. """
# Ignore the click if it contains modifiers we do not handle.
polygon = self.polygon
if event.shift_down or event.alt_down:
event.handled = False
else:
# If we are over a point, we will either move it or remove it.
over = self._over_point(event, polygon.model.points)
if over is not None:
# Control down means remove it.
if event.control_down:
del polygon.model.points[over]
# Otherwise, prepare to drag it.
else:
self._dragged = over
event.window.set_pointer('right arrow')
self.event_state = 'drag_point'
self.request_redraw()
return
def complete_mouse_move(self, event):
""" Handle the mouse moving in the 'complete' state. """
# If we are over a point, then we have to prepare to move it.
over = self._over_point(event, self.polygon.model.points)
if over is not None:
if event.control_down:
event.window.set_pointer('bullseye')
else:
event.window.set_pointer('right arrow')
else:
event.handled = False
event.window.set_pointer('arrow')
self.request_redraw()
return
#------------------------------------------------------------------------
# "drag_point" state
#------------------------------------------------------------------------
def drag_point_draw(self, gc):
""" Draw the polygon in the 'drag_point' state. """
self.complete_draw(gc)
return
def drag_point_left_up(self, event):
""" Handle the left mouse coming up in the 'drag_point' state. """
self.event_state = 'complete'
self.request_redraw()
return
def drag_point_mouse_move(self, event):
""" Handle the mouse moving in the 'drag_point' state. """
# Only worry about the event if it's inside our bounds.
polygon = self.polygon
dragged_point = polygon.model.points[self._dragged]
# If the point has actually moved, update it.
if dragged_point != (event.x, event.y):
polygon.model.points[self._dragged] = \
(event.x + self.x, event.y - self.y)
self.request_redraw()
return
#------------------------------------------------------------------------
# "incomplete" state
#------------------------------------------------------------------------
def incomplete_draw(self, gc):
""" Draw the polygon in the 'incomplete' state. """
self.polygon.border_dash = (4.0, 2.0)
self.polygon._draw_open(gc)
return
def incomplete_left_dclick(self, event):
""" Handle a left double-click in the incomplete state. """
# Remove the point that was placed by the first mouse up, since
# another one will be placed on the up stroke of the double click.
del self.polygon.model.points[-1]
event.window.set_pointer('right arrow')
self.event_state = 'complete'
self.complete = True
self.request_redraw()
return
def incomplete_left_up(self, event):
""" Handle the left mouse button coming up in incomplete state. """
# If the click was over the start vertex, we are done.
if self._is_over_start( event ):
del self.polygon.model.points[-1]
self.event_state = 'complete'
event.window.set_pointer('right arrow')
self.complete = True
# Otherwise, add the point and move on.
else:
self.polygon.model.points.append((event.x + self.x, event.y - self.y))
self.request_redraw()
return
def incomplete_mouse_move(self, event):
""" Handle the mouse moving in incomplete state. """
# If we move over the initial point, then we change the cursor.
if self._is_over_start( event ):
event.window.set_pointer('bullseye')
else:
event.window.set_pointer('pencil')
# If the point has actually changed, then we need to update our model.
if self.polygon.model.points != (event.x + self.x, event.y - self.y):
self.polygon.model.points[-1] = (event.x + self.x, event.y - self.y)
self.request_redraw()
return
#------------------------------------------------------------------------
# "normal" state
#------------------------------------------------------------------------
def normal_left_up(self, event):
""" Handle the left button up in the 'normal' state. """
# Append the current point twice, because we need to have the starting
# point and the current point be separate, since the current point
# will be moved with the mouse from now on.
pt = (event.x + self.x, event.y - self.y)
self.polygon.model.points.append(pt)
self.polygon.model.points.append(pt)
self.event_state = 'incomplete'
return
def normal_mouse_move(self, event):
""" Handle the mouse moving in the 'normal' state. """
event.window.set_pointer('pencil')
return
#------------------------------------------------------------------------
# private methods
#------------------------------------------------------------------------
def _is_near_point(self, point, event):
""" Determine if the pointer is near a specified point. """
event_point = (event.x + self.x, event.y - self.y)
return ((abs(point[0] - event_point[0]) + \
abs(point[1] - event_point[1])) <= self.proximity_distance)
def _is_over_start(self, event):
""" Test if the event is 'over' the starting vertex. """
return (len(self.polygon.model.points) > 0 and
self._is_near_point(self.polygon.model.points[0], event))
def _over_point(self, event, points):
""" Return the index of a point in points that event is 'over'.
Returns none if there is no such point.
"""
for i, point in enumerate(points):
if self._is_near_point(point, event):
result = i
break
else:
result = None
return result
# EOF
|