Advertisement
here2share

# PIDDLE.py

Nov 18th, 2019
279
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 25.93 KB | None | 0 0
  1. # PIDDLE.py
  2.  
  3. """
  4. PIDDLE (Plug-In Drawing, Does Little Else)
  5. 2D Plug-In Drawing System
  6. By...
  7. Magnus Lie Hetland
  8. Andy Robinson
  9. Joseph J. Strout
  10. and others
  11. """
  12.  
  13. import sys
  14. import string
  15.  
  16. inch = 72        # 1 PIDDLE drawing unit == 1/72 imperial inch
  17. cm = inch/2.54   # more sensible measurement unit
  18.  
  19. #-------------------------------------------------------------------------
  20. # StateSaver
  21. #-------------------------------------------------------------------------
  22. class StateSaver(object):
  23.  
  24.     def __init__(self, canvas):
  25.         self.canvas = canvas
  26.         self.defaultLineColor = canvas.defaultLineColor
  27.         self.defaultFillColor = canvas.defaultFillColor
  28.         self.defaultLineWidth = canvas.defaultLineWidth
  29.         self.defaultFont = canvas.defaultFont
  30.  
  31.     def __del__(self):
  32.         self.canvas.defaultLineColor = self.defaultLineColor
  33.         self.canvas.defaultFillColor = self.defaultFillColor
  34.         self.canvas.defaultLineWidth = self.defaultLineWidth
  35.         self.canvas.defaultFont = self.defaultFont
  36.  
  37. #-------------------------------------------------------------------------
  38. # Color
  39. #-------------------------------------------------------------------------
  40. class Color(object):
  41.     """This class is used to represent color.  Components red, green, blue
  42.    are in the range 0 (dark) to 1 (full intensity)."""
  43.  
  44.     def __init__(self, red=0, green=0, blue=0):
  45.         "Initialize with red, green, blue in range [0-1]."
  46.         _float = float
  47.         d = self.__dict__
  48.         d["red"] = _float(red)
  49.         d["green"] = _float(green)
  50.         d["blue"] = _float(blue)
  51.  
  52.     def __setattr__(self, name, value):
  53.         raise TypeError("piddle.Color has read-only attributes")
  54.  
  55.     def __mul__(self,x):
  56.         return Color(self.red*x, self.green*x, self.blue*x)
  57.  
  58.     def __rmul__(self,x):
  59.         return Color(self.red*x, self.green*x, self.blue*x)
  60.  
  61.     def __div__(self,x):
  62.         return Color(self.red/x, self.green/x, self.blue/x)
  63.  
  64.     def __rdiv__(self,x):
  65.         return Color(self.red/x, self.green/x, self.blue/x)
  66.  
  67.     def __add__(self,x):
  68.         return Color(self.red+x.red, self.green+x.green, self.blue+x.blue)
  69.  
  70.     def __sub__(self,x):
  71.         return Color(self.red-x.red, self.green-x.green, self.blue-x.blue)
  72.  
  73.     def __repr__(self):
  74.         return "Color(%1.2f,%1.2f,%1.2f)" % (self.red, self.green, self.blue)
  75.  
  76.     def __hash__(self):
  77.         return hash( (self.red, self.green, self.blue) )
  78.  
  79.     def __cmp__(self,other):
  80.         try:
  81.             dsum = 4*self.red-4*other.red + 2*self.green-2*other.green + self.blue-other.blue
  82.         except:
  83.             return -1
  84.         if dsum > 0: return 1
  85.         if dsum < 0: return -1
  86.         return 0
  87.  
  88. def myisstring(obj):
  89.     if sys.version_info[0] > 2:
  90.         return isinstance(obj, str)
  91.     else:
  92.         return isinstance(obj, basestring)
  93.  
  94. def HexColor(val):
  95.     """This class converts a hex string, or an actual integer number,
  96.    into the corresponding color.  E.g., in "AABBCC" or 0xAABBCC,
  97.    AA is the red, BB is the green, and CC is the blue (00-FF)."""
  98.     if myisstring(val):
  99.         val = string.atoi(val,16)
  100.     factor = 1.0 / 255
  101.     return Color(factor * ((val >> 16) & 0xFF),
  102.                  factor * ((val >> 8) & 0xFF),
  103.                  factor * (val & 0xFF))
  104.  
  105. # color constants -- mostly from HTML standard
  106. aliceblue =            HexColor(0xF0F8FF)
  107. antiquewhite =         HexColor(0xFAEBD7)
  108. aqua =                 HexColor(0x00FFFF)
  109. aquamarine =           HexColor(0x7FFFD4)
  110. azure =                HexColor(0xF0FFFF)
  111. beige =                HexColor(0xF5F5DC)
  112. bisque =               HexColor(0xFFE4C4)
  113. black =                HexColor(0x000000)
  114. blanchedalmond =       HexColor(0xFFEBCD)
  115. blue =                 HexColor(0x0000FF)
  116. blueviolet =           HexColor(0x8A2BE2)
  117. brown =                HexColor(0xA52A2A)
  118. burlywood =            HexColor(0xDEB887)
  119. cadetblue =            HexColor(0x5F9EA0)
  120. chartreuse =           HexColor(0x7FFF00)
  121. chocolate =            HexColor(0xD2691E)
  122. coral =                HexColor(0xFF7F50)
  123. cornflower =           HexColor(0x6495ED)
  124. cornsilk =             HexColor(0xFFF8DC)
  125. crimson =              HexColor(0xDC143C)
  126. cyan =                 HexColor(0x00FFFF)
  127. darkblue =             HexColor(0x00008B)
  128. darkcyan =             HexColor(0x008B8B)
  129. darkgoldenrod =        HexColor(0xB8860B)
  130. darkgray =             HexColor(0xA9A9A9)
  131. darkgreen =            HexColor(0x006400)
  132. darkkhaki =            HexColor(0xBDB76B)
  133. darkmagenta =          HexColor(0x8B008B)
  134. darkolivegreen =       HexColor(0x556B2F)
  135. darkorange =           HexColor(0xFF8C00)
  136. darkorchid =           HexColor(0x9932CC)
  137. darkred =              HexColor(0x8B0000)
  138. darksalmon =           HexColor(0xE9967A)
  139. darkseagreen =         HexColor(0x8FBC8B)
  140. darkslateblue =        HexColor(0x483D8B)
  141. darkslategray =        HexColor(0x2F4F4F)
  142. darkturquoise =        HexColor(0x00CED1)
  143. darkviolet =           HexColor(0x9400D3)
  144. deeppink =             HexColor(0xFF1493)
  145. deepskyblue =          HexColor(0x00BFFF)
  146. dimgray =              HexColor(0x696969)
  147. dodgerblue =           HexColor(0x1E90FF)
  148. firebrick =            HexColor(0xB22222)
  149. floralwhite =          HexColor(0xFFFAF0)
  150. forestgreen =          HexColor(0x228B22)
  151. fuchsia =              HexColor(0xFF00FF)
  152. gainsboro =            HexColor(0xDCDCDC)
  153. ghostwhite =           HexColor(0xF8F8FF)
  154. gold =                 HexColor(0xFFD700)
  155. goldenrod =            HexColor(0xDAA520)
  156. gray =                 HexColor(0x808080)
  157. grey =                 gray
  158. green =                HexColor(0x008000)
  159. greenyellow =          HexColor(0xADFF2F)
  160. honeydew =             HexColor(0xF0FFF0)
  161. hotpink =              HexColor(0xFF69B4)
  162. indianred =            HexColor(0xCD5C5C)
  163. indigo =               HexColor(0x4B0082)
  164. ivory =                HexColor(0xFFFFF0)
  165. khaki =                HexColor(0xF0E68C)
  166. lavender =             HexColor(0xE6E6FA)
  167. lavenderblush =        HexColor(0xFFF0F5)
  168. lawngreen =            HexColor(0x7CFC00)
  169. lemonchiffon =         HexColor(0xFFFACD)
  170. lightblue =            HexColor(0xADD8E6)
  171. lightcoral =           HexColor(0xF08080)
  172. lightcyan =            HexColor(0xE0FFFF)
  173. lightgoldenrodyellow = HexColor(0xFAFAD2)
  174. lightgreen =           HexColor(0x90EE90)
  175. lightgrey =            HexColor(0xD3D3D3)
  176. lightpink =            HexColor(0xFFB6C1)
  177. lightsalmon =          HexColor(0xFFA07A)
  178. lightseagreen =        HexColor(0x20B2AA)
  179. lightskyblue =         HexColor(0x87CEFA)
  180. lightslategray =       HexColor(0x778899)
  181. lightsteelblue =       HexColor(0xB0C4DE)
  182. lightyellow =          HexColor(0xFFFFE0)
  183. lime =                 HexColor(0x00FF00)
  184. limegreen =            HexColor(0x32CD32)
  185. linen =                HexColor(0xFAF0E6)
  186. magenta =              HexColor(0xFF00FF)
  187. maroon =               HexColor(0x800000)
  188. mediumaquamarine =     HexColor(0x66CDAA)
  189. mediumblue =           HexColor(0x0000CD)
  190. mediumorchid =         HexColor(0xBA55D3)
  191. mediumpurple =         HexColor(0x9370DB)
  192. mediumseagreen =       HexColor(0x3CB371)
  193. mediumslateblue =      HexColor(0x7B68EE)
  194. mediumspringgreen =    HexColor(0x00FA9A)
  195. mediumturquoise =      HexColor(0x48D1CC)
  196. mediumvioletred =      HexColor(0xC71585)
  197. midnightblue =         HexColor(0x191970)
  198. mintcream =            HexColor(0xF5FFFA)
  199. mistyrose =            HexColor(0xFFE4E1)
  200. moccasin =             HexColor(0xFFE4B5)
  201. navajowhite =          HexColor(0xFFDEAD)
  202. navy =                 HexColor(0x000080)
  203. oldlace =              HexColor(0xFDF5E6)
  204. olive =                HexColor(0x808000)
  205. olivedrab =            HexColor(0x6B8E23)
  206. orange =               HexColor(0xFFA500)
  207. orangered =            HexColor(0xFF4500)
  208. orchid =               HexColor(0xDA70D6)
  209. palegoldenrod =        HexColor(0xEEE8AA)
  210. palegreen =            HexColor(0x98FB98)
  211. paleturquoise =        HexColor(0xAFEEEE)
  212. palevioletred =        HexColor(0xDB7093)
  213. papayawhip =           HexColor(0xFFEFD5)
  214. peachpuff =            HexColor(0xFFDAB9)
  215. peru =                 HexColor(0xCD853F)
  216. pink =                 HexColor(0xFFC0CB)
  217. plum =                 HexColor(0xDDA0DD)
  218. powderblue =           HexColor(0xB0E0E6)
  219. purple =               HexColor(0x800080)
  220. red =                  HexColor(0xFF0000)
  221. rosybrown =            HexColor(0xBC8F8F)
  222. royalblue =            HexColor(0x4169E1)
  223. saddlebrown =          HexColor(0x8B4513)
  224. salmon =               HexColor(0xFA8072)
  225. sandybrown =           HexColor(0xF4A460)
  226. seagreen =             HexColor(0x2E8B57)
  227. seashell =             HexColor(0xFFF5EE)
  228. sienna =               HexColor(0xA0522D)
  229. silver =               HexColor(0xC0C0C0)
  230. skyblue =              HexColor(0x87CEEB)
  231. slateblue =            HexColor(0x6A5ACD)
  232. slategray =            HexColor(0x708090)
  233. snow =                 HexColor(0xFFFAFA)
  234. springgreen =          HexColor(0x00FF7F)
  235. steelblue =            HexColor(0x4682B4)
  236. tan =                  HexColor(0xD2B48C)
  237. teal =                 HexColor(0x008080)
  238. thistle =              HexColor(0xD8BFD8)
  239. tomato =               HexColor(0xFF6347)
  240. turquoise =            HexColor(0x40E0D0)
  241. violet =               HexColor(0xEE82EE)
  242. wheat =                HexColor(0xF5DEB3)
  243. white =                HexColor(0xFFFFFF)
  244. whitesmoke =           HexColor(0xF5F5F5)
  245. yellow =               HexColor(0xFFFF00)
  246. yellowgreen =          HexColor(0x9ACD32)
  247.  
  248. # special case -- indicates no drawing should be done
  249. transparent = Color(-1, -1, -1)
  250.  
  251. #-------------------------------------------------------------------------
  252. # Font
  253. #-------------------------------------------------------------------------
  254. class Font(object):
  255.     "This class represents font typeface, size, and style."
  256.  
  257.     def __init__(self, size=12, bold=0, italic=0, underline=0, face=None):
  258.         # public mode variables
  259.         d = self.__dict__
  260.         d["bold"] = bold
  261.         d["italic"] = italic
  262.         d["underline"] = underline
  263.  
  264.         # public font size (points)
  265.         d["size"] = size
  266.  
  267.         # typeface -- a name or set of names, interpreted by the Canvas,
  268.         # or "None" to indicate the Canvas-specific default typeface
  269.         d["face"] = face
  270.  
  271.     def __cmp__(self, other):
  272.         """Compare two fonts to see if they're the same."""
  273.         if self.face == other.face and self.size == other.size and \
  274.            self.bold == other.bold and self.italic == other.italic \
  275.            and self.underline == other.underline:
  276.             return 0
  277.         else:
  278.             return 1
  279.  
  280.     def __repr__(self):
  281.         return "Font(%d,%d,%d,%d,%s)" % (self.size, self.bold, self.italic, \
  282.                                          self.underline, repr(self.face))
  283.  
  284.     def __setattr__(self, name, value):
  285.         raise TypeError("piddle.Font has read-only attributes")
  286.  
  287.  
  288. #-------------------------------------------------------------------------
  289. # constants needed for Canvas.drawFigure
  290. #-------------------------------------------------------------------------
  291. figureLine = 1
  292. figureArc = 2
  293. figureCurve = 3
  294.  
  295. #-------------------------------------------------------------------------
  296. # key constants used for special keys in the onKey callback
  297. #-------------------------------------------------------------------------
  298. keyBksp = '\010'    # (erases char to left of cursor)
  299. keyDel = '\177'     # (erases char to right of cursor)
  300. keyLeft = '\034'
  301. keyRight = '\035'
  302. keyUp = '\036'
  303. keyDown = '\037'
  304. keyPgUp = '\013'
  305. keyPgDn = '\014'
  306. keyHome = '\001'
  307. keyEnd = '\004'
  308. keyClear = '\033'
  309. keyTab = '\t'
  310.  
  311. modShift = 1        # shift key was also held
  312. modControl = 2      # control key was also held
  313.  
  314. #-------------------------------------------------------------------------
  315. # Canvas
  316. #-------------------------------------------------------------------------
  317. class Canvas(object):
  318.     """This is the base class for a drawing canvas.  The 'plug-in renderer'
  319.    we speak of are really just classes derived from this one, which implement
  320.    the various drawing methods."""
  321.  
  322.     def __init__(self, size=(300,300), name="PIDDLE"):
  323.         """Initialize the canvas, and set default drawing parameters.
  324.        Derived classes should be sure to call this method."""
  325.         # defaults used when drawing
  326.         self.defaultLineColor = black
  327.         self.defaultFillColor = transparent
  328.         self.defaultLineWidth = 1
  329.         self.defaultFont = Font()
  330.  
  331.         # set up null event handlers
  332.  
  333.         # onClick: x,y is Canvas coordinates of mouseclick
  334.         def ignoreClick(canvas,x,y): pass
  335.         self.onClick = ignoreClick
  336.  
  337.         # onOver: x,y is Canvas location of mouse
  338.         def ignoreOver(canvas,x,y): pass
  339.         self.onOver = ignoreOver
  340.  
  341.         # onKey: key is printable character or one of the constants above;
  342.         #    modifiers is a tuple containing any of (modShift, modControl)
  343.         def ignoreKey(canvas,key,modifiers): pass
  344.         self.onKey = ignoreKey
  345.  
  346.         # size and name, for user's reference
  347.         self.size, self.name = size,name
  348.  
  349.     #------------ canvas capabilities -------------
  350.     def isInteractive(self):
  351.         "Returns 1 if onClick, onOver, and onKey events are possible, 0 otherwise."
  352.         return 0
  353.  
  354.     def canUpdate(self):
  355.         "Returns 1 if the drawing can be meaningfully updated over time \
  356.        (e.g., screen graphics), 0 otherwise (e.g., drawing to a file)."
  357.         return 0
  358.  
  359.     #------------ general management -------------
  360.     def clear(self):
  361.         "Call this to clear and reset the graphics context."
  362.         pass
  363.  
  364.     def flush(self):
  365.         "Call this to indicate that any commands that have been issued \
  366.                but which might be buffered should be flushed to the screen"
  367.         pass
  368.  
  369.         def save(self, file=None, format=None):
  370.  
  371.                 """For backends that can be save to a file or sent to a
  372.                stream, create a valid file out of what's currently been
  373.                drawn on the canvas.  Trigger any finalization here.
  374.                Though some backends may allow further drawing after this call,
  375.                presume that this is not possible for maximum portability
  376.                file may be either a string or a file object with a write method
  377.                     if left as the default, the canvas's current name will be used
  378.                format may be used to specify the type of file format to use as
  379.                     well as any corresponding extension to use for the filename
  380.                     This is an optional argument and backends may ignore it if
  381.                     they only produce one file format."""
  382.                 pass
  383.  
  384.  
  385.  
  386.     def setInfoLine(self, s):
  387.         "For interactive Canvases, displays the given string in the \
  388.        'info line' somewhere where the user can probably see it."
  389.         pass
  390.  
  391.     #------------ string/font info ------------
  392.     def stringWidth(self, s, font=None):
  393.         "Return the logical width of the string if it were drawn \
  394.        in the current font (defaults to self.font)."
  395.         raise NotImplementedError('stringWidth')
  396.  
  397.     def fontHeight(self, font=None):
  398.         "Find the height of one line of text (baseline to baseline) of the given font."
  399.         # the following approximation is correct for PostScript fonts,
  400.         # and should be close for most others:
  401.         if not font: font = self.defaultFont
  402.         return 1.2 * font.size
  403.  
  404.     def fontAscent(self, font=None):
  405.         "Find the ascent (height above base) of the given font."
  406.         raise NotImplementedError('fontAscent')
  407.  
  408.     def fontDescent(self, font=None):
  409.         "Find the descent (extent below base) of the given font."
  410.         raise NotImplementedError('fontDescent')
  411.  
  412.     #------------- drawing helpers --------------
  413.  
  414.     def arcPoints(self, x1,y1, x2,y2, startAng=0, extent=360):
  415.         "Return a list of points approximating the given arc."
  416.         # Note: this implementation is simple and not particularly efficient.
  417.         xScale = abs((x2-x1)/2.0)
  418.         yScale = abs((y2-y1)/2.0)
  419.  
  420.         x = min(x1,x2)+xScale
  421.         y = min(y1,y2)+yScale
  422.  
  423.         # "Guesstimate" a proper number of points for the arc:
  424.         steps = min(max(xScale,yScale)*(extent/10.0)/10,200)
  425.         if steps < 5: steps = 5
  426.  
  427.         from math import sin, cos, pi
  428.  
  429.         pointlist = []
  430.         step = float(extent)/steps
  431.         angle = startAng
  432.         for i in range(int(steps+1)):
  433.             point = (x+xScale*cos((angle/180.0)*pi),
  434.                      y-yScale*sin((angle/180.0)*pi))
  435.             pointlist.append(point)
  436.             angle = angle+step
  437.  
  438.         return pointlist
  439.  
  440.     def curvePoints(self, x1, y1, x2, y2, x3, y3, x4, y4):
  441.         "Return a list of points approximating the given Bezier curve."
  442.  
  443.         bezierSteps = min(max(max(x1,x2,x3,x4)-min(x1,x2,x3,x3),
  444.                               max(y1,y2,y3,y4)-min(y1,y2,y3,y4)),
  445.                           200)
  446.  
  447.         dt1 = 1. / bezierSteps
  448.         dt2 = dt1 * dt1
  449.         dt3 = dt2 * dt1
  450.  
  451.         xx = x1
  452.         yy = y1
  453.         ux = uy = vx = vy = 0
  454.  
  455.         ax = x4 - 3*x3 + 3*x2 - x1
  456.         ay = y4 - 3*y3 + 3*y2 - y1
  457.         bx = 3*x3 - 6*x2 + 3*x1
  458.         by = 3*y3 - 6*y2 + 3*y1
  459.         cx = 3*x2 - 3*x1
  460.         cy = 3*y2 - 3*y1
  461.  
  462.         mx1 = ax * dt3
  463.         my1 = ay * dt3
  464.  
  465.         lx1 = bx * dt2
  466.         ly1 = by * dt2
  467.  
  468.         kx = mx1 + lx1 + cx*dt1
  469.         ky = my1 + ly1 + cy*dt1
  470.  
  471.         mx = 6*mx1
  472.         my = 6*my1
  473.  
  474.         lx = mx + 2*lx1
  475.         ly = my + 2*ly1
  476.  
  477.         pointList = [(xx, yy)]
  478.  
  479.         for i in range(bezierSteps):
  480.             xx = xx + ux + kx
  481.             yy = yy + uy + ky
  482.             ux = ux + vx + lx
  483.             uy = uy + vy + ly
  484.             vx = vx + mx
  485.             vy = vy + my
  486.             pointList.append((xx, yy))
  487.  
  488.         return pointList
  489.  
  490.     def drawMultiLineString(self, s, x,y, font=None, color=None, angle=0):
  491.         "Breaks string into lines (on \n, \r, \n\r, or \r\n), and calls drawString on each."
  492.         import math
  493.         import string
  494.         h = self.fontHeight(font)
  495.         dy = h * math.cos(angle*math.pi/180.0)
  496.         dx = h * math.sin(angle*math.pi/180.0)
  497.         s = string.replace(s, '\r\n', '\n')
  498.         s = string.replace(s, '\n\r', '\n')
  499.         s = string.replace(s, '\r', '\n')
  500.         lines = string.split(s, '\n')
  501.         for line in lines:
  502.             self.drawString(line, x, y, font, color, angle)
  503.             x = x + dx
  504.             y = y + dy
  505.  
  506.     #------------- drawing methods --------------
  507.  
  508.     # Note default parameters "=None" means use the defaults
  509.     # set in the Canvas method: defaultLineColor, etc.
  510.  
  511.     def drawLine(self, x1,y1, x2,y2, color=None, width=None):
  512.         "Draw a straight line between x1,y1 and x2,y2."
  513.         raise NotImplementedError('drawLine')
  514.  
  515.     def drawLines(self, lineList, color=None, width=None):
  516.         "Draw a set of lines of uniform color and width.  \
  517.        lineList: a list of (x1,y1,x2,y2) line coordinates."
  518.         # default implementation:
  519.         for x1, y1, x2, y2 in lineList:
  520.             self.drawLine(x1, y1, x2, y2 ,color,width)
  521.  
  522.  
  523.     # For text, color defaults to self.lineColor.
  524.  
  525.     def drawString(self, s, x,y, font=None, color=None, angle=0):
  526.         "Draw a string starting at location x,y."
  527.         # NOTE: the baseline goes on y; drawing covers (y-ascent,y+descent)
  528.         raise NotImplementedError('drawString')
  529.  
  530.  
  531.     # For fillable shapes, edgeColor defaults to self.defaultLineColor,
  532.     # edgeWidth defaults to self.defaultLineWidth, and
  533.     # fillColor defaults to self.defaultFillColor.
  534.     # Specify "don't fill" by passing fillColor=transparent.
  535.  
  536.     def drawCurve(self, x1,y1, x2,y2, x3,y3, x4,y4,
  537.                 edgeColor=None, edgeWidth=None, fillColor=None, closed=0):
  538.         "Draw a Bézier curve with control points x1,y1 to x4,y4."
  539.  
  540.         pointlist = self.curvePoints(x1, y1, x2, y2, x3, y3, x4, y4)
  541.         self.drawPolygon(pointlist,
  542.                          edgeColor=edgeColor,
  543.                          edgeWidth=edgeWidth,
  544.                          fillColor=fillColor, closed=closed)
  545.  
  546.     def drawRect(self, x1,y1, x2,y2, edgeColor=None, edgeWidth=None, fillColor=None):
  547.         "Draw the rectangle between x1,y1, and x2,y2. \
  548.        These should have x1<x2 and y1<y2."
  549.  
  550.         pointList = [ (x1,y1), (x2,y1), (x2,y2), (x1,y2) ]
  551.         self.drawPolygon(pointList, edgeColor, edgeWidth, fillColor, closed=1)
  552.  
  553.     def drawRoundRect(self, x1,y1, x2,y2, rx=8, ry=8,
  554.                       edgeColor=None, edgeWidth=None, fillColor=None):
  555.         "Draw a rounded rectangle between x1,y1, and x2,y2, \
  556.        with corners inset as ellipses with x radius rx and y radius ry. \
  557.        These should have x1<x2, y1<y2, rx>0, and ry>0."
  558.  
  559.         x1, x2 = min(x1,x2), max(x1, x2)
  560.         y1, y2 = min(y1,y2), max(y1, y2)
  561.  
  562.         dx = rx*2
  563.         dy = ry*2
  564.  
  565.         partList = [
  566.             (figureArc, x1, y1, x1+dx, y1+dy, 180, -90),
  567.             (figureLine, x1+rx, y1, x2-rx, y1),
  568.             (figureArc, x2-dx, y1, x2, y1+dy, 90, -90),
  569.             (figureLine, x2, y1+ry, x2, y2-ry),
  570.             (figureArc, x2-dx, y2, x2, y2-dy, 0, -90),
  571.             (figureLine, x2-rx, y2, x1+rx, y2),
  572.             (figureArc, x1, y2, x1+dx, y2-dy, -90, -90),
  573.             (figureLine, x1, y2-ry, x1, y1+rx)
  574.             ]
  575.  
  576.         self.drawFigure(partList, edgeColor, edgeWidth, fillColor, closed=1)
  577.  
  578.     def drawEllipse(self, x1,y1, x2,y2, edgeColor=None, edgeWidth=None, fillColor=None):
  579.         "Draw an orthogonal ellipse inscribed within the rectangle x1,y1,x2,y2. \
  580.        These should have x1<x2 and y1<y2."
  581.  
  582.         pointlist = self.arcPoints(x1, y1, x2, y2, 0, 360)
  583.         self.drawPolygon(pointlist,edgeColor, edgeWidth, fillColor, closed=1)
  584.  
  585.     def drawArc(self, x1,y1, x2,y2, startAng=0, extent=360,
  586.                 edgeColor=None, edgeWidth=None, fillColor=None):
  587.         "Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2, \
  588.        starting at startAng degrees and covering extent degrees.   Angles \
  589.        start with 0 to the right (+x) and increase counter-clockwise. \
  590.        These should have x1<x2 and y1<y2."
  591.  
  592.         center = (x1+x2)/2, (y1+y2)/2
  593.         pointlist = self.arcPoints(x1, y1, x2, y2, startAng, extent)
  594.  
  595.         # Fill...
  596.         self.drawPolygon(pointlist+[center]+[pointlist[0]],
  597.                  transparent, 0, fillColor)
  598.  
  599.         # Outline...
  600.         self.drawPolygon(pointlist, edgeColor, edgeWidth, transparent)
  601.  
  602.     def drawPolygon(self, pointlist,
  603.                     edgeColor=None, edgeWidth=None, fillColor=None, closed=0):
  604.         """drawPolygon(pointlist) -- draws a polygon
  605.        pointlist: a list of (x,y) tuples defining vertices
  606.        closed: if 1, adds an extra segment connecting the last point to the first
  607.        """
  608.         raise NotImplementedError('drawPolygon')
  609.  
  610.     def drawFigure(self, partList,
  611.                    edgeColor=None, edgeWidth=None, fillColor=None, closed=0):
  612.         """drawFigure(partList) -- draws a complex figure
  613.        partlist: a set of lines, curves, and arcs defined by a tuple whose
  614.                  first element is one of figureLine, figureArc, figureCurve
  615.                  and whose remaining 4, 6, or 8 elements are parameters."""
  616.  
  617.         pointList = []
  618.  
  619.         for tuple in partList:
  620.             op = tuple[0]
  621.             args = list(tuple[1:])
  622.  
  623.             if op == figureLine:
  624.                 pointList.extend( [args[:2], args[2:]] )
  625.             elif op == figureArc:
  626.                 pointList.extend(apply(self.arcPoints,args))
  627.             elif op == figureCurve:
  628.                 pointList.extend(apply(self.curvePoints,args))
  629.             else:
  630.                 raise TypeError("unknown figure operator: " + op)
  631.  
  632.         self.drawPolygon(pointList, edgeColor, edgeWidth, fillColor, closed=closed)
  633.  
  634.  
  635.     # no colors apply to drawImage; the image is drawn as-is
  636.  
  637.     def drawImage(self, image, x1,y1, x2=None,y2=None):
  638.         """Draw a PIL Image into the specified rectangle.  If x2 and y2 are
  639.        omitted, they are calculated from the image size."""
  640.         raise NotImplementedError('drawImage')
  641.  
  642.  
  643.  
  644. #-------------------------------------------------------------------------
  645.  
  646. # utility functions #
  647.  
  648. def getFileObject(file):
  649.     """Common code for every Canvas.save() operation takes a string
  650.    or a potential file object and assures that a valid fileobj is returned"""
  651.  
  652.     if file:
  653.         if myisstring(file):
  654.             fileobj = open(file, "wb")
  655.         else:
  656.             if hasattr(file, "write"):
  657.                 fileobj = file
  658.             else:
  659.                 raise Exception('Invalid file argument to save')
  660.     else:
  661.         raise Exception('Invalid file argument to save')
  662.  
  663.     return fileobj
  664.  
  665.  
  666. class AffineMatrix(object):
  667.     # A = [ a c e]
  668.     #     [ b d f]
  669.     #     [ 0 0 1]
  670.     # self.A = [a b c d e f] = " [ A[0] A[1] A[2] A[3] A[4] A[5] ]"
  671.     def __init__(self, init=None):
  672.         if init:
  673.             if len(init) == 6 :
  674.                 self.A = init
  675.             if type(init) == type(self): # erpht!!! this seems so wrong
  676.                 self.A = init.A
  677.         else:
  678.             self.A = [1.0, 0, 0, 1.0, 0.0, 0.0] # set to identity
  679.  
  680.     def scale(self, sx, sy):
  681.         self.A = [sx*self.A[0], sx*self.A[1], sy*self.A[2], sy * self.A[3], self.A[4], self.A[5] ]
  682.  
  683.     def rotate(self, theta):
  684.         "counter clockwise rotation in standard SVG/libart coordinate system"
  685.         # clockwise in postscript "y-upward" coordinate system
  686.         # R = [ c  -s  0 ]
  687.         #     [ s   c  0 ]
  688.         #     [ 0   0  1 ]
  689.  
  690.         co = math.cos(PI*theta/180.0)
  691.         si = math.sin(PI*theta/180.0)
  692.         self.A = [self.A[0] * co + self.A[2] * si,
  693.                   self.A[1] * co + self.A[3] * si,
  694.                   -self.A[0]* si + self.A[2] * co,
  695.                   -self.A[1]* si + self.A[3] * co,
  696.                   self.A[4],
  697.                   self.A[5] ]
  698.  
  699.     def translate(self, tx, ty):
  700.         self.A  = [ self.A[0], self.A[1], self.A[2], self.A[3],
  701.                     self.A[0]*tx + self.A[2]*ty + self.A[4],
  702.                     self.A[1]*tx + self.A[3]*ty + self.A[5] ]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement