1from Cocoa import * 2from Quartz import * 3 4from SampleCIView import SampleCIView 5 6from math import sin 7 8import objc 9 10NUM_POINTS=4 11 12class CIBevelView (SampleCIView): 13 currentPoint = objc.ivar(type=objc._C_INT) 14 points = objc.ivar() 15 angleTime = objc.ivar(type=objc._C_FLT) 16 lineImage = objc.ivar() 17 twirlFilter = objc.ivar() 18 heightFieldFilter = objc.ivar() 19 shadedFilter = objc.ivar() 20 21 22 def initWithFrame_(self, frameRect): 23 self = super(CIBevelView, self).initWithFrame_(frameRect) 24 if self is None: 25 return None 26 27 self.points = [ None ] * NUM_POINTS 28 self.points[0] = CGPointMake(0.5 * frameRect.size.width, frameRect.size.height - 100.0) 29 self.points[1] = CGPointMake(150.0, 100.0) 30 self.points[2] = CGPointMake(frameRect.size.width - 150.0, 100.0) 31 self.points[3] = CGPointMake(0.7*self.points[0].x + 0.3*self.points[2].x, 0.7*self.points[0].y + 0.3*self.points[2].y) 32 33 url = NSURL.fileURLWithPath_( 34 NSBundle.mainBundle().pathForResource_ofType_("lightball", "tiff")) 35 36 self.lightball = CIImage.imageWithContentsOfURL_(url) 37 38 self.heightFieldFilter = CIFilter.filterWithName_("CIHeightFieldFromMask") 39 self.heightFieldFilter.setDefaults() 40 self.heightFieldFilter.setValue_forKey_(15.0, "inputRadius") 41 42 self.twirlFilter = CIFilter.filterWithName_("CITwirlDistortion") 43 self.twirlFilter.setDefaults() 44 self.twirlFilter.setValue_forKey_( 45 CIVector.vectorWithX_Y_( 46 0.5*frameRect.size.width, 47 0.5*frameRect.size.height), 48 "inputCenter") 49 self.twirlFilter.setValue_forKey_(300.0, "inputRadius") 50 self.twirlFilter.setValue_forKey_(0.0, "inputAngle") 51 52 self.shadedFilter = CIFilter.filterWithName_("CIShadedMaterial") 53 self.shadedFilter.setDefaults() 54 self.shadedFilter.setValue_forKey_(self.lightball, "inputShadingImage") 55 self.shadedFilter.setValue_forKey_(20.0, "inputScale") 56 57 # 1/30 second should give us decent animation 58 NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( 59 1.0/30.0, self, 'changeTwirlAngle:', None, True) 60 return self 61 62 63 def changeTwirlAngle_(self, timer): 64 self.angleTime += timer.timeInterval() 65 self.twirlFilter.setValue_forKey_( 66 -0.2 * sin(self.angleTime*5.0), 'inputAngle') 67 self.updateImage() 68 69 def mouseDragged_(self, event): 70 loc = self.convertPoint_fromView_(event.locationInWindow(), None) 71 self.points[self.currentPoint].x = loc.x 72 self.points[self.currentPoint].y = loc.y 73 self.lineImage = None 74 75 # normally we'd want this, but the timer will cause us to 76 # redisplay anyway 77 #self.setNeedsDisplay_(True) 78 79 def mouseDown_(self, event): 80 d = 1e4 81 loc = self.convertPoint_fromView_(event.locationInWindow(), None) 82 for i in range(NUM_POINTS): 83 x = self.points[i].x - loc.x 84 y = self.points[i].y - loc.y 85 t = x*x + y*y 86 87 if t < d: 88 self.currentPoint = i 89 d = t 90 91 self.mouseDragged_(event) 92 93 def updateImage(self): 94 context = NSGraphicsContext.currentContext().CIContext() 95 if self.lineImage is None: 96 bounds = self.bounds() 97 layer = context.createCGLayerWithSize_info_( 98 CGSizeMake(NSWidth(bounds), NSHeight(bounds)), None) 99 100 cg = CGLayerGetContext(layer) 101 102 CGContextSetRGBStrokeColor(cg, 1,1,1,1) 103 CGContextSetLineCap(cg, kCGLineCapRound) 104 105 CGContextSetLineWidth(cg, 60.0) 106 CGContextMoveToPoint(cg, self.points[0].x, self.points[0].y) 107 for i in range(1, NUM_POINTS): 108 CGContextAddLineToPoint(cg, self.points[i].x, self.points[i].y) 109 CGContextStrokePath(cg) 110 111 self.lineImage = CIImage.alloc().initWithCGLayer_(layer) 112 113 self.heightFieldFilter.setValue_forKey_(self.lineImage, "inputImage") 114 self.twirlFilter.setValue_forKey_( 115 self.heightFieldFilter.valueForKey_("outputImage"), 116 "inputImage") 117 118 self.shadedFilter.setValue_forKey_( 119 self.twirlFilter.valueForKey_("outputImage"), 120 "inputImage") 121 122 self.setImage_(self.shadedFilter.valueForKey_("outputImage")) 123