Changeset 147 for nodebox/trunk/src/DrawingPrimitives.py
- Timestamp:
- 09/18/07 10:59:03 (16 months ago)
- Files:
-
- 1 modified
-
nodebox/trunk/src/DrawingPrimitives.py (modified) (69 diffs)
Legend:
- Unmodified
- Added
- Removed
-
nodebox/trunk/src/DrawingPrimitives.py
r89 r147 1 import os 2 from random import choice, shuffle 1 3 from AppKit import * 2 4 from Foundation import * 3 from random import choice, shuffle4 import os5 5 6 6 __all__ = [ … … 13 13 "NORMAL","FORTYFIVE", 14 14 "NUMBER", "TEXT", "BOOLEAN","BUTTON", 15 "Point", "Grob", "BezierPath", "PathElement", " Rect", "Oval", "Color", "Transform", "Image", "Text",15 "Point", "Grob", "BezierPath", "PathElement", "ClippingPath", "Rect", "Oval", "Color", "Transform", "Image", "Text", 16 16 "Variable", "Canvas", "Context", 17 17 "grid", "random","choice", "files","autotext", … … 48 48 BOOLEAN = 3 49 49 BUTTON = 4 50 51 _STATE_NAMES = { 52 '_outputmode': 'outputmode', 53 '_colorrange': 'colorrange', 54 '_fillcolor': 'fill', 55 '_strokecolor': 'stroke', 56 '_strokewidth': 'strokewidth', 57 '_transform': 'transform', 58 '_transformmode': 'transformmode', 59 '_fontname': 'font', 60 '_fontsize': 'fontsize', 61 '_align': 'align', 62 '_lineheight': 'lineheight', 63 } 50 64 51 65 def _save(): … … 89 103 """Appends the grob to the canvas. 90 104 This will result in a draw later on, when the scene graph is rendered.""" 91 self._ctx._canvas.append( (self._draw) ) 92 93 class BezierPath(Grob): 105 self._ctx._canvas.append( self ) 106 107 def copy(self): 108 """Returns a deep copy of this grob.""" 109 raise NotImplementedError, "Copy is not implemented on this Grob class." 110 111 def inheritFromContext(self, ignore=()): 112 attrs_to_copy = list(self.__class__.stateAttributes) 113 [attrs_to_copy.remove(k) for k, v in _STATE_NAMES.items() if v in ignore] 114 _copy_attrs(self._ctx, self, attrs_to_copy) 115 116 def checkKwargs(self, kwargs): 117 remaining = [arg for arg in kwargs.keys() if arg not in self.kwargs] 118 if remaining: 119 raise NodeBoxError, "Unknown argument(s) '%s'" % ", ".join(remaining) 120 checkKwargs = classmethod(checkKwargs) 121 122 class TransformMixin(object): 123 124 """Mixin class for transformation support. 125 Adds the _transform and _transformmode attributes to the class.""" 126 127 def __init__(self): 128 self._reset() 129 130 def _reset(self): 131 self._transform = Transform() 132 self._transformmode = CENTER 133 134 def transform(self, mode): 135 self._transformmode = mode 136 137 def translate(self, x, y): 138 self._transform.translate(x, y) 139 140 def transform(self, mode=None): 141 if mode is not None: 142 self._transformmode = mode 143 return self._transformmode 144 145 def reset(self): 146 self._transform = Transform() 147 148 def rotate(self, degrees=0, radians=0): 149 self._transform.rotate(-degrees,-radians) 150 151 def translate(self, x=0, y=0): 152 self._transform.translate(x,y) 153 154 def scale(self, x=1, y=None): 155 self._transform.scale(x,y) 156 157 def skew(self, x=0, y=0): 158 self._transform.skew(x,y) 159 160 class ColorMixin(object): 161 162 """Mixin class for color support. 163 Adds the _fillcolor, _strokecolor and _strokewidth attributes to the class.""" 164 165 def __init__(self, **kwargs): 166 try: 167 self._fillcolor = Color(self._ctx, kwargs['fill']) 168 except KeyError: 169 self._fillcolor = Color(self._ctx) 170 try: 171 self._strokecolor = Color(self._ctx, kwargs['stroke']) 172 except KeyError: 173 self._strokecolor = None 174 self._strokewidth = kwargs.get('strokewidth', 1.0) 175 176 def nofill(self): 177 self._fillcolor = None 178 179 def fill(self, *args): 180 if len(args) > 0: 181 self._fillcolor = Color(self._ctx, *args) 182 return self._fillcolor 183 184 def nostroke(self): 185 self._strokecolor = None 186 187 def stroke(self, *args): 188 if len(args) > 0: 189 self._strokecolor = Color(self._ctx, *args) 190 return self._strokecolor 191 192 def strokewidth(self, width=None): 193 if width is not None: 194 self._strokewidth = width 195 return self._strokewidth 196 197 class BezierPath(Grob, TransformMixin, ColorMixin): 94 198 """A BezierPath provides a wrapper around NSBezierPath.""" 95 96 def __init__(self, ctx, path=None): 97 Grob.__init__(self, ctx) 199 200 stateAttributes = ('_fillcolor', '_strokecolor', '_strokewidth', '_transform', '_transformmode') 201 kwargs = ('fill', 'stroke', 'strokewidth') 202 203 def __init__(self, ctx, path=None, **kwargs): 204 super(BezierPath, self).__init__(ctx) 205 TransformMixin.__init__(self) 206 ColorMixin.__init__(self, **kwargs) 207 self._segment_cache = None 98 208 if path is None: 99 209 self.path = NSBezierPath.bezierPath() … … 101 211 self.path = NSBezierPath.bezierPath() 102 212 self.extend(path) 213 elif isinstance(path, BezierPath): 214 self.path = path.path.copy() 215 _copy_attrs(path, self, self.stateAttributes) 216 elif isinstance(path, NSBezierPath): 217 self.path = path 103 218 else: 104 self.path = path 105 self._transform = Transform(self._ctx) 106 self.transformmode = CENTER 107 self.fillcolor = Color(self._ctx) 108 self.strokecolor = None 109 self.strokewidth = 1.0 110 self._segment_cache = None 111 112 def inheritFromContext(self): 113 self.fillcolor = self._ctx._fillcolor 114 self.strokecolor = self._ctx._strokecolor 115 self.strokewidth = self._ctx._strokewidth 116 self._transform = self._ctx._transform.copy() 117 self.transformmode = self._ctx._transformmode 118 119 def addclip(self): 120 self.inheritFromContext() 121 self._ctx._canvas.append(self._addclip) 122 addClip = addclip 123 124 def _addclip(self): 125 p = self.transform.transformBezierPath(self) 126 p.path.addClip() 127 128 def setclip(self): 129 self.inheritFromContext() 130 self._ctx._canvas.append(self._setclip) 131 setClip = setclip 132 133 def _setclip(self): 134 # XXX: setClip here has the effect of setting the clip independent of the view, 135 # meaning that we can draw over the bounds of the view, which is not good. 136 # 137 # setclip should restore the original graphicscontext (and save it back immediately 138 # so the stack is left intact) and add (not set) the clip there. 139 # Also, we need a place for endclip to store its code, since it can not 140 # be part of the current path. 141 p = self.transform.transformBezierPath(self) 142 _restore() 143 _save() 144 p.path.addClip() 219 raise NodeBoxError, "Don't know what to do with %s." % path 220 221 def copy(self): 222 return self.__class__(self._ctx, self) 145 223 146 224 ### Path methods ### … … 176 254 def contains(self, x, y): 177 255 return self.path.containsPoint_((x,y)) 256 257 ### Basic shapes ### 258 259 def rect(self, x, y, width, height): 260 self._segment_cache = None 261 self.path.appendBezierPathWithRect_( ((x, y), (width, height)) ) 262 263 def oval(self, x, y, width, height): 264 self._segment_cache = None 265 self.path.appendBezierPathWithOvalInRect_( ((x, y), (width, height)) ) 266 267 def line(self, x1, y1, x2, y2): 268 self._segment_cache = None 269 self.path.moveToPoint_( (x1, y1) ) 270 self.path.lineToPoint_( (x2, y2) ) 178 271 179 272 ### List methods ### … … 216 309 trans = self._transform.copy() 217 310 218 if (self. transformmode == CENTER):311 if (self._transformmode == CENTER): 219 312 (x, y), (w, h) = self.bounds 220 313 deltax = x+w/2 221 314 deltay = y+h/2 222 t = Transform( self._ctx)315 t = Transform() 223 316 t.translate(-deltax,-deltay) 224 317 trans.prepend(t) 225 t = Transform( self._ctx)318 t = Transform() 226 319 t.translate(deltax,deltay) 227 320 trans.append(t) … … 234 327 _save() 235 328 self.transform.concat() 236 if (self. fillcolor):237 self. fillcolor.set()329 if (self._fillcolor): 330 self._fillcolor.set() 238 331 self.path.fill() 239 if (self. strokecolor):240 self. strokecolor.set()241 self.path.setLineWidth_(self. strokewidth)332 if (self._strokecolor): 333 self._strokecolor.set() 334 self.path.setLineWidth_(self._strokewidth) 242 335 self.path.stroke() 243 336 _restore() … … 286 379 287 380 class PathElement(object): 381 288 382 def __init__(self, cmd=None, pts=None): 289 383 self.cmd = cmd … … 312 406 self.ctrl1 = Point() 313 407 self.ctrl2 = Point() 314 #else:315 # raise NodeBoxError, "PathElement: unknown command %s" % cmd316 408 317 409 def __repr__(self): … … 335 427 return not self.__eq__(other) 336 428 429 class ClippingPath(Grob): 430 431 def __init__(self, ctx, path): 432 self._ctx = ctx 433 self.path = path 434 self._grobs = [] 435 436 def append(self, grob): 437 self._grobs.append(grob) 438 439 def _draw(self): 440 _save() 441 cp = self.path.transform.transformBezierPath(self.path) 442 cp.path.addClip() 443 for grob in self._grobs: 444 grob._draw() 445 _restore() 446 337 447 class Rect(BezierPath): 338 def __init__(self, ctx, x, y, width, height): 448 449 def __init__(self, ctx, x, y, width, height, **kwargs): 339 450 r = (x,y), (width,height) 340 BezierPath.__init__(self, ctx, NSBezierPath.bezierPathWithRect_(r)) 451 super(Rect, self).__init__(ctx, NSBezierPath.bezierPathWithRect_(r), **kwargs) 452 453 def copy(self): 454 raise NotImplementedError, "Please don't use Rect anymore" 341 455 342 456 class Oval(BezierPath): 343 def __init__(self, ctx, x, y, width, height): 457 458 def __init__(self, ctx, x, y, width, height, **kwargs): 344 459 r = (x,y), (width,height) 345 BezierPath.__init__(self, ctx, NSBezierPath.bezierPathWithOvalInRect_(r)) 460 super(Oval, self).__init__(ctx, NSBezierPath.bezierPathWithOvalInRect_(r), **kwargs) 461 462 def copy(self): 463 raise NotImplementedError, "Please don't use Oval anymore" 346 464 347 465 class Color(object): … … 352 470 353 471 # Decompose the arguments into tuples. 354 # This is done in a loop, because giving a fill with a tuple 355 # wraps the tuple in a tuple. 356 while(params == 1 and isinstance(args[0], tuple)): 472 if params == 1 and isinstance(args[0], tuple): 357 473 args = args[0] 358 474 params = len(args) … … 365 481 args = self._normalizeList(args) 366 482 g, = args 367 clr = NSColor.colorWithDevice Cyan_magenta_yellow_black_alpha_(0, 0, 0, 1.0-g, 1)483 clr = NSColor.colorWithDeviceWhite_alpha_(g, 1) 368 484 elif params == 2: # Gray and alpha 369 485 args = self._normalizeList(args) 370 486 g, a = args 371 clr = NSColor.colorWithDevice Cyan_magenta_yellow_black_alpha_(0, 0, 0, 1.0-g, a)487 clr = NSColor.colorWithDeviceWhite_alpha_(g, a) 372 488 elif params == 3 and self._ctx._colormode == RGB: # RGB, no alpha 373 489 args = self._normalizeList(args) 374 490 r,g,b = args 375 clr = NSColor.colorWith CalibratedRed_green_blue_alpha_(r, g, b, 1)491 clr = NSColor.colorWithDeviceRed_green_blue_alpha_(r, g, b, 1) 376 492 elif params == 3 and self._ctx._colormode == HSB: # HSB, no alpha 377 493 args = self._normalizeList(args) 378 494 h, s, b = args 379 clr = NSColor.colorWith CalibratedHue_saturation_brightness_alpha_(h, s, b, 1)495 clr = NSColor.colorWithDeviceHue_saturation_brightness_alpha_(h, s, b, 1) 380 496 elif params == 4 and self._ctx._colormode == RGB: # RGB and alpha 381 497 args = self._normalizeList(args) 382 498 r,g,b, a = args 383 clr = NSColor.colorWith CalibratedRed_green_blue_alpha_(r, g, b, a)499 clr = NSColor.colorWithDeviceRed_green_blue_alpha_(r, g, b, a) 384 500 elif params == 4 and self._ctx._colormode == HSB: # HSB and alpha 385 501 args = self._normalizeList(args) 386 502 h, s, b, a = args 387 clr = NSColor.colorWith CalibratedHue_saturation_brightness_alpha_(h, s, b, a)503 clr = NSColor.colorWithDeviceHue_saturation_brightness_alpha_(h, s, b, a) 388 504 elif params == 4 and self._ctx._colormode == CMYK: # CMYK, no alpha 389 505 args = self._normalizeList(args) … … 395 511 clr = NSColor.colorWithDeviceCyan_magenta_yellow_black_alpha_(c, m, y, k, a) 396 512 else: 397 clr = NSColor.colorWithDevice Cyan_magenta_yellow_black_alpha_(0, 0, 0, 1, 1)513 clr = NSColor.colorWithDeviceWhite_alpha_(0, 1) 398 514 399 515 self._cmyk = clr.colorUsingColorSpaceName_(NSDeviceCMYKColorSpace) 400 self._rgb = clr.colorUsingColorSpaceName_(NS CalibratedRGBColorSpace)516 self._rgb = clr.colorUsingColorSpaceName_(NSDeviceRGBColorSpace) 401 517 402 518 def __repr__(self): … … 405 521 406 522 def set(self): 407 self._cmyk.set() 523 if self._ctx._outputmode == RGB: 524 self._rgb.set() 525 else: 526 self._cmyk.set() 408 527 409 528 def copy(self): 410 return self.__class__(color=self._rgb.copy()) 529 new = self.__class__(self._ctx) 530 new._rgb = self._rgb.copy() 531 new._updateCmyk() 532 return new 411 533 412 534 def _updateCmyk(self): … … 414 536 415 537 def _updateRgb(self): 416 self._rgb = self._cmyk.colorUsingColorSpaceName_(NS CalibratedRGBColorSpace)538 self._rgb = self._cmyk.colorUsingColorSpaceName_(NSDeviceRGBColorSpace) 417 539 418 540 def _get_hue(self): … … 421 543 val = self._normalize(val) 422 544 h, s, b, a = self._rgb.getHue_saturation_brightness_alpha_() 423 self._rgb = NSColor.colorWith CalibratedHue_saturation_brightness_alpha_(val, s, b, a)545 self._rgb = NSColor.colorWithDeviceHue_saturation_brightness_alpha_(val, s, b, a) 424 546 self._updateCmyk() 425 547 h = hue = property(_get_hue, _set_hue, doc="the hue of the color") … … 430 552 val = self._normalize(val) 431 553 h, s, b, a = self._rgb.getHue_saturation_brightness_alpha_() 432 self._rgb = NSColor.colorWith CalibratedHue_saturation_brightness_alpha_(h, val, b, a)554 self._rgb = NSColor.colorWithDeviceHue_saturation_brightness_alpha_(h, val, b, a) 433 555 self._updateCmyk() 434 556 s = saturation = property(_get_saturation, _set_saturation, doc="the saturation of the color") … … 439 561 val = self._normalize(val) 440 562 h, s, b, a = self._rgb.getHue_saturation_brightness_alpha_() 441 self._rgb = NSColor.colorWith CalibratedHue_saturation_brightness_alpha_(h, s, val, a)563 self._rgb = NSColor.colorWithDeviceHue_saturation_brightness_alpha_(h, s, val, a) 442 564 self._updateCmyk() 443 565 v = brightness = property(_get_brightness, _set_brightness, doc="the brightness of the color") … … 448 570 val = self._normalize(val) 449 571 h, s, b, a = values 450 self._rgb = NSColor.colorWith CalibratedHue_saturation_brightness_alpha_(h, s, b, a)572 self._rgb = NSColor.colorWithDeviceHue_saturation_brightness_alpha_(h, s, b, a) 451 573 self._updateCmyk() 452 574 hsba = property(_get_hsba, _set_hsba, doc="the hue, saturation, brightness and alpha of the color") … … 457 579 val = self._normalize(val) 458 580 r, g, b, a = self._rgb.getRed_green_blue_alpha_() 459 self._rgb = NSColor.colorWith CalibratedRed_green_blue_alpha_(val, g, b, a)581 self._rgb = NSColor.colorWithDeviceRed_green_blue_alpha_(val, g, b, a) 460 582 self._updateCmyk() 461 583 r = red = property(_get_red, _set_red, doc="the red component of the color") … … 466 588 val = self._normalize(val) 467 589 r, g, b, a = self._rgb.getRed_green_blue_alpha_() 468 self._rgb = NSColor.colorWith CalibratedRed_green_blue_alpha_(r, val, b, a)590 self._rgb = NSColor.colorWithDeviceRed_green_blue_alpha_(r, val, b, a) 469 591 self._updateCmyk() 470 592 g = green = property(_get_green, _set_green, doc="the green component of the color") … … 475 597 val = self._normalize(val) 476 598 r, g, b, a = self._rgb.getRed_green_blue_alpha_() 477 self._rgb = NSColor.colorWith CalibratedRed_green_blue_alpha_(r, g, val, a)599 self._rgb = NSColor.colorWithDeviceRed_green_blue_alpha_(r, g, val, a) 478 600 self._updateCmyk() 479 601 b = blue = property(_get_blue, _set_blue, doc="the blue component of the color") … … 484 606 val = self._normalize(val) 485 607 r, g, b, a = self._rgb.getRed_green_blue_alpha_() 486 self._rgb = NSColor.colorWith CalibratedRed_green_blue_alpha_(r, g, b, val)608 self._rgb = NSColor.colorWithDeviceRed_green_blue_alpha_(r, g, b, val) 487 609 self._updateCmyk() 488 610 a = alpha = property(_get_alpha, _set_alpha, doc="the alpha component of the color") … … 493 615 val = self._normalizeList(val) 494 616 r, g, b, a = val 495 self._rgb = NSColor.colorWith CalibratedRed_green_blue_alpha_(r, g, b, a)617 self._rgb = NSColor.colorWithDeviceRed_green_blue_alpha_(r, g, b, a) 496 618 self._updateCmyk() 497 619 rgba = property(_get_rgba, _set_rgba, doc="the red, green, blue and alpha values of the color") … … 556 678 color = Color 557 679 558 def Gray(ctx, g, alpha=1):559 return Color(ctx, g, g, g, alpha)560 561 680 class Transform(object): 562 def __init__(self, ctx, transform=None): 563 self._ctx = ctx 681 def __init__(self, transform=None): 564 682 if transform is None: 565 683 transform = NSAffineTransform.transform() … … 577 695 578 696 def copy(self): 579 return self.__class__(self. _ctx, self.transform.copy())697 return self.__class__(self.transform.copy()) 580 698 581 699 def __repr__(self): … … 611 729 x = math.pi * x / 180 612 730 y = math.pi * y / 180 613 t = Transform( self._ctx)731 t = Transform() 614 732 t.matrix = 1, math.tan(y), -math.tan(x), 1, 0, 0 615 733 self.prepend(t) … … 633 751 def transformBezierPath(self, path): 634 752 if isinstance(path, BezierPath): 753 ctx = path._ctx 635 754 path = path.path 755 elif isinstance(path, NSBezierPath): 756 ctx = None 757 else: 758 raise NodeBoxError, "Can only transform BezierPaths" 636 759 path = self.transform.transformBezierPath_(path) 637 return BezierPath(self._ctx, path) 638 639 class Image(Grob): 760 return BezierPath(ctx, path) 761 762 class Image(Grob, TransformMixin): 763 764 stateAttributes = ('_transform', '_transformmode') 765 kwargs = () 766 640 767 def __init__(self, ctx, path=None, x=0, y=0, width=None, height=None, alpha=1.0, image=None, data=None): 641 768 """ … … 652 779 - data: a stream of bytes of image data. 653 780 """ 654 Grob.__init__(self, ctx) 781 super(Image, self).__init__(ctx) 782 TransformMixin.__init__(self) 655 783 if data is not None: 656 784 d = NSData.dataWithBytes_length_(data, len(data)) … … 684 812 self.width = width 685 813 self.height = height 686 self.transform = Transform(self._ctx)687 814 self.alpha = alpha 688 815 self.debugImage = False 689 816 817 def copy(self): 818 new = self.__class__(self._ctx) 819 _copy_attrs(self, new, ('image', 'x', 'y', 'width', 'height', '_transform', '_transformmode', 'alpha', 'debugImage')) 820 return new 821 690 822 def getSize(self): 691 823 return self.image.size() 692 824 693 825 size = property(getSize) 694 695 def inheritFromContext(self):696 self.transform = self._ctx._transform.copy()697 self.transformmode = self._ctx._transformmode698 826 699 827 def _draw(self): … … 714 842 715 843 # Center-mode transforms: translate to image center 716 if self. transformmode == CENTER:844 if self._transformmode == CENTER: 717 845 # This is the hardest case: center-mode transformations with given width or height. 718 846 # Order is very important in this code. … … 720 848 # Set the position first, before any of the scaling or transformations are done. 721 849 # Context transformations might change the translation, and we don't want that. 722 t = Transform( self._ctx)850 t = Transform() 723 851 t.translate(self.x, self.y) 724 852 t.concat() … … 732 860 dX = srcW / 2 733 861 dY = srcH / 2 734 t = Transform( self._ctx)862 t = Transform() 735 863 t.translate(dX, dY) 736 864 t.concat() 737 865 738 866 # Do current transformation. 739 self. transform.concat()867 self._transform.concat() 740 868 741 869 # Move back to the previous position. 742 t = Transform( self._ctx)870 t = Transform() 743 871 t.translate(-dX, -dY) 744 872 t.concat() 745 873 746 874 # Finally, scale the image according to the factors. 747 t = Transform( self._ctx)875 t = Transform() 748 876 t.scale(factor) 749 877 t.concat() 750 878 else: 751 879 # Do current transformation 752 self. transform.concat()880 self._transform.concat() 753 881 # Scale according to width or height factor 754 t = Transform( self._ctx)882 t = Transform() 755 883 t.translate(self.x, self.y) # Here we add the positioning of the image. 756 884 t.scale(factor) … … 769 897 x,y = self.x, self.y 770 898 # Center-mode transforms: translate to image center 771 if self. transformmode == CENTER:899 if self._transformmode == CENTER: 772 900 deltaX = srcW / 2 773 901 deltaY = srcH / 2 774 t = Transform( self._ctx)902 t = Transform() 775 903 t.translate(x+deltaX, y+deltaY) 776 904 t.concat() … … 778 906 y = -deltaY 779 907 # Do current transformation 780 self. transform.concat()908 self._transform.concat() 781 909 # A debugImage draws a black rectangle instead of an image. 782 910 if self.debugImage: … … 791 919 # is better: always use zero coordinates for drawAtPoint and use a transform to set the 792 920 # final position. 793 t = Transform( self._ctx)921 t = Transform() 794
