1from Quartz import * 2import math 3 4def doEgg(context): 5 p0 = CGPoint(0, 0) 6 p1 = CGPoint(0, 200) 7 c1 = CGPoint(140, 5) 8 c2 = CGPoint(80, 198) 9 10 CGContextTranslateCTM(context, 100, 5) 11 CGContextBeginPath(context) 12 13 CGContextMoveToPoint(context, p0.x, p0.y) 14 # Create the Bezier path segment for the right side of the egg. 15 CGContextAddCurveToPoint(context, c1.x, c1.y, c2.x, c2.y, p1.x, p1.y) 16 # Create the Bezier path segment for the left side of the egg. 17 CGContextAddCurveToPoint(context, -c2.x, c2.y, -c1.x, c1.y, p0.x, p0.y) 18 CGContextClosePath(context) 19 CGContextSetLineWidth(context, 2) 20 CGContextDrawPath(context, kCGPathStroke) 21 22def addRoundedRectToPath(context, rect, ovalWidth, ovalHeight): 23 # If either ovalWidth or ovalHeight is 0, draw a regular rectangle. 24 if ovalWidth == 0 or ovalHeight == 0: 25 CGContextAddRect(context, rect) 26 else: 27 CGContextSaveGState(context) 28 if 1: 29 # Translate to lower-left corner of rectangle. 30 CGContextTranslateCTM(context, 31 CGRectGetMinX(rect), CGRectGetMinY(rect)) 32 # Scale by the oval width and height so that 33 # each rounded corner is 0.5 units in radius. 34 CGContextScaleCTM(context, ovalWidth, ovalHeight) 35 # Unscale the rectangle width by the amount of the X scaling. 36 fw = CGRectGetWidth(rect) / ovalWidth 37 # Unscale the rectangle height by the amount of the Y scaling. 38 fh = CGRectGetHeight(rect) / ovalHeight 39 # Start at the right edge of the rect, at the midpoint in Y. 40 CGContextMoveToPoint(context, fw, fh/2) 41 # Segment 1 42 CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 0.5) 43 # Segment 2 44 CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 0.5) 45 # Segment 3 46 CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 0.5) 47 # Segment 4 48 CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 0.5) 49 # Closing the path adds the last segment. 50 CGContextClosePath(context) 51 CGContextRestoreGState(context) 52 53def doRoundedRects(context): 54 rect = CGRectMake(10, 10, 210, 150) 55 ovalWidth = 100 56 ovalHeight = 100 57 CGContextSetLineWidth(context, 2.) 58 CGContextBeginPath(context) 59 addRoundedRectToPath(context, rect, ovalWidth, ovalHeight) 60 CGContextSetRGBStrokeColor(context, 1, 0, 0, 1) 61 CGContextDrawPath(context, kCGPathStroke) 62 63def doStrokeWithCTM(context): 64 CGContextTranslateCTM(context, 150., 180.) 65 CGContextSetLineWidth(context, 10) 66 # Draw ellipse 1 with a uniform stroke. 67 CGContextSaveGState(context) 68 if 1: 69 # Scale the CTM so the circular arc will be elliptical. 70 CGContextScaleCTM(context, 2, 1) 71 CGContextBeginPath(context) 72 # Create an arc that is a circle. 73 CGContextAddArc(context, 0., 0., 45., 0., 2*math.pi, 0) 74 # Restore the context parameters prior to stroking the path. 75 # CGContextRestoreGState does not affect the path in the context. 76 CGContextRestoreGState(context) 77 CGContextStrokePath(context) 78 79 # *** was 0, -120 80 CGContextTranslateCTM(context, 220., 0.) 81 # Draw ellipse 2 with non-uniform stroke. 82 CGContextSaveGState(context) 83 if 1: 84 # Scale the CTM so the circular arc will be elliptical. 85 CGContextScaleCTM(context, 2, 1) 86 CGContextBeginPath(context) 87 # Create an arc that is a circle. 88 CGContextAddArc(context, 0., 0., 45., 0., 2*math.pi, 0) 89 # Stroke the path with the scaled coordinate system in effect. 90 CGContextStrokePath(context) 91 CGContextRestoreGState(context) 92 93def doRotatedEllipsesWithCGPath(context): 94 totreps = 144 95 tint = 1.0 96 tintIncrement = 1.0/totreps 97 98 # Create a new transform consisting of a 45 degree rotation. 99 theTransform = CGAffineTransformMakeRotation(math.pi/4) 100 # Apply a scaling transformation to the transform just created. 101 theTransform = CGAffineTransformScale(theTransform, 1, 2) 102 # Create a mutable CGPath object. 103 path = CGPathCreateMutable() 104 if path is None: 105 print >>sys.stderr, "Couldn't create path!" 106 return 107 108 # Add a circular arc to the CGPath object, transformed 109 # by an affine transform. 110 CGPathAddArc(path, theTransform, 0., 0., 45., 0., 2*math.pi, False); 111 # Close the CGPath object. 112 CGPathCloseSubpath(path) 113 114 # Place the first ellipse at a good location. 115 CGContextTranslateCTM(context, 100, 100) 116 for i in range(totreps): 117 CGContextBeginPath(context) 118 # Add the CGPath object to the current path in the context. 119 CGContextAddPath(context, path) 120 121 # Set the fill color for this instance of the ellipse. 122 CGContextSetRGBFillColor(context, tint, 0., 0., 1.) 123 # Filling the path implicitly closes it. 124 CGContextFillPath(context) 125 # Compute the next tint color. 126 tint -= tintIncrement 127 # Move over for the next ellipse. 128 CGContextTranslateCTM(context, 1, 0.) 129 130def alignPointToUserSpace(context, p): 131 # Compute the coordinates of the point in device space. 132 p = CGContextConvertPointToDeviceSpace(context, p) 133 # Ensure that coordinates are at exactly the corner 134 # of a device pixel. 135 p.x = math.floor(p.x) 136 p.y = math.floor(p.y) 137 # Convert the device aligned coordinate back to user space. 138 return CGContextConvertPointToUserSpace(context, p) 139 140def alignSizeToUserSpace(context, s): 141 # Compute the size in device space. 142 s = CGContextConvertSizeToDeviceSpace(context, s) 143 # Ensure that size is an integer multiple of device pixels. 144 s.width = math.floor(s.width) 145 s.height = math.floor(s.height) 146 # Convert back to user space. 147 return CGContextConvertSizeToUserSpace(context, s) 148 149def alignRectToUserSpace(context, r): 150 # Compute the coordinates of the rectangle in device space. 151 r = CGContextConvertRectToDeviceSpace(context, r) 152 # Ensure that the x and y coordinates are at a pixel corner. 153 r.origin.x = math.floor(r.origin.x) 154 r.origin.y = math.floor(r.origin.y) 155 # Ensure that the width and height are an integer number of 156 # device pixels. Note that this produces a width and height 157 # that is less than or equal to the original width. Another 158 # approach is to use ceil to ensure that the new rectangle 159 # encloses the original one. 160 r.size.width = math.floor(r.size.width) 161 r.size.height = math.floor(r.size.height) 162 163 # Convert back to user space. 164 return CGContextConvertRectToUserSpace(context, r) 165 166def doPixelAlignedFillAndStroke(context): 167 p1 = CGPointMake(16.7, 17.8) 168 p2 = CGPointMake(116.7, 17.8) 169 r = CGRectMake(16.7, 20.8, 100.6, 100.6) 170 171 CGContextSetLineWidth(context, 2) 172 CGContextSetRGBFillColor(context, 1., 0., 0., 1.) 173 CGContextSetRGBStrokeColor(context, 1., 0., 0., 1.) 174 175 # Unaligned drawing. 176 CGContextBeginPath(context) 177 CGContextMoveToPoint(context, p1.x, p1.y) 178 CGContextAddLineToPoint(context, p2.x, p2.y) 179 CGContextStrokePath(context) 180 CGContextFillRect(context, r) 181 182 # Translate to the right before drawing along 183 # aligned coordinates. 184 CGContextTranslateCTM(context, 106, 0) 185 186 # Aligned drawing. 187 188 # Compute the length of the line in user space. 189 s = CGSizeMake(p2.x - p1.x, p2.y - p1.y) 190 191 CGContextBeginPath(context) 192 # Align the starting point to a device 193 # pixel boundary. 194 p1 = alignPointToUserSpace(context, p1) 195 # Establish the starting point of the line. 196 CGContextMoveToPoint(context, p1.x, p1.y) 197 # Compute the line length as an integer 198 # number of device pixels. 199 s = alignSizeToUserSpace(context, s) 200 CGContextAddLineToPoint(context, 201 p1.x + s.width, 202 p1.y + s.height) 203 CGContextStrokePath(context) 204 # Compute a rect that is aligned to device 205 # space with a width that is an integer 206 # number of device pixels. 207 r = alignRectToUserSpace(context, r) 208 CGContextFillRect(context, r) 209