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
|
"""A filled polygon component"""
# Major package imports.
from numpy import array
# Enthought library imports.
from kiva.constants import EOF_FILL_STROKE, FILL, FILL_STROKE
from kiva.agg import points_in_polygon
from traits.api import Any, Event, Float, HasTraits, Instance, List, \
Property, Trait, Tuple
from traitsui.api import Group, View
# Local imports.
from enable.api import border_size_trait, Component
from enable.colors import ColorTrait
class PolygonModel(HasTraits):
""" The data model for a Polygon. """
# The points that make up the vertices of this polygon.
points = List(Tuple)
def reset(self):
self.points = []
return
class Polygon(Component):
""" A filled polygon component. """
#--------------------------------------------------------------------------
# Trait definitions.
#--------------------------------------------------------------------------
# The background color of this polygon.
background_color = ColorTrait("white")
# The color of the border of this polygon.
border_color = ColorTrait("black")
# The dash pattern to use for this polygon.
border_dash = Any
# The thickness of the border of this polygon.
border_size = Trait(1, border_size_trait)
# Event fired when the polygon is "complete".
complete = Event
# The rule to use to determine the inside of the polygon.
inside_rule = Trait('winding',
{'winding':FILL_STROKE, 'oddeven':EOF_FILL_STROKE })
# The points that make up this polygon.
model = Instance(PolygonModel, ())
# Convenience property to access the model's points.
points = Property
# The color of each vertex.
vertex_color = ColorTrait("black")
# The size of each vertex.
vertex_size = Float(3.0)
traits_view = View(Group('<component>', id = 'component'),
Group('<links>', id = 'links'),
Group('background_color', '_',
'border_color', '_',
'border_size',
id = 'Box',
style = 'custom'))
colorchip_map = {'color': 'color', 'alt_color': 'border_color'}
#--------------------------------------------------------------------------
# Traits property accessors
#--------------------------------------------------------------------------
def _get_points(self):
return self.model.points
#--------------------------------------------------------------------------
# 'Polygon' interface
#--------------------------------------------------------------------------
def reset(self):
"Reset the polygon to the initial state"
self.model.reset()
self.event_state = 'normal'
return
#--------------------------------------------------------------------------
# 'Component' interface
#--------------------------------------------------------------------------
def _draw_mainlayer(self, gc, view_bounds=None, mode="normal"):
"Draw the component in the specified graphics context"
self._draw_closed(gc)
return
#--------------------------------------------------------------------------
# Protected interface
#--------------------------------------------------------------------------
def _is_in(self, point):
""" Test if the point (an x, y tuple) is within this polygonal region.
To perform the test, we use the winding number inclusion algorithm,
referenced in the comp.graphics.algorithms FAQ
(http://www.faqs.org/faqs/graphics/algorithms-faq/) and described in
detail here:
http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
"""
point_array = array((point,))
vertices = array(self.model.points)
winding = self.inside_rule == 'winding'
result = points_in_polygon(point_array, vertices, winding)
return result[0]
#--------------------------------------------------------------------------
# Private interface
#--------------------------------------------------------------------------
def _draw_closed(self, gc):
"Draw this polygon as a closed polygon"
if len(self.model.points) > 2:
# Set the drawing parameters.
gc.set_fill_color(self.background_color_)
gc.set_stroke_color(self.border_color_)
gc.set_line_width(self.border_size)
gc.set_line_dash(self.border_dash)
# Draw the path.
gc.begin_path()
gc.move_to(self.model.points[0][0] - self.x,
self.model.points[0][1] + self.y)
offset_points = [(x - self.x, y + self.y)
for x, y in self.model.points]
gc.lines(offset_points)
gc.close_path()
gc.draw_path(self.inside_rule_)
# Draw the vertices.
self._draw_vertices(gc)
return
def _draw_open ( self, gc ):
"Draw this polygon as an open polygon"
if len(self.model.points) > 2:
# Set the drawing parameters.
gc.set_fill_color( self.background_color_ )
gc.set_stroke_color( self.border_color_ )
gc.set_line_width( self.border_size )
gc.set_line_dash( self.border_dash )
# Draw the path.
gc.begin_path()
gc.move_to(self.model.points[0][0] - self.x,
self.model.points[0][1] + self.y)
offset_points = [(x - self.x, y + self.y) for x, y in self.model.points ]
gc.lines(offset_points)
gc.draw_path(self.inside_rule_)
# Draw the vertices.
self._draw_vertices(gc)
return
def _draw_vertices(self, gc):
"Draw the vertices of the polygon."
gc.set_fill_color(self.vertex_color_)
gc.set_line_dash(None)
offset = self.vertex_size / 2.0
offset_points = [(x + self.x, y + self.y)
for x, y in self.model.points]
if hasattr(gc, 'draw_path_at_points'):
path = gc.get_empty_path()
path.rect(-offset, -offset, self.vertex_size, self.vertex_size)
gc.draw_path_at_points(offset_points, path, FILL_STROKE)
else:
for x, y in offset_points:
gc.draw_rect((x - offset, y - offset,
self.vertex_size, self.vertex_size), FILL)
return
# EOF
|