1 # -*- coding: iso-8859-1 -*-
2 #----------------------------------------------------------------------------
4 # Purpose: DrawnShape class
6 # Author: Pierre Hjälm (from C++ original by Julian Smart)
10 # Copyright: (c) 2004 Pierre Hjälm - 1998 Julian Smart
11 # License: wxWindows license
12 #----------------------------------------------------------------------------
16 from _basic
import RectangleShape
17 from _oglmisc
import *
20 METAFLAGS_ATTACHMENTS
= 2
31 DRAWOP_SET_TEXT_COLOUR
= 4
32 DRAWOP_SET_BK_COLOUR
= 5
33 DRAWOP_SET_BK_MODE
= 6
34 DRAWOP_SET_CLIPPING_RECT
= 7
35 DRAWOP_DESTROY_CLIPPING_RECT
= 8
38 DRAWOP_DRAW_POLYLINE
= 21
39 DRAWOP_DRAW_POLYGON
= 22
41 DRAWOP_DRAW_ROUNDED_RECT
= 24
42 DRAWOP_DRAW_ELLIPSE
= 25
43 DRAWOP_DRAW_POINT
= 26
46 DRAWOP_DRAW_SPLINE
= 29
47 DRAWOP_DRAW_ELLIPTIC_ARC
= 30
50 def __init__(self
, theOp
):
56 def GetPerimeterPoint(self
, x1
, y1
, x2
, y2
, xOffset
, yOffset
, attachmentMode
):
59 def Scale(self
,scaleX
, scaleY
):
62 def Translate(self
, x
, y
):
65 class OpSetGDI(DrawOp
):
66 """Set font, brush, text colour."""
67 def __init__(self
, theOp
, theImage
, theGdiIndex
, theMode
= 0):
68 DrawOp
.__init
__(self
, theOp
)
70 self
._gdiIndex
= theGdiIndex
71 self
._image
= theImage
74 def Do(self
, dc
, xoffset
= 0, yoffset
= 0):
75 if self
._op
== DRAWOP_SET_PEN
:
76 # Check for overriding this operation for outline colour
77 if self
._gdiIndex
in self
._image
._outlineColours
:
78 if self
._image
._outlinePen
:
79 dc
.SetPen(self
._image
._outlinePen
)
82 dc
.SetPen(self
._image
._gdiObjects
[self
._gdiIndex
])
85 elif self
._op
== DRAWOP_SET_BRUSH
:
86 # Check for overriding this operation for outline or fill colour
87 if self
._gdiIndex
in self
._image
._outlineColours
:
88 # Need to construct a brush to match the outline pen's colour
89 if self
._image
._outlinePen
:
90 br
= wx
.TheBrushList
.FindOrCreateBrush(self
._image
._outlinePen
, wx
.SOLID
)
93 elif self
._gdiIndex
in self
._image
._fillColours
:
94 if self
._image
._fillBrush
:
95 dc
.SetBrush(self
._image
._fillBrush
)
97 brush
= self
._image
._gdiObjects
[self
._gdiIndex
]
100 elif self
._op
== DRAWOP_SET_FONT
:
102 dc
.SetFont(self
._image
._gdiObjects
[self
._gdiIndex
])
105 elif self
._op
== DRAWOP_SET_TEXT_COLOUR
:
106 dc
.SetTextForeground(wx
.Colour(self
._r
, self
._g
, self
._b
))
107 elif self
._op
== DRAWOP_SET_BK_COLOUR
:
108 dc
.SetTextBackground(wx
.Colour(self
._r
, self
._g
, self
._b
))
109 elif self
._op
== DRAWOP_SET_BK_MODE
:
110 dc
.SetBackgroundMode(self
._mode
)
113 class OpSetClipping(DrawOp
):
114 """Set/destroy clipping."""
115 def __init__(self
, theOp
, theX1
, theY1
, theX2
, theY2
):
116 DrawOp
.__init
__(self
, theOp
)
123 def Do(self
, dc
, xoffset
, yoffset
):
124 if self
._op
== DRAWOP_SET_CLIPPING_RECT
:
125 dc
.SetClippingRegion(self
._x
1 + xoffset
, self
._y
1 + yoffset
, self
._x
2 + xoffset
, self
._y
2 + yoffset
)
126 elif self
._op
== DRAWOP_DESTROY_CLIPPING_RECT
:
127 dc
.DestroyClippingRegion()
129 def Scale(self
, scaleX
, scaleY
):
135 def Translate(self
, x
, y
):
140 class OpDraw(DrawOp
):
141 """Draw line, rectangle, rounded rectangle, ellipse, point, arc, text."""
142 def __init__(self
, theOp
, theX1
, theY1
, theX2
, theY2
, theRadius
= 0.0, s
= ""):
143 DrawOp
.__init
__(self
, theOp
)
151 self
._radius
= theRadius
154 def Do(self
, dc
, xoffset
, yoffset
):
155 if self
._op
== DRAWOP_DRAW_LINE
:
156 dc
.DrawLine(self
._x
1 + xoffset
, self
._y
1 + yoffset
, self
._x
2 + xoffset
, self
._y
2 + yoffset
)
157 elif self
._op
== DRAWOP_DRAW_RECT
:
158 dc
.DrawRectangle(self
._x
1 + xoffset
, self
._y
1 + yoffset
, self
._x
2, self
._y
2)
159 elif self
._op
== DRAWOP_DRAW_ROUNDED_RECT
:
160 dc
.DrawRoundedRectangle(self
._x
1 + xoffset
, self
._y
1 + yoffset
, self
._x
2, self
._y
2, self
._radius
)
161 elif self
._op
== DRAWOP_DRAW_ELLIPSE
:
162 dc
.DrawEllipse(self
._x
1 + xoffset
, self
._y
1 + yoffset
, self
._x
2, self
._y
2)
163 elif self
._op
== DRAWOP_DRAW_ARC
:
164 dc
.DrawArc(self
._x
2 + xoffset
, self
._y
2 + yoffset
, self
._x
3 + xoffset
, self
._y
3 + yoffset
, self
._x
1 + xoffset
, self
._y
1 + yoffset
)
165 elif self
._op
== DRAWOP_DRAW_ELLIPTIC_ARC
:
166 dc
.DrawEllipticArc(self
._x
1 + xoffset
, self
._y
1 + yoffset
, self
._x
2, self
._y
2, self
._x
3 * 360 / (2 * math
.pi
), self
.y3
* 360 / (2 * math
.pi
))
167 elif self
._op
== DRAWOP_DRAW_POINT
:
168 dc
.DrawPoint(self
._x
1 + xoffset
, self
._y
1 + yoffset
)
169 elif self
._op
== DRAWOP_DRAW_TEXT
:
170 dc
.DrawText(self
._textString
, self
._x
1 + xoffset
, self
._y
1 + yoffset
)
171 def Scale(self
, scaleX
, scaleY
):
177 if self
._op
!= DRAWOP_DRAW_ELLIPTIC_ARC
:
181 self
._radius
*= scaleX
183 def Translate(self
, x
, y
):
187 if self
._op
== DRAWOP_DRAW_LINE
:
190 elif self
._op
== DRAWOP_DRAW_ARC
:
196 def Rotate(self
, x
, y
, theta
, sinTheta
, cosTheta
):
197 newX1
= self
._x
1 * cosTheta
+ self
._y
1 * sinTheta
+ x
* (1 - cosTheta
) + y
* sinTheta
198 newY1
= self
._x
1 * sinTheta
+ self
._y
1 * cosTheta
+ y
* (1 - cosTheta
) + x
* sinTheta
200 if self
._op
== DRAWOP_DRAW_LINE
:
201 newX2
= self
._x
2 * cosTheta
- self
._y
2 * sinTheta
+ x
* (1 - cosTheta
) + y
* sinTheta
202 newY2
= self
._x
2 * sinTheta
+ self
._y
2 * cosTheta
+ y
* (1 - cosTheta
) + x
* sinTheta
;
209 elif self
._op
in [DRAWOP_DRAW_RECT
, DRAWOP_DRAW_ROUNDED_RECT
, DRAWOP_DRAW_ELLIPTIC_ARC
]:
210 # Assume only 0, 90, 180, 270 degree rotations.
211 # oldX1, oldY1 represents the top left corner. Find the
212 # bottom right, and rotate that. Then the width/height is
213 # the difference between x/y values.
214 oldBottomRightX
= self
._x
1 + self
._x
2
215 oldBottomRightY
= self
._y
1 + self
._y
2
216 newBottomRightX
= oldBottomRightX
* cosTheta
- oldBottomRightY
* sinTheta
+ x
* (1 - cosTheta
) + y
* sinTheta
217 newBottomRightY
= oldBottomRightX
* sinTheta
+ oldBottomRightY
* cosTheta
+ y
* (1 - cosTheta
) + x
* sinTheta
219 # Now find the new top-left, bottom-right coordinates.
220 minX
= min(newX1
, newBottomRightX
)
221 minY
= min(newY1
, newBottomRightY
)
222 maxX
= max(newX1
, newBottomRightX
)
223 maxY
= max(newY1
, newBottomRightY
)
227 self
._x
2 = maxX
- minX
# width
228 self
._y
2 = maxY
- minY
# height
230 if self
._op
== DRAWOP_DRAW_ELLIPTIC_ARC
:
231 # Add rotation to angles
234 elif self
._op
== DRAWOP_DRAW_ARC
:
235 newX2
= self
._x
2 * cosTheta
- self
._y
2 * sinTheta
+ x
* (1 - cosTheta
) + y
* sinTheta
236 newY2
= self
._x
2 * sinTheta
+ self
._y
2 * cosTheta
+ y
* (1 - cosTheta
) + x
* sinTheta
237 newX3
= self
._x
3 * cosTheta
- self
._y
3 * sinTheta
+ x
* (1 - cosTheta
) + y
* sinTheta
238 newY3
= self
._x
3 * sinTheta
+ self
._y
3 * cosTheta
+ y
* (1 - cosTheta
) + x
* sinTheta
248 class OpPolyDraw(DrawOp
):
249 """Draw polygon, polyline, spline."""
250 def __init__(self
, theOp
, thePoints
):
251 DrawOp
.__init
__(self
, theOp
)
253 self
._noPoints
= len(thePoints
)
254 self
._points
= thePoints
256 def Do(self
, dc
, xoffset
, yoffset
):
257 if self
._op
== DRAWOP_DRAW_POLYLINE
:
258 dc
.DrawLines(self
._points
, xoffset
, yoffset
)
259 elif self
._op
== DRAWOP_DRAW_POLYGON
:
260 dc
.DrawPolygon(self
._points
, xoffset
, yoffset
)
261 elif self
._op
== DRAWOP_DRAW_SPLINE
:
262 dc
.DrawSpline(self
._points
) # no offsets in DrawSpline
264 def Scale(self
, scaleX
, scaleY
):
265 for i
in range(self
._noPoints
):
266 self
._points
[i
] = self
._points
[i
][0] * scaleX
, self
._points
[i
][1] * scaleY
268 def Translate(self
, x
, y
):
269 for i
in range(self
._noPoints
):
270 self
._points
[i
][0] += x
271 self
._points
[i
][1] += y
273 def Rotate(self
, x
, y
, theta
, sinTheta
, cosTheta
):
274 for i
in range(self
._noPoints
):
275 x1
= self
._points
[i
][0]
276 y1
= self
._points
[i
][1]
278 self
._points
[i
][0] = x1
* cosTheta
- y1
* sinTheta
+ x
* (1 - cosTheta
) + y
* sinTheta
279 self
._points
[i
][1] = x1
* sinTheta
+ y1
* cosTheta
+ y
* (1 - cosTheta
) + x
* sinTheta
281 def OnDrawOutline(self
, dc
, x
, y
, w
, h
, oldW
, oldH
):
282 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
284 # Multiply all points by proportion of new size to old size
285 x_proportion
= abs(w
/ oldW
)
286 y_proportion
= abs(h
/ oldH
)
288 dc
.DrawPolygon([(x_proportion
* x
, y_proportion
* y
) for x
, y
in self
._points
], x
, y
)
290 def GetPerimeterPoint(self
, x1
, y1
, x2
, y2
, xOffset
, yOffset
, attachmentMode
):
291 # First check for situation where the line is vertical,
292 # and we would want to connect to a point on that vertical --
293 # oglFindEndForPolyline can't cope with this (the arrow
294 # gets drawn to the wrong place).
295 if attachmentMode
== ATTACHMENT_MODE_NONE
and x1
== x2
:
296 # Look for the point we'd be connecting to. This is
298 for point
in self
._points
:
300 if y2
> y1
and point
[1] > 0:
301 return point
[0]+xOffset
, point
[1]+yOffset
302 elif y2
< y1
and point
[1] < 0:
303 return point
[0]+xOffset
, point
[1]+yOffset
305 return FindEndForPolyline([ p
[0] + xOffset
for p
in self
._points
],
306 [ p
[1] + yOffset
for p
in self
._points
],
310 class PseudoMetaFile(object):
312 A simple metafile-like class which can load data from a Windows
313 metafile on all platforms.
316 self
._currentRotation
= 0
317 self
._rotateable
= True
320 self
._outlinePen
= None
321 self
._fillBrush
= None
325 self
._gdiObjects
= []
327 self
._outlineColours
= []
328 self
._fillColours
= []
332 self
._gdiObjects
= []
333 self
._outlineColours
= []
334 self
._fillColours
= []
335 self
._outlineColours
= -1
338 return self
._ops
!= []
343 def SetOutlineOp(self
, op
):
346 def GetOutlineOp(self
):
347 return self
._outlineOp
349 def SetOutlinePen(self
, pen
):
350 self
._outlinePen
= pen
352 def GetOutlinePen(self
, pen
):
353 return self
._outlinePen
355 def SetFillBrush(self
, brush
):
356 self
._fillBrush
= brush
358 def GetFillBrush(self
):
359 return self
._fillBrush
361 def SetSize(self
, w
, h
):
365 def SetRotateable(self
, rot
):
366 self
._rotateable
= rot
368 def GetRotateable(self
):
369 return self
._rotateable
371 def GetFillColours(self
):
372 return self
._fillColours
374 def GetOutlineColours(self
):
375 return self
._outlineColours
377 def Draw(self
, dc
, xoffset
, yoffset
):
379 op
.Do(dc
, xoffset
, yoffset
)
381 def Scale(self
, sx
, sy
):
388 def Translate(self
, x
, y
):
392 def Rotate(self
, x
, y
, theta
):
393 theta1
= theta
- self
._currentRotation
397 cosTheta
= cos(theta1
)
398 sinTheta
= sin(theta1
)
401 op
.Rotate(x
, y
, theta
, sinTheta
, cosTheta
)
403 self
._currentRotation
= theta
405 def LoadFromMetaFile(self
, filename
, rwidth
, rheight
):
406 if not os
.path
.exist(filename
):
409 print "LoadFromMetaFile not implemented yet."
413 def ScaleTo(self
, w
, h
):
414 scaleX
= w
/ self
._width
415 scaleY
= h
/ self
._height
417 self
.Scale(scaleX
, scaleY
)
420 maxX
, maxY
, minX
, minY
= -99999.9, -99999.9, 99999.9, 99999.9
423 if op
.GetOp() in [DRAWOP_DRAW_LINE
, DRAWOP_DRAW_RECT
, DRAWOP_DRAW_ROUNDED_RECT
, DRAWOP_DRAW_ELLIPSE
, DRAWOP_DRAW_POINT
, DRAWOP_DRAW_TEXT
]:
432 if op
.GetOp() == DRAWOP_DRAW_LINE
:
441 elif op
.GetOp() in [ DRAWOP_DRAW_RECT
, DRAWOP_DRAW_ROUNDED_RECT
, DRAWOP_DRAW_ELLIPSE
]:
442 if op
._x
1 + op
._x
2 < minX
:
443 minX
= op
._x
1 + op
._x
2
444 if op
._x
1 + op
._x
2 > maxX
:
445 maxX
= op
._x
1 + op
._x
2
446 if op
._y
1 + op
._y
2 < minY
:
447 minY
= op
._y
1 + op
._y
2
448 if op
._y
1 + op
._y
2 > maxX
:
449 maxY
= op
._y
1 + op
._y
2
450 elif op
.GetOp() == DRAWOP_DRAW_ARC
:
451 # TODO: don't yet know how to calculate the bounding box
452 # for an arc. So pretend it's a line; to get a correct
453 # bounding box, draw a blank rectangle first, of the
471 elif op
.GetOp() in [DRAWOP_DRAW_POLYLINE
, DRAWOP_DRAW_POLYGON
, DRAWOP_DRAW_SPLINE
]:
472 for point
in op
._points
:
482 return [minX
, minY
, maxX
, maxY
]
484 # Calculate size from current operations
485 def CalculateSize(self
, shape
):
486 boundMinX
, boundMinY
, boundMaxX
, boundMaxY
= self
.GetBounds()
488 # By Pierre Hjälm: This is NOT in the old version, which
489 # gets this totally wrong. Since the drawing is centered, we
490 # cannot get the width by measuring from left to right, we
491 # must instead make enough room to handle the largest
493 #self.SetSize(boundMaxX - boundMinX, boundMaxY - boundMinY)
495 w
= max(abs(boundMinX
), abs(boundMaxX
)) * 2
496 h
= max(abs(boundMinY
), abs(boundMaxY
)) * 2
501 shape
.SetWidth(self
._width
)
502 shape
.SetHeight(self
._height
)
504 # Set of functions for drawing into a pseudo metafile
505 def DrawLine(self
, pt1
, pt2
):
506 op
= OpDraw(DRAWOP_DRAW_LINE
, pt1
[0], pt1
[1], pt2
[0], pt2
[1])
509 def DrawRectangle(self
, rect
):
510 op
= OpDraw(DRAWOP_DRAW_RECT
, rect
[0], rect
[1], rect
[2], rect
[3])
513 def DrawRoundedRectangle(self
, rect
, radius
):
514 op
= OpDraw(DRAWOP_DRAW_ROUNDED_RECT
, rect
[0], rect
[1], rect
[2], rect
[3])
518 def DrawEllipse(self
, rect
):
519 op
= OpDraw(DRAWOP_DRAW_ELLIPSE
, rect
[0], rect
[1], rect
[2], rect
[3])
522 def DrawArc(self
, centrePt
, startPt
, endPt
):
523 op
= OpDraw(DRAWOP_DRAW_ARC
, centrePt
[0], centrePt
[1], startPt
[0], startPt
[1])
524 op
._x
3, op
._y
3 = endPt
528 def DrawEllipticArc(self
, rect
, startAngle
, endAngle
):
529 startAngleRadians
= startAngle
* math
.pi
* 2 / 360
530 endAngleRadians
= endAngle
* math
.pi
* 2 / 360
532 op
= OpDraw(DRAWOP_DRAW_ELLIPTIC_ARC
, rect
[0], rect
[1], rect
[2], rect
[3])
533 op
._x
3 = startAngleRadians
534 op
._y
3 = endAngleRadians
538 def DrawPoint(self
, pt
):
539 op
= OpDraw(DRAWOP_DRAW_POINT
, pt
[0], pt
[1], 0, 0)
542 def DrawText(self
, text
, pt
):
543 op
= OpDraw(DRAWOP_DRAW_TEXT
, pt
[0], pt
[1], 0, 0)
544 op
._textString
= text
547 def DrawLines(self
, pts
):
548 op
= OpPolyDraw(DRAWOP_DRAW_POLYLINE
, pts
)
552 # oglMETAFLAGS_OUTLINE: will be used for drawing the outline and
553 # also drawing lines/arrows at the circumference.
554 # oglMETAFLAGS_ATTACHMENTS: will be used for initialising attachment
555 # points at the vertices (perhaps a rare case...)
556 def DrawPolygon(self
, pts
, flags
= 0):
557 op
= OpPolyDraw(DRAWOP_DRAW_POLYGON
, pts
)
560 if flags
& METAFLAGS_OUTLINE
:
561 self
._outlineOp
= len(self
._ops
) - 1
563 def DrawSpline(self
, pts
):
564 op
= OpPolyDraw(DRAWOP_DRAW_SPLINE
, pts
)
567 def SetClippingRect(self
, rect
):
568 OpSetClipping(DRAWOP_SET_CLIPPING_RECT
, rect
[0], rect
[1], rect
[2], rect
[3])
570 def DestroyClippingRect(self
):
571 op
= OpSetClipping(DRAWOP_DESTROY_CLIPPING_RECT
, 0, 0, 0, 0)
574 def SetPen(self
, pen
, isOutline
= False):
575 self
._gdiObjects
.append(pen
)
576 op
= OpSetGDI(DRAWOP_SET_PEN
, self
, len(self
._gdiObjects
) - 1)
580 self
._outlineColours
.append(len(self
._gdiObjects
) - 1)
582 def SetBrush(self
, brush
, isFill
= False):
583 self
._gdiObjects
.append(brush
)
584 op
= OpSetGDI(DRAWOP_SET_BRUSH
, self
, len(self
._gdiObjects
) - 1)
588 self
._fillColours
.append(len(self
._gdiObjects
) - 1)
590 def SetFont(self
, font
):
591 self
._gdiObjects
.append(font
)
592 op
= OpSetGDI(DRAWOP_SET_FONT
, self
, len(self
._gdiObjects
) - 1)
595 def SetTextColour(self
, colour
):
596 op
= OpSetGDI(DRAWOP_SET_TEXT_COLOUR
, self
, 0)
597 op
._r
, op
._g
, op
._b
= colour
.Red(), colour
.Green(), colour
.Blue()
601 def SetBackgroundColour(self
, colour
):
602 op
= OpSetGDI(DRAWOP_SET_BK_COLOUR
, self
, 0)
603 op
._r
, op
._g
, op
._b
= colour
.Red(), colour
.Green(), colour
.Blue()
607 def SetBackgroundMode(self
, mode
):
608 op
= OpSetGDI(DRAWOP_SET_BK_MODE
, self
, 0)
611 class DrawnShape(RectangleShape
):
613 Draws a pseudo-metafile shape, which can be loaded from a simple
616 wxDrawnShape allows you to specify a different shape for each of four
617 orientations (North, West, South and East). It also provides a set of
618 drawing functions for programmatic drawing of a shape, so that during
619 construction of the shape you can draw into it as if it were a device
626 RectangleShape
.__init
__(self
, 100, 50)
627 self
._saveToFile
= True
628 self
._currentAngle
= DRAWN_ANGLE_0
630 self
._metafiles
=PseudoMetaFile(), PseudoMetaFile(), PseudoMetaFile(), PseudoMetaFile()
632 def OnDraw(self
, dc
):
633 # Pass pen and brush in case we have force outline
635 if self
._shadowMode
!= SHADOW_NONE
:
636 if self
._shadowBrush
:
637 self
._metafiles
[self
._currentAngle
]._fillBrush
= self
._shadowBrush
638 self
._metafiles
[self
._currentAngle
]._outlinePen
= wx
.Pen(wx
.WHITE
, 1, wx
.TRANSPARENT
)
639 self
._metafiles
[self
._currentAngle
].Draw(dc
, self
._xpos
+ self
._shadowOffsetX
, self
._ypos
+ self
._shadowOffsetY
)
641 self
._metafiles
[self
._currentAngle
]._outlinePen
= self
._pen
642 self
._metafiles
[self
._currentAngle
]._fillBrush
= self
._brush
643 self
._metafiles
[self
._currentAngle
].Draw(dc
, self
._xpos
, self
._ypos
)
645 def SetSize(self
, w
, h
, recursive
= True):
646 self
.SetAttachmentSize(w
, h
)
648 if self
.GetWidth() == 0.0:
651 scaleX
= w
/ self
.GetWidth()
653 if self
.GetHeight() == 0.0:
656 scaleY
= h
/ self
.GetHeight()
659 if self
._metafiles
[i
].IsValid():
660 self
._metafiles
[i
].Scale(scaleX
, scaleY
)
664 self
.SetDefaultRegionSize()
666 def Scale(self
, sx
, sy
):
667 """Scale the shape by the given amount."""
669 if self
._metafiles
[i
].IsValid():
670 self
._metafiles
[i
].Scale(sx
, sy
)
671 self
._metafiles
[i
].CalculateSize(self
)
673 def Translate(self
, x
, y
):
674 """Translate the shape by the given amount."""
676 if self
._metafiles
[i
].IsValid():
677 self
._metafiles
[i
].Translate(x
, y
)
678 self
._metafiles
[i
].CalculateSize(self
)
680 # theta is absolute rotation from the zero position
681 def Rotate(self
, x
, y
, theta
):
682 """Rotate about the given axis by the given amount in radians."""
683 self
._currentAngle
= self
.DetermineMetaFile(theta
)
685 if self
._currentAngle
== 0:
687 if not self
._metafiles
[0].GetRotateable():
690 self
._metafiles
[0].Rotate(x
, y
, theta
)
692 actualTheta
= theta
- self
._rotation
694 # Rotate attachment points
695 sinTheta
= math
.sin(actualTheta
)
696 cosTheta
= math
.cos(actualTheta
)
698 for point
in self
._attachmentPoints
:
702 point
._x
= x1
* cosTheta
- y1
* sinTheta
+ x
* (1.0 - cosTheta
) + y
* sinTheta
703 point
._y
= x1
* sinTheta
+ y1
* cosTheta
+ y
* (1.0 - cosTheta
) + x
* sinTheta
705 self
._rotation
= theta
707 self
._metafiles
[self
._currentAngle
].CalculateSize(self
)
709 # Which metafile do we use now? Based on current rotation and validity
711 def DetermineMetaFile(self
, rotation
):
713 angles
= [0.0, math
.pi
/ 2, math
.pi
, 3 * pi
/ 2]
718 if RoughlyEqual(rotation
, angles
[i
], tolerance
):
722 if whichMetaFile
> 0 and not self
._metafiles
[whichMetaFile
].IsValid():
727 def OnDrawOutline(self
, dc
, x
, y
, w
, h
):
728 if self
._metafiles
[self
._currentAngle
].GetOutlineOp() != -1:
729 op
= self
._metafiles
[self
._currentAngle
].GetOps()[self
._metafiles
[self
._currentAngle
].GetOutlineOp()]
730 if op
.OnDrawOutline(dc
, x
, y
, w
, h
, self
._width
, self
._height
):
733 # Default... just use a rectangle
734 RectangleShape
.OnDrawOutline(self
, dc
, x
, y
, w
, h
)
736 # Get the perimeter point using the special outline op, if there is one,
737 # otherwise use default wxRectangleShape scheme
738 def GetPerimeterPoint(self
, x1
, y1
, x2
, y2
):
739 if self
._metafiles
[self
._currentAngle
].GetOutlineOp() != -1:
740 op
= self
._metafiles
[self
._currentAngle
].GetOps()[self
._metafiles
[self
._currentAngle
].GetOutlineOp()]
741 p
= op
.GetPerimeterPoint(x1
, y1
, x2
, y2
, self
.GetX(), self
.GetY(), self
.GetAttachmentMode())
745 return RectangleShape
.GetPerimeterPoint(self
, x1
, y1
, x2
, y2
)
747 def LoadFromMetaFile(self
, filename
):
748 """Load a (very simple) Windows metafile, created for example by
749 Top Draw, the Windows shareware graphics package."""
750 return self
._metafiles
[0].LoadFromMetaFile(filename
)
752 # Set of functions for drawing into a pseudo metafile.
753 # They use integers, but doubles are used internally for accuracy
755 def DrawLine(self
, pt1
, pt2
):
756 self
._metafiles
[self
._currentAngle
].DrawLine(pt1
, pt2
)
758 def DrawRectangle(self
, rect
):
759 self
._metafiles
[self
._currentAngle
].DrawRectangle(rect
)
761 def DrawRoundedRectangle(self
, rect
, radius
):
762 """Draw a rounded rectangle.
764 radius is the corner radius. If radius is negative, it expresses
765 the radius as a proportion of the smallest dimension of the rectangle.
767 self
._metafiles
[self
._currentAngle
].DrawRoundedRectangle(rect
, radius
)
769 def DrawEllipse(self
, rect
):
770 self
._metafiles
[self
._currentAngle
].DrawEllipse(rect
)
772 def DrawArc(self
, centrePt
, startPt
, endPt
):
774 self
._metafiles
[self
._currentAngle
].DrawArc(centrePt
, startPt
, endPt
)
776 def DrawEllipticArc(self
, rect
, startAngle
, endAngle
):
777 """Draw an elliptic arc."""
778 self
._metafiles
[self
._currentAngle
].DrawEllipticArc(rect
, startAngle
, endAngle
)
780 def DrawPoint(self
, pt
):
781 self
._metafiles
[self
._currentAngle
].DrawPoint(pt
)
783 def DrawText(self
, text
, pt
):
784 self
._metafiles
[self
._currentAngle
].DrawText(text
, pt
)
786 def DrawLines(self
, pts
):
787 self
._metafiles
[self
._currentAngle
].DrawLines(pts
)
789 def DrawPolygon(self
, pts
, flags
= 0):
792 flags can be one or more of:
793 METAFLAGS_OUTLINE (use this polygon for the drag outline) and
794 METAFLAGS_ATTACHMENTS (use the vertices of this polygon for attachments).
796 if flags
and METAFLAGS_ATTACHMENTS
:
797 self
.ClearAttachments()
798 for i
in range(len(pts
)):
799 self
._attachmentPoints
.append(AttachmentPoint(i
,pts
[i
][0],pts
[i
][1]))
800 self
._metafiles
[self
._currentAngle
].DrawPolygon(pts
, flags
)
802 def DrawSpline(self
, pts
):
803 self
._metafiles
[self
._currentAngle
].DrawSpline(pts
)
805 def SetClippingRect(self
, rect
):
806 """Set the clipping rectangle."""
807 self
._metafiles
[self
._currentAngle
].SetClippingRect(rect
)
809 def DestroyClippingRect(self
):
810 """Destroy the clipping rectangle."""
811 self
._metafiles
[self
._currentAngle
].DestroyClippingRect()
813 def SetDrawnPen(self
, pen
, isOutline
= False):
814 """Set the pen for this metafile.
816 If isOutline is True, this pen is taken to indicate the outline
817 (and if the outline pen is changed for the whole shape, the pen
818 will be replaced with the outline pen).
820 self
._metafiles
[self
._currentAngle
].SetPen(pen
, isOutline
)
822 def SetDrawnBrush(self
, brush
, isFill
= False):
823 """Set the brush for this metafile.
825 If isFill is True, the brush is used as the fill brush.
827 self
._metafiles
[self
._currentAngle
].SetBrush(brush
, isFill
)
829 def SetDrawnFont(self
, font
):
830 self
._metafiles
[self
._currentAngle
].SetFont(font
)
832 def SetDrawnTextColour(self
, colour
):
833 """Set the current text colour for the current metafile."""
834 self
._metafiles
[self
._currentAngle
].SetTextColour(colour
)
836 def SetDrawnBackgroundColour(self
, colour
):
837 """Set the current background colour for the current metafile."""
838 self
._metafiles
[self
._currentAngle
].SetBackgroundColour(colour
)
840 def SetDrawnBackgroundMode(self
, mode
):
841 """Set the current background mode for the current metafile."""
842 self
._metafiles
[self
._currentAngle
].SetBackgroundMode(mode
)
844 def CalculateSize(self
):
845 """Calculate the wxDrawnShape size from the current metafile.
847 Call this after you have drawn into the shape.
849 self
._metafiles
[self
._currentAngle
].CalculateSize(self
)
851 def DrawAtAngle(self
, angle
):
852 """Set the metafile for the given orientation, which can be one of:
859 self
._currentAngle
= angle
862 """Return the current orientation, which can be one of:
869 return self
._currentAngle
871 def GetRotation(self
):
872 """Return the current rotation of the shape in radians."""
873 return self
._rotation
875 def SetSaveToFile(self
, save
):
876 """If save is True, the image will be saved along with the shape's
877 other attributes. The reason why this might not be desirable is that
878 if there are many shapes with the same image, it would be more
879 efficient for the application to save one copy, and not duplicate
880 the information for every shape. The default is True.
882 self
._saveToFile
= save
884 def GetMetaFile(self
, which
= 0):
885 """Return a reference to the internal 'pseudo-metafile'."""
886 return self
._metafiles
[which
]