| 1 |
#!/usr/bin/env python |
|---|
| 2 |
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 3 |
##~ License |
|---|
| 4 |
##~ |
|---|
| 5 |
##- The RuneBlade Foundation library is intended to ease some |
|---|
| 6 |
##- aspects of writing intricate Jabber, XML, and User Interface (wxPython, etc.) |
|---|
| 7 |
##- applications, while providing the flexibility to modularly change the |
|---|
| 8 |
##- architecture. Enjoy. |
|---|
| 9 |
##~ |
|---|
| 10 |
##~ Copyright (C) 2002 TechGame Networks, LLC. |
|---|
| 11 |
##~ |
|---|
| 12 |
##~ This library is free software; you can redistribute it and/or |
|---|
| 13 |
##~ modify it under the terms of the BSD style License as found in the |
|---|
| 14 |
##~ LICENSE file included with this distribution. |
|---|
| 15 |
##~ |
|---|
| 16 |
##~ TechGame Networks, LLC can be reached at: |
|---|
| 17 |
##~ 3578 E. Hartsel Drive #211 |
|---|
| 18 |
##~ Colorado Springs, Colorado, USA, 80920 |
|---|
| 19 |
##~ |
|---|
| 20 |
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 21 |
|
|---|
| 22 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 23 |
#~ Imports |
|---|
| 24 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 25 |
|
|---|
| 26 |
from OpenGL import GL, GLU |
|---|
| 27 |
import Numeric |
|---|
| 28 |
|
|---|
| 29 |
from RBRapier.Tools import Transformations2d |
|---|
| 30 |
from RBRapier.Tools.Geometry import Curves |
|---|
| 31 |
from RBRapier.Tools.Geometry.gluPolygonTesselation import PolygonTesselator as _PolygonTesselator |
|---|
| 32 |
|
|---|
| 33 |
from RapierGeometry import GLGeometryCollector, GLStroke, GLFill |
|---|
| 34 |
|
|---|
| 35 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 36 |
#~ Constants / Variables / Etc. |
|---|
| 37 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 38 |
|
|---|
| 39 |
FillRuleLookup = {'evenodd': GLU.GLU_TESS_WINDING_ODD, 'nonzero': GLU.GLU_TESS_WINDING_NONZERO, None: GLU.GLU_TESS_WINDING_ODD} |
|---|
| 40 |
|
|---|
| 41 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 42 |
#~ Definitions |
|---|
| 43 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 44 |
|
|---|
| 45 |
class Style(object): |
|---|
| 46 |
def __init__(self, *styles, **kwstyle): |
|---|
| 47 |
self._style = kwstyle |
|---|
| 48 |
map(self._style.update, styles) |
|---|
| 49 |
|
|---|
| 50 |
def copy(self): |
|---|
| 51 |
result = self.__class__() |
|---|
| 52 |
result._style = self._style.copy() |
|---|
| 53 |
return result |
|---|
| 54 |
|
|---|
| 55 |
def __add__(self, other): |
|---|
| 56 |
return Style(self._style, other._style) |
|---|
| 57 |
|
|---|
| 58 |
def __getitem__(self, index): |
|---|
| 59 |
return self._style[index] |
|---|
| 60 |
def get(self, *args): |
|---|
| 61 |
return self._style.get(*args) |
|---|
| 62 |
|
|---|
| 63 |
def GetFillColor(self): |
|---|
| 64 |
color = self._style.get('fill', None) |
|---|
| 65 |
if color is None: |
|---|
| 66 |
return None |
|---|
| 67 |
elif color == 'currentColor': |
|---|
| 68 |
color = self._style['color'] |
|---|
| 69 |
opacity = int(255*self._style.get('fill-opacity', 1.)) |
|---|
| 70 |
return color + (opacity,) |
|---|
| 71 |
|
|---|
| 72 |
def GetStrokeColor(self): |
|---|
| 73 |
color = self._style.get('stroke', None) |
|---|
| 74 |
if color is None: |
|---|
| 75 |
return None |
|---|
| 76 |
elif color == 'currentColor': |
|---|
| 77 |
color = self._style['color'] |
|---|
| 78 |
opacity = int(255*self._style.get('stroke-opacity', 1.)) |
|---|
| 79 |
return color + (opacity,) |
|---|
| 80 |
|
|---|
| 81 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 82 |
|
|---|
| 83 |
class Transform(Transformations2d.Composite): |
|---|
| 84 |
viewport = (None, None) |
|---|
| 85 |
def GetViewport(self): |
|---|
| 86 |
return self.viewport |
|---|
| 87 |
def SetViewport(self, size): |
|---|
| 88 |
self.viewport = size |
|---|
| 89 |
|
|---|
| 90 |
def __mul__(self, other): |
|---|
| 91 |
if isinstance(other, Transform): |
|---|
| 92 |
return Transform(self.collection + other.collection) |
|---|
| 93 |
else: |
|---|
| 94 |
return Transformations2d.Composite.__mul__(self, other) |
|---|
| 95 |
|
|---|
| 96 |
def __rmul__(self, other): |
|---|
| 97 |
if isinstance(other, Transform): |
|---|
| 98 |
return Transform(other.collection + self.collection) |
|---|
| 99 |
else: |
|---|
| 100 |
return Transformations2d.Composite.__rmul__(self, other) |
|---|
| 101 |
|
|---|
| 102 |
def copy(self): |
|---|
| 103 |
result = self.__class__() |
|---|
| 104 |
result.collection = self.collection[:] |
|---|
| 105 |
result.viewport = self.viewport[:] |
|---|
| 106 |
return result |
|---|
| 107 |
|
|---|
| 108 |
def translate(self, x=0.0, y=0.0): |
|---|
| 109 |
self.Add(Transformations2d.Translate((x,y))) |
|---|
| 110 |
|
|---|
| 111 |
def scale(self, x=1., y=None): |
|---|
| 112 |
if y is None: y = x |
|---|
| 113 |
self.Add(Transformations2d.Scale((x,y))) |
|---|
| 114 |
|
|---|
| 115 |
def rotate(self, angle=0.0, x=0, y=0): |
|---|
| 116 |
if x or y: |
|---|
| 117 |
self.Add(Transformations2d.Translate((x,y))) |
|---|
| 118 |
self.Add(Transformations2d.Rotate(angle)) |
|---|
| 119 |
self.Add(Transformations2d.Translate((-x,-y))) |
|---|
| 120 |
else: |
|---|
| 121 |
self.Add(Transformations2d.Rotate(angle, (x,y))) |
|---|
| 122 |
|
|---|
| 123 |
def skewx(self, angle=0.0): |
|---|
| 124 |
self.Add(Transformations2d.Skew(xy=angle)) |
|---|
| 125 |
|
|---|
| 126 |
def skewy(self, angle=0.0): |
|---|
| 127 |
self.Add(Transformations2d.Skew(yx=angle)) |
|---|
| 128 |
|
|---|
| 129 |
def matrix_byrow(self, xx=1.0, xy=0.0, xh=0.0, yx=0.0, yy=1.0, yh=0.0, hx=0.0, hy=0.0, hh=1.0): |
|---|
| 130 |
"""9 component matrix in row format""" |
|---|
| 131 |
self.Add(Transformations2d.Matrix([[xx, xy, xh], [yx, yy, yh], [hx, hy, hh]])) |
|---|
| 132 |
|
|---|
| 133 |
def matrix(self, a=1.0, b=0.0, c=0.0, d=1.0, e=0.0, f=0.0): |
|---|
| 134 |
"""9 component matrix in svg arrangement -- [[a, c, e],[b, d, f],[0,0,1]]""" |
|---|
| 135 |
self.Add(Transformations2d.Matrix([[a, c, e],[b, d, f],[0,0,1]])) |
|---|
| 136 |
|
|---|
| 137 |
def Add(self, *args, **kw): |
|---|
| 138 |
try: |
|---|
| 139 |
del self._cached_matrix |
|---|
| 140 |
except AttributeError: pass |
|---|
| 141 |
return Transformations2d.Composite.Add(self, *args, **kw) |
|---|
| 142 |
|
|---|
| 143 |
def asArray3x3(self): |
|---|
| 144 |
try: |
|---|
| 145 |
return self._cached_matrix |
|---|
| 146 |
except AttributeError: |
|---|
| 147 |
matrix = Transformations2d.Composite.asArray3x3(self) |
|---|
| 148 |
self._cached_matrix = matrix |
|---|
| 149 |
return matrix |
|---|
| 150 |
|
|---|
| 151 |
def AddSVGTransforms(self, transforms): |
|---|
| 152 |
def AddTransforms(name, *args): |
|---|
| 153 |
transformfn = getattr(self, name.lower(), None) |
|---|
| 154 |
if transformfn is not None: |
|---|
| 155 |
transformfn(*args) |
|---|
| 156 |
|
|---|
| 157 |
transforms.RestoreToEx(onadd=AddTransforms) |
|---|
| 158 |
|
|---|
| 159 |
def fromSVGTransforms(klass, transforms): |
|---|
| 160 |
result = klass() |
|---|
| 161 |
result.AddSVGTransforms(transforms) |
|---|
| 162 |
return result |
|---|
| 163 |
fromSVGTransforms = classmethod(fromSVGTransforms) |
|---|
| 164 |
|
|---|
| 165 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 166 |
|
|---|
| 167 |
class SVGTesselator(_PolygonTesselator): |
|---|
| 168 |
def _OnVertex(self, vertexdata): |
|---|
| 169 |
self.vertexdata.append(vertexdata[:2]) |
|---|
| 170 |
|
|---|
| 171 |
class SVGPathConnector(object): |
|---|
| 172 |
pos = (0,0) |
|---|
| 173 |
controlpoint = None |
|---|
| 174 |
beziersteps = 16 |
|---|
| 175 |
ellipsesteps = 32 |
|---|
| 176 |
|
|---|
| 177 |
command_table = { |
|---|
| 178 |
'M': 'move', |
|---|
| 179 |
'm': 'move_rel', |
|---|
| 180 |
'Z': 'closepath', |
|---|
| 181 |
'z': 'closepath', |
|---|
| 182 |
'L': 'line', |
|---|
| 183 |
'l': 'line_rel', |
|---|
| 184 |
'H': 'hline', |
|---|
| 185 |
'h': 'hline_rel', |
|---|
| 186 |
'V': 'vline', |
|---|
| 187 |
'v': 'vline_rel', |
|---|
| 188 |
'C': 'cubicbezier', |
|---|
| 189 |
'c': 'cubicbezier_rel', |
|---|
| 190 |
'S': 'smoothcurve', |
|---|
| 191 |
's': 'smoothcurve_rel', |
|---|
| 192 |
'Q': 'quadraticbezier', |
|---|
| 193 |
'q': 'quadraticbezier_rel', |
|---|
| 194 |
'T': 'smoothquadraticbezier', |
|---|
| 195 |
't': 'smoothquadraticbezier_rel', |
|---|
| 196 |
'A': 'ellipticarc', |
|---|
| 197 |
'a': 'ellipticarc_rel'} |
|---|
| 198 |
|
|---|
| 199 |
def __init__(self): |
|---|
| 200 |
self.pathlist = [] |
|---|
| 201 |
|
|---|
| 202 |
def AddSVGPath(self, path): |
|---|
| 203 |
def AddPathElement(commandidx, *args): |
|---|
| 204 |
name = self.command_table.get(commandidx, '') |
|---|
| 205 |
transformfn = getattr(self, name, None) |
|---|
| 206 |
if transformfn is not None: |
|---|
| 207 |
transformfn(*args) |
|---|
| 208 |
path.RestoreToEx(onadd=AddPathElement) |
|---|
| 209 |
|
|---|
| 210 |
def fromSVGPath(klass, path): |
|---|
| 211 |
result = klass() |
|---|
| 212 |
result.AddSVGPath(path) |
|---|
| 213 |
return result |
|---|
| 214 |
fromSVGPath = classmethod(fromSVGPath) |
|---|
| 215 |
|
|---|
| 216 |
def _ReduceDuplications(self): |
|---|
| 217 |
def _reduce(path): |
|---|
| 218 |
for idx in range(len(path)-1, 0, -1): |
|---|
| 219 |
if path[idx] == path[idx-1]: |
|---|
| 220 |
del path[idx] |
|---|
| 221 |
return path |
|---|
| 222 |
self.pathlist = filter(None, map(_reduce, self.pathlist)) |
|---|
| 223 |
|
|---|
| 224 |
def GetContours(self): |
|---|
| 225 |
self._ReduceDuplications() |
|---|
| 226 |
return [map(tuple, contour) for contour in self.pathlist] |
|---|
| 227 |
|
|---|
| 228 |
def move_rel(self, x, y): |
|---|
| 229 |
dx, dy = self.pos |
|---|
| 230 |
return self.move(x+dx, y+dy) |
|---|
| 231 |
def move(self, x, y): |
|---|
| 232 |
pathlist = self.pathlist |
|---|
| 233 |
if not pathlist or pathlist[-1]: |
|---|
| 234 |
pathlist.append([(x,y)]) |
|---|
| 235 |
else: |
|---|
| 236 |
pathlist[-1][:] = [(x,y)] |
|---|
| 237 |
self._savepos((x,y)) |
|---|
| 238 |
|
|---|
| 239 |
def closepath(self): |
|---|
| 240 |
pathlist = self.pathlist |
|---|
| 241 |
if len(pathlist[-1]) <= 1: |
|---|
| 242 |
pathlist.pop() |
|---|
| 243 |
else: |
|---|
| 244 |
pos = pathlist[-1][0] |
|---|
| 245 |
pathlist[-1].append(pos) |
|---|
| 246 |
pathlist.append([]) |
|---|
| 247 |
self._savepos(pos) |
|---|
| 248 |
|
|---|
| 249 |
def line_rel(self, x, y): |
|---|
| 250 |
dx, dy = self.pos |
|---|
| 251 |
return self.line(x+dx, y+dy) |
|---|
| 252 |
def line(self, x, y): |
|---|
| 253 |
self.pathlist[-1].append((x,y)) |
|---|
| 254 |
self._savepos((x,y)) |
|---|
| 255 |
|
|---|
| 256 |
def hline_rel(self, x): |
|---|
| 257 |
dx = self.pos[0] |
|---|
| 258 |
return self.hline(x+dx) |
|---|
| 259 |
def hline(self, x): |
|---|
| 260 |
y = self.pos[1] |
|---|
| 261 |
self.pathlist[-1].append((x,y)) |
|---|
| 262 |
self._savepos((x,y)) |
|---|
| 263 |
|
|---|
| 264 |
def vline_rel(self, y): |
|---|
| 265 |
dy = self.pos[1] |
|---|
| 266 |
return self.vline(y+dy) |
|---|
| 267 |
def vline(self, y): |
|---|
| 268 |
x = self.pos[0] |
|---|
| 269 |
self.pathlist[-1].append((x,y)) |
|---|
| 270 |
self._savepos((x,y)) |
|---|
| 271 |
|
|---|
| 272 |
def cubicbezier_rel(self, x1, y1, x2, y2, x, y): |
|---|
| 273 |
dx, dy = self.pos |
|---|
| 274 |
return self.cubicbezier(x1+dx, y1+dy, x2+dx, y2+dy, x+dx, y+dy) |
|---|
| 275 |
def cubicbezier(self, x1, y1, x2, y2, x, y): |
|---|
| 276 |
bezier = Curves.CubicBezier(self.pos, (x1, y1), (x2, y2), (x, y), steps=self.beziersteps) |
|---|
| 277 |
self.pathlist[-1].extend(list(bezier)) |
|---|
| 278 |
self._savepos((x,y), (x2, y2)) |
|---|
| 279 |
|
|---|
| 280 |
def smoothcurve_rel(self, x2, y2, x, y): |
|---|
| 281 |
dx, dy = self.pos |
|---|
| 282 |
return self.smoothcurve(x2+dx, y2+dy, x+dx, y+dy) |
|---|
| 283 |
def smoothcurve(self, x2, y2, x, y): |
|---|
| 284 |
x1r, y1r = self._getControlPoint() |
|---|
| 285 |
return self.cubicbezier(x1r, y1r, x2, y2, x, y) |
|---|
| 286 |
|
|---|
| 287 |
def quadraticbezier_rel(self, x1, y1, x, y): |
|---|
| 288 |
dx, dy = self.pos |
|---|
| 289 |
return self.quadraticbezier(x1+dx, y1+dy, x+dx, y+dy) |
|---|
| 290 |
def quadraticbezier(self, x1, y1, x, y): |
|---|
| 291 |
bezier = Curves.QuadraticBezier(self.pos, (x1, y1), (x, y), steps=self.beziersteps) |
|---|
| 292 |
self.pathlist[-1].extend(list(bezier)) |
|---|
| 293 |
self._savepos((x,y), (x1,y1)) |
|---|
| 294 |
|
|---|
| 295 |
def smoothquadraticbezier_rel(self, x, y): |
|---|
| 296 |
dx, dy = self.pos |
|---|
| 297 |
return self.smoothquadraticbezier(x+dx, y+dy) |
|---|
| 298 |
def smoothquadraticbezier(self, x, y): |
|---|
| 299 |
x1r, y1r = self._getControlPoint() |
|---|
| 300 |
return self.quadraticbezier(x1r, y1r, x, y) |
|---|
| 301 |
|
|---|
| 302 |
def ellipticarc_rel(self, rx, ry, xrotation, largeArcFlag, sweepFlag, x, y): |
|---|
| 303 |
dx, dy = self.pos |
|---|
| 304 |
return self.ellipticarc(rx, ry, xrotation, largeArcFlag, sweepFlag, x+dx, y+dy) |
|---|
| 305 |
def ellipticarc(self, rx, ry, xrotation, largeArcFlag, sweepFlag, x, y): |
|---|
| 306 |
startpt, endpt = self.pos, (x,y) |
|---|
| 307 |
if startpt == endpt: return |
|---|
| 308 |
ellipse = Curves.Ellipse.fromArc(startpt, endpt, abs(rx), abs(ry), xrotation % 360, bool(largeArcFlag), bool(sweepFlag), inDegrees=True, steps=self.ellipsesteps) |
|---|
| 309 |
self.pathlist[-1].extend(list(ellipse)) |
|---|
| 310 |
self._savepos((x,y)) |
|---|
| 311 |
|
|---|
| 312 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 313 |
|
|---|
| 314 |
def _savepos(self, pos, controlpoint=None): |
|---|
| 315 |
self.pos = pos |
|---|
| 316 |
self.controlpoint = controlpoint |
|---|
| 317 |
|
|---|
| 318 |
def _getControlPoint(self): |
|---|
| 319 |
if self.controlpoint is None: |
|---|
| 320 |
return self.pos |
|---|
| 321 |
else: |
|---|
| 322 |
x,y = self.pos |
|---|
| 323 |
xcp, ycp = self.controlpoint |
|---|
| 324 |
return 2*x-xcp, 2*y-ycp |
|---|
| 325 |
|
|---|
| 326 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 327 |
#~ Render Items |
|---|
| 328 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 329 |
|
|---|
| 330 |
class RenderItem(object): |
|---|
| 331 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 332 |
#~ Constants / Variables / Etc. |
|---|
| 333 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 334 |
|
|---|
| 335 |
transform = None |
|---|
| 336 |
position = None |
|---|
| 337 |
default_transform = Transform() |
|---|
| 338 |
style = None |
|---|
| 339 |
default_style = Style(stroke=(0,0,0)) |
|---|
| 340 |
|
|---|
| 341 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 342 |
#~ Definitions |
|---|
| 343 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 344 |
|
|---|
| 345 |
def PreCompile(self, ristack=[]): |
|---|
| 346 |
pass |
|---|
| 347 |
|
|---|
| 348 |
def Compile(self, style, transform, target, ristack): |
|---|
| 349 |
#style, transform = self._GetStyleAndTransform(style, transform) |
|---|
| 350 |
pass |
|---|
| 351 |
|
|---|
| 352 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 353 |
|
|---|
| 354 |
def SetPosition(self, position): |
|---|
| 355 |
self.position = position |
|---|
| 356 |
|
|---|
| 357 |
def SetTransform(self, transform): |
|---|
| 358 |
if self.transform is None: |
|---|
| 359 |
self.transform = Transform.fromSVGTransforms(transform) |
|---|
| 360 |
else: |
|---|
| 361 |
self.transform.AddSVGTransforms(transform) |
|---|
| 362 |
|
|---|
| 363 |
def SetStyle(self, style): |
|---|
| 364 |
self.style = Style(style) |
|---|
| 365 |
|
|---|
| 366 |
def IsGroupRendered(self): |
|---|
| 367 |
return True |
|---|
| 368 |
|
|---|
| 369 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 370 |
|
|---|
| 371 |
def _GetStyleAndTransform(self, style=None, transform=None): |
|---|
| 372 |
if style is None: |
|---|
| 373 |
if self.style is None: |
|---|
| 374 |
style = self.default_style.copy() |
|---|
| 375 |
else: |
|---|
| 376 |
style = self.style.copy() |
|---|
| 377 |
elif self.style is not None: |
|---|
| 378 |
style = style + self.style |
|---|
| 379 |
|
|---|
| 380 |
if transform is None: |
|---|
| 381 |
if self.transform is None: |
|---|
| 382 |
transformout = self.default_transform.copy() |
|---|
| 383 |
else: |
|---|
| 384 |
transformout = self.transform.copy() |
|---|
| 385 |
elif self.transform is not None: |
|---|
| 386 |
transformout = transform * self.transform |
|---|
| 387 |
else: |
|---|
| 388 |
transformout = transform |
|---|
| 389 |
|
|---|
| 390 |
if self.position is not None: |
|---|
| 391 |
if transformout is transform: |
|---|
| 392 |
transformout = transform.copy() |
|---|
| 393 |
transformout.translate(*self.position) |
|---|
| 394 |
return style, transformout |
|---|
| 395 |
|
|---|
| 396 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 397 |
|
|---|
| 398 |
def AddProgress(self, target=None): |
|---|
| 399 |
if target is not None: |
|---|
| 400 |
target.AddGeometryProgress() |
|---|
| 401 |
|
|---|
| 402 |
def CountGroups(self, allowcache=True): |
|---|
| 403 |
return 0 |
|---|
| 404 |
def CountGeometry(self, allowcache=True): |
|---|
| 405 |
return 1 |
|---|
| 406 |
|
|---|
| 407 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 408 |
|
|---|
| 409 |
class GroupRenderItem(RenderItem): |
|---|
| 410 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 411 |
#~ Constants / Variables / Etc. |
|---|
| 412 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 413 |
|
|---|
| 414 |
renderChildren = () |
|---|
| 415 |
otherChildren = () |
|---|
| 416 |
|
|---|
| 417 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 418 |
#~ Public Methods |
|---|
| 419 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 420 |
|
|---|
| 421 |
def PreCompile(self, ristack=[]): |
|---|
| 422 |
ristack = ristack + [self] |
|---|
| 423 |
for child in self.renderChildren: |
|---|
| 424 |
child.PreCompile(ristack) |
|---|
| 425 |
|
|---|
| 426 |
def Compile(self, style=None, transform=None, target=None, ristack=[]): |
|---|
| 427 |
style, transform = self._GetStyleAndTransform(style, transform) |
|---|
| 428 |
ristack = ristack + [self] |
|---|
| 429 |
for child in self.renderChildren: |
|---|
| 430 |
child.Compile(style, transform, target, ristack) |
|---|
| 431 |
child.AddProgress(target) |
|---|
| 432 |
|
|---|
| 433 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 434 |
|
|---|
| 435 |
def SetChildren(self, children): |
|---|
| 436 |
self.renderChildren = [] |
|---|
| 437 |
self.otherChildren = [] |
|---|
| 438 |
for child in children: |
|---|
| 439 |
if isinstance(child, RenderItem): |
|---|
| 440 |
if child.IsGroupRendered(): |
|---|
| 441 |
self.renderChildren.append(child) |
|---|
| 442 |
continue |
|---|
| 443 |
self.otherChildren.append(child) |
|---|
| 444 |
|
|---|
| 445 |
#~ Progress Measurements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 446 |
|
|---|
| 447 |
def AddProgress(self, target=None): |
|---|
| 448 |
if target is not None: |
|---|
| 449 |
target.AddGroupProgress() |
|---|
| 450 |
|
|---|
| 451 |
def CountGroups(self, allowcache=True): |
|---|
| 452 |
if allowcache: |
|---|
| 453 |
try: return self._cache_countgroups |
|---|
| 454 |
except AttributeError: pass |
|---|
| 455 |
result = sum([child.CountGroups(allowcache) for child in self.renderChildren], 1) |
|---|
| 456 |
self._cache_countgroups = result |
|---|
| 457 |
return result |
|---|
| 458 |
|
|---|
| 459 |
def CountGeometry(self, allowcache=True): |
|---|
| 460 |
if allowcache: |
|---|
| 461 |
try: return self._cache_countgeometry |
|---|
| 462 |
except AttributeError: pass |
|---|
| 463 |
result = sum([child.CountGeometry(allowcache) for child in self.renderChildren], 0) |
|---|
| 464 |
self._cache_countgeometry = result |
|---|
| 465 |
return result |
|---|
| 466 |
|
|---|
| 467 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 468 |
|
|---|
| 469 |
class UseRenderItem(RenderItem): |
|---|
| 470 |
dimensions = (0.,0.) |
|---|
| 471 |
|
|---|
| 472 |
def PreCompile(self, ristack=[]): |
|---|
| 473 |
self.useitem.PreCompile(ristack+[self]) |
|---|
| 474 |
|
|---|
| 475 |
def Compile(self, style=None, transform=None, target=None, ristack=[]): |
|---|
| 476 |
style, transform = self._GetStyleAndTransform(style, transform) |
|---|
| 477 |
self.useitem.Compile(style, transform, target, ristack+[self]) |
|---|
| 478 |
self.useitem.AddProgress(target) |
|---|
| 479 |
|
|---|
| 480 |
def _GetStyleAndTransform(self, style=None, transform=None): |
|---|
| 481 |
style, transform = RenderItem._GetStyleAndTransform(self, style, transform) |
|---|
| 482 |
transform.SetViewport(self.dimensions) |
|---|
| 483 |
return style, transform |
|---|
| 484 |
|
|---|
| 485 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 486 |
|
|---|
| 487 |
def SetDimensions(self, width, height): |
|---|
| 488 |
self.dimensions = width, height |
|---|
| 489 |
|
|---|
| 490 |
def SetUseItem(self, useitem): |
|---|
| 491 |
self.useitem = useitem |
|---|
| 492 |
|
|---|
| 493 |
#~ Progress Measurements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 494 |
|
|---|
| 495 |
def AddProgress(self, target=None): |
|---|
| 496 |
if target is not None: |
|---|
| 497 |
target.AddGroupProgress() |
|---|
| 498 |
|
|---|
| 499 |
def CountGroups(self, allowcache=True): |
|---|
| 500 |
return 1 + self.useitem.CountGroups(allowcache) |
|---|
| 501 |
def CountGeometry(self, allowcache=True): |
|---|
| 502 |
return self.useitem.CountGeometry(allowcache) |
|---|
| 503 |
|
|---|
| 504 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 505 |
|
|---|
| 506 |
class ContainerGroupRenderItem(GroupRenderItem): |
|---|
| 507 |
viewbox = (None, None, None, None) |
|---|
| 508 |
dimensions = (None, None) |
|---|
| 509 |
alignment = ('xmid', 'ymid', 'meet') |
|---|
| 510 |
position = None |
|---|
| 511 |
|
|---|
| 512 |
def _GetStyleAndTransform(self, style=None, transform=None): |
|---|
| 513 |
if transform is None: |
|---|
| 514 |
# This implies that the default OpenGL portal is setup and the |
|---|
| 515 |
# display is [-1,1] wide and [-1,1] tall. Since I'm trying to |
|---|
| 516 |
# implement this coordinate independent, then the svg dimensions |
|---|
| 517 |
# should be mapped to this [-1,1] range in both directions. At |
|---|
| 518 |
# this point, we can also flip the coordinate system for OpenGL. |
|---|
| 519 |
|
|---|
| 520 |
transform = Transform() |
|---|
| 521 |
if self.position is not None: |
|---|
| 522 |
ux, uy = self.position |
|---|
| 523 |
else: ux, uy = 0., 0. |
|---|
| 524 |
if self.dimensions != (None, None): |
|---|
| 525 |
uw, uh = self.dimensions |
|---|
| 526 |
else: uw, uh = 1., 1. |
|---|
| 527 |
|
|---|
| 528 |
transform.matrix_byrow(2./uw,0.,-1.-2.*ux/uw, 0., -2./uh, 1.+2.*uy/uh) |
|---|
| 529 |
else: |
|---|
| 530 |
transform = transform.copy() |
|---|
| 531 |
ux, uy = (0.,0.) |
|---|
| 532 |
uw, uh = transform.GetViewport() |
|---|
| 533 |
|
|---|
| 534 |
if self.position is not None: |
|---|
| 535 |
ux, uy = self.position |
|---|
| 536 |
if self.dimensions != (None, None): |
|---|
| 537 |
uw, uh = self.dimensions |
|---|
| 538 |
|
|---|
| 539 |
vx, vy, vw, vh = self.viewbox |
|---|
| 540 |
uw, uh = uw or vw, uh or vh |
|---|
| 541 |
|
|---|
| 542 |
if vw > 0. and vh > 0.: |
|---|
| 543 |
vx, vy = vx or 0., vy or 0. |
|---|
| 544 |
|
|---|
| 545 |
xalign, yalign, aligntype = [(s or '').lower() for s in self.alignment] |
|---|
| 546 |
xalign, yalign = xalign or 'xmid', yalign or 'ymid' |
|---|
| 547 |
nw, nh = float(uw)/vw, float(uh)/vh |
|---|
| 548 |
|
|---|
| 549 |
if aligntype == 'meet': # scale by larger dimension |
|---|
| 550 |
if nh < nw: # height is greater -- scale by height |
|---|
| 551 |
sx = sy = uh/vh |
|---|
| 552 |
else: # width is greater -- scale by width |
|---|
| 553 |
sx = sy = uw/vw |
|---|
| 554 |
elif aligntype == 'slice': # scale by smaller dimension |
|---|
| 555 |
if nh > nw: # width is greater -- scale by height |
|---|
| 556 |
sx = sy = uh/vh |
|---|
| 557 |
else: # height is greater -- scale by width |
|---|
| 558 |
sx = sy = uw/vw |
|---|
| 559 |
else: # scale by both width and height |
|---|
| 560 |
sx, sy = uw/vw, uh/vh |
|---|
| 561 |
|
|---|
| 562 |
if aligntype in ['meet', 'slice']: |
|---|
| 563 |
if xalign == 'xmin': tx = ux - sx*vx + 0. |
|---|
| 564 |
elif xalign == 'xmid': tx = ux - sx*vx + 0.5*(uw-vw*sx) |
|---|
| 565 |
elif xalign == 'xmax': tx = ux - sx*vx + (uw-vw*sx) |
|---|
| 566 |
else: assert False |
|---|
| 567 |
|
|---|
| 568 |
if yalign == 'ymin': ty = uy - sy*vy + 0. |
|---|
| 569 |
elif yalign == 'ymid': ty = uy - sy*vy + 0.5*(uh-vh*sy) |
|---|
| 570 |
elif yalign == 'ymax': ty = uy - sy*vy + (uh-vh*sy) |
|---|
| 571 |
else: assert False |
|---|
| 572 |
else: |
|---|
| 573 |
tx, ty = ux, uy |
|---|
| 574 |
|
|---|
| 575 |
transform.matrix_byrow(sx, 0., tx, 0., sy, ty) |
|---|
| 576 |
|
|---|
| 577 |
return GroupRenderItem._GetStyleAndTransform(self, style, transform) |
|---|
| 578 |
|
|---|
| 579 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 580 |
|
|---|
| 581 |
def SetDimensions(self, width, height): |
|---|
| 582 |
self.dimensions = width, height |
|---|
| 583 |
def SetViewBox(self, viewbox): |
|---|
| 584 |
if viewbox is not None: |
|---|
| 585 |
self.viewbox = viewbox |
|---|
| 586 |
def SetAlignment(self, xalign, yalign, alignstyle): |
|---|
| 587 |
self.alignment = xalign, yalign, alignstyle |
|---|
| 588 |
|
|---|
| 589 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 590 |
|
|---|
| 591 |
class SymbolGroup(ContainerGroupRenderItem): |
|---|
| 592 |
def IsGroupRendered(self): |
|---|
| 593 |
return False |
|---|
| 594 |
|
|---|
| 595 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 596 |
|
|---|
| 597 |
class SVGGroupRenderItem(ContainerGroupRenderItem): |
|---|
| 598 |
target = None |
|---|
| 599 |
def Compile(self, style=None, transform=None, intarget=None, ristack=[]): |
|---|
| 600 |
self.target = GLGeometryCollector(self.CountGroups(), self.CountGeometry()) |
|---|
| 601 |
# TODO: implement render options |
|---|
| 602 |
|
|---|
| 603 |
ContainerGroupRenderItem.Compile(self, style, transform, self.target, ristack) |
|---|
| 604 |
|
|---|
| 605 |
if intarget is not None: |
|---|
| 606 |
result = self.target.Commit(intarget) |
|---|
| 607 |
else: # we're the root... return our goodies |
|---|
| 608 |
result = self.target.GetRenderables() |
|---|
| 609 |
|
|---|
| 610 |
del self.target |
|---|
| 611 |
return result |
|---|
| 612 |
|
|---|
| 613 |
def CompileProgress(self): |
|---|
| 614 |
if self.target: |
|---|
| 615 |
return self.target.Progress() |
|---|
| 616 |
else: return None |
|---|
| 617 |
|
|---|
| 618 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 619 |
|
|---|
| 620 |
def SetRenderOptions(self, text=None, shape=None, image=None): |
|---|
| 621 |
pass # TODO: implement |
|---|
| 622 |
|
|---|
| 623 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 624 |
#~ Shapes |
|---|
| 625 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 626 |
|
|---|
| 627 |
class Line(RenderItem): |
|---|
| 628 |
def Compile(self, style, transform, target, ristack): |
|---|
| 629 |
style, transform = self._GetStyleAndTransform(style, transform) |
|---|
| 630 |
|
|---|
| 631 |
points = transform.TransformPoints(self.points) |
|---|
| 632 |
strokecolor = style.GetStrokeColor() |
|---|
| 633 |
if strokecolor is not None: |
|---|
| 634 |
stroke = GLStroke(points, strokecolor) |
|---|
| 635 |
stroke.AddLines(range(2)) |
|---|
| 636 |
stroke.Commit(target) |
|---|
| 637 |
|
|---|
| 638 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 639 |
|
|---|
| 640 |
def SetLine(self, c0, c1): |
|---|
| 641 |
self.points = [c0, c1] |
|---|
| 642 |
|
|---|
| 643 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 644 |
|
|---|
| 645 |
class Rect(RenderItem): |
|---|
| 646 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 647 |
#~ Constants / Variables / Etc. |
|---|
| 648 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 649 |
|
|---|
| 650 |
dimensions = (1,1) |
|---|
| 651 |
|
|---|
| 652 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 653 |
#~ Public Methods |
|---|
| 654 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 655 |
|
|---|
| 656 |
def Compile(self, style, transform, target, ristack): |
|---|
| 657 |
style, transform = self._GetStyleAndTransform(style, transform) |
|---|
| 658 |
x,y = 0., 0. |
|---|
| 659 |
w,h = self.dimensions |
|---|
| 660 |
|
|---|
| 661 |
# TODO: compute rounded rectangles |
|---|
| 662 |
points = [(x,y), (x, y+h), (x+w, y+h), (x+w, y)] |
|---|
| 663 |
points = transform.TransformPoints(points) |
|---|
| 664 |
|
|---|
| 665 |
fillcolor = style.GetFillColor() |
|---|
| 666 |
if fillcolor is not None: |
|---|
| 667 |
fill = GLFill(points, fillcolor) |
|---|
| 668 |
fill.AddTriangleFan(range(4)) |
|---|
| 669 |
fill.Commit(target) |
|---|
| 670 |
|
|---|
| 671 |
strokecolor = style.GetStrokeColor() |
|---|
| 672 |
if strokecolor is not None: |
|---|
| 673 |
stroke = GLStroke(points, strokecolor) |
|---|
| 674 |
stroke.AddLineLoop(range(4)) |
|---|
| 675 |
stroke.Commit(target) |
|---|
| 676 |
|
|---|
| 677 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 678 |
|
|---|
| 679 |
def SetDimensions(self, width, height): |
|---|
| 680 |
self.dimensions = width, height |
|---|
| 681 |
|
|---|
| 682 |
def SetRadi(self, radiusx, radiusy): |
|---|
| 683 |
if radiusx or radiusy: |
|---|
| 684 |
warnings.warn('Rounded rectangles are not yet supported') |
|---|
| 685 |
#self.radiusx = radiusx |
|---|
| 686 |
#self.radiusy = radiusy |
|---|
| 687 |
|
|---|
| 688 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 689 |
|
|---|
| 690 |
class Ellipse(RenderItem): |
|---|
| 691 |
ellipsesteps = 64 |
|---|
| 692 |
def Compile(self, style, transform, target, ristack): |
|---|
| 693 |
style, transform = self._GetStyleAndTransform(style, transform) |
|---|
| 694 |
|
|---|
| 695 |
ellipse = Curves.Ellipse(self.center, self.radiusx, self.radiusy, steps=self.ellipsesteps) |
|---|
| 696 |
points = transform.TransformPoints(ellipse.asCurve()) |
|---|
| 697 |
|
|---|
| 698 |
fillcolor = style.GetFillColor() |
|---|
| 699 |
if fillcolor is not None: |
|---|
| 700 |
fillpoints = Numeric.concatenate((transform.TransformPoints([self.center]), points)) |
|---|
| 701 |
fill = GLFill(fillpoints, fillcolor) |
|---|
| 702 |
fill.AddTriangleFan(range(len(points))+[1]) |
|---|
| 703 |
fill.Commit(target) |
|---|
| 704 |
|
|---|
| 705 |
strokecolor = style.GetStrokeColor() |
|---|
| 706 |
if strokecolor is not None: |
|---|
| 707 |
stroke = GLStroke(points, strokecolor) |
|---|
| 708 |
stroke.AddLineLoop(range(len(points))) |
|---|
| 709 |
stroke.Commit(target) |
|---|
| 710 |
|
|---|
| 711 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 712 |
|
|---|
| 713 |
def SetCenter(self, center): |
|---|
| 714 |
self.center = center |
|---|
| 715 |
def SetRadius(self, radius): |
|---|
| 716 |
self.SetRadi(radius, radius) |
|---|
| 717 |
def SetRadi(self, radiusx, radiusy): |
|---|
| 718 |
self.radiusx = radiusx |
|---|
| 719 |
self.radiusy = radiusy |
|---|
| 720 |
|
|---|
| 721 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 722 |
|
|---|
| 723 |
class Circle(Ellipse): |
|---|
| 724 |
# Note: this could be optimized beyond ellipse. |
|---|
| 725 |
# However, we get two implementations for one :) |
|---|
| 726 |
pass |
|---|
| 727 |
|
|---|
| 728 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 729 |
|
|---|
| 730 |
class Polyline(RenderItem): |
|---|
| 731 |
def Compile(self, style, transform, target, ristack): |
|---|
| 732 |
if len(self.points) < 2: return |
|---|
| 733 |
style, transform = self._GetStyleAndTransform(style, transform) |
|---|
| 734 |
|
|---|
| 735 |
fillcolor = style.GetFillColor() |
|---|
| 736 |
if fillcolor is not None and len(self.points) >= 3: |
|---|
| 737 |
windingrule = FillRuleLookup.get(style.get('fill-rule')) |
|---|
| 738 |
if windingrule is None: windingrule = FillRuleLookup[None] |
|---|
| 739 |
|
|---|
| 740 |
tesselator = SVGTesselator(normal=(0,0,1)) |
|---|
| 741 |
tesselator.open(windingrule=windingrule) |
|---|
| 742 |
glmodedatalist = tesselator.tessellate([self.points]) |
|---|
| 743 |
tesselator.close() |
|---|
| 744 |
for glmode, fillpoints in glmodedatalist: |
|---|
| 745 |
fillpoints = transform.TransformPoints(fillpoints) |
|---|
| 746 |
fill = GLFill(fillpoints, fillcolor) |
|---|
| 747 |
fill.Add(glmode, range(len(fillpoints))) |
|---|
| 748 |
fill.Commit(target) |
|---|
| 749 |
|
|---|
| 750 |
strokecolor = style.GetStrokeColor() |
|---|
| 751 |
if strokecolor is not None: |
|---|
| 752 |
points = transform.TransformPoints(self.points) |
|---|
| 753 |
stroke = GLStroke(points, strokecolor) |
|---|
| 754 |
stroke.AddLineStrip(range(len(points))) |
|---|
| 755 |
stroke.Commit(target) |
|---|
| 756 |
|
|---|
| 757 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 758 |
|
|---|
| 759 |
def SetPoints(self, points): |
|---|
| 760 |
self.points = points |
|---|
| 761 |
|
|---|
| 762 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 763 |
|
|---|
| 764 |
class Polygon(Polyline): |
|---|
| 765 |
def Compile(self, style, transform, target, ristack): |
|---|
| 766 |
if len(self.points) < 2: return |
|---|
| 767 |
style, transform = self._GetStyleAndTransform(style, transform) |
|---|
| 768 |
|
|---|
| 769 |
windingrule = FillRuleLookup.get(style.get('fill-rule')) |
|---|
| 770 |
if windingrule is None: windingrule = FillRuleLookup[None] |
|---|
| 771 |
tesselator = SVGTesselator(normal=(0,0,1)) |
|---|
| 772 |
tesselator.open(windingrule=windingrule) |
|---|
| 773 |
|
|---|
| 774 |
fillcolor = style.GetFillColor() |
|---|
| 775 |
if fillcolor is not None: |
|---|
| 776 |
glmodedatalist = tesselator.tessellate([self.points]) |
|---|
| 777 |
for glmode, fillpoints in glmodedatalist: |
|---|
| 778 |
fillpoints = transform.TransformPoints(fillpoints) |
|---|
| 779 |
fill = GLFill(fillpoints, fillcolor) |
|---|
| 780 |
fill.Add(glmode, range(len(fillpoints))) |
|---|
| 781 |
fill.Commit(target) |
|---|
| 782 |
|
|---|
| 783 |
strokecolor = style.GetStrokeColor() |
|---|
| 784 |
if strokecolor is not None: |
|---|
| 785 |
glmodedatalist = tesselator.tessellate([self.points]) |
|---|
| 786 |
for glmode, strokepoints in glmodedatalist: |
|---|
| 787 |
strokepoints = transform.TransformPoints(strokepoints) |
|---|
| 788 |
stroke = GLStroke(strokepoints, strokecolor) |
|---|
| 789 |
stroke.Add(glmode, range(len(strokepoints))) |
|---|
| 790 |
stroke.Commit(target) |
|---|
| 791 |
tesselator.close() |
|---|
| 792 |
|
|---|
| 793 |
def SetPoints(self, points): |
|---|
| 794 |
if points[0] != points[-1]: |
|---|
| 795 |
self.points = points + [points[0]] |
|---|
| 796 |
else: |
|---|
| 797 |
self.points = points |
|---|
| 798 |
assert len(self.points) >= 3 |
|---|
| 799 |
|
|---|
| 800 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 801 |
|
|---|
| 802 |
class Path(RenderItem): |
|---|
| 803 |
def Compile(self, style, transform, target, ristack): |
|---|
| 804 |
contours = SVGPathConnector.fromSVGPath(self.path).GetContours() |
|---|
| 805 |
if not contours: return # must be a degenerate path... exit gracefully |
|---|
| 806 |
|
|---|
| 807 |
style, transform = self._GetStyleAndTransform(style, transform) |
|---|
| 808 |
|
|---|
| 809 |
fillcolor = style.GetFillColor() |
|---|
| 810 |
if fillcolor is not None: |
|---|
| 811 |
# paths can only be formed when there is more than two points... |
|---|
| 812 |
contourlist = [contour for contour in contours if len(contour)>2] |
|---|
| 813 |
|
|---|
| 814 |
if contourlist: |
|---|
| 815 |
windingrule = FillRuleLookup.get(style.get('fill-rule')) |
|---|
| 816 |
if windingrule is None: windingrule = FillRuleLookup[None] |
|---|
| 817 |
|
|---|
| 818 |
tesselator = SVGTesselator(normal=(0,0,1)) |
|---|
| 819 |
tesselator.open(windingrule=windingrule) |
|---|
| 820 |
glmodedatalist = tesselator.tessellate(contourlist) |
|---|
| 821 |
tesselator.close() |
|---|
| 822 |
for glmode, fillpoints in glmodedatalist: |
|---|
| 823 |
fillpoints = transform.TransformPoints(fillpoints) |
|---|
| 824 |
fill = GLFill(fillpoints, fillcolor) |
|---|
| 825 |
fill.Add(glmode, range(len(fillpoints))) |
|---|
| 826 |
fill.Commit(target) |
|---|
| 827 |
|
|---|
| 828 |
strokecolor = style.GetStrokeColor() |
|---|
| 829 |
if strokecolor is not None: |
|---|
| 830 |
points = [pt for contour in contours for pt in contour] |
|---|
| 831 |
points = transform.TransformPoints(points) |
|---|
| 832 |
|
|---|
| 833 |
stroke = GLStroke(points, strokecolor) |
|---|
| 834 |
index = 0 |
|---|
| 835 |
for count in map(len, contours): |
|---|
| 836 |
stroke.AddLineStrip(range(index, index+count)) |
|---|
| 837 |
index += count |
|---|
| 838 |
stroke.Commit(target) |
|---|
| 839 |
|
|---|
| 840 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 841 |
|
|---|
| 842 |
def SetPath(self, path): |
|---|
| 843 |
self.path = path |
|---|
| 844 |
|
|---|
| 845 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 846 |
#~ Images |
|---|
| 847 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 848 |
|
|---|
| 849 |
class Image(RenderItem): |
|---|
| 850 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 851 |
|
|---|
| 852 |
def SetDimensions(self, width, height): |
|---|
| 853 |
self.dimensions = width, height |
|---|
| 854 |
def SetImageReference(self, ref): |
|---|
| 855 |
self.imageref = ref |
|---|
| 856 |
|
|---|
| 857 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 858 |
#~ Text Content |
|---|
| 859 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 860 |
|
|---|
| 861 |
class ContentRenderItem(RenderItem): |
|---|
| 862 |
#~ SVG Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 863 |
|
|---|
| 864 |
def SetContent(self, content): |
|---|
| 865 |
self.content = content |
|---|
| 866 |
|
|---|
| 867 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 868 |
#~ Main mapping |
|---|
| 869 |
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 870 |
|
|---|
| 871 |
class RapierSVGRenderItemFactory(object): |
|---|
| 872 |
mapping = { |
|---|
| 873 |
'svg': SVGGroupRenderItem, |
|---|
| 874 |
'g': GroupRenderItem, |
|---|
| 875 |
|
|---|
| 876 |
'line': Line, |
|---|
| 877 |
'rect': Rect, |
|---|
| 878 |
#'image': Image, |
|---|
| 879 |
'path': Path, |
|---|
| 880 |
'polyline': Polyline, |
|---|
| 881 |
'polygon': Polygon, |
|---|
| 882 |
'ellipse': Ellipse, |
|---|
| 883 |
'circle': Circle, |
|---|
| 884 |
|
|---|
| 885 |
'use': UseRenderItem, |
|---|
| 886 |
'symbol': SymbolGroup, |
|---|
| 887 |
#'pattern': None, |
|---|
| 888 |
#'marker': None, |
|---|
| 889 |
|
|---|
| 890 |
#'text': None, |
|---|
| 891 |
#'tspan': None, |
|---|
| 892 |
'title': None, |
|---|
| 893 |
'desc': None, |
|---|
| 894 |
|
|---|
| 895 |
'defs': None, |
|---|
| 896 |
} |
|---|
| 897 |
|
|---|
| 898 |
def __call__(self, renderitem): |
|---|
| 899 |
factory = self.mapping.get(renderitem) |
|---|
| 900 |
if factory is not None: |
|---|
| 901 |
return factory() |
|---|
| 902 |
else: |
|---|
| 903 |
return None |
|---|
| 904 |
|
|---|