from Cocoa import * from Quartz import * from SampleCIView import SampleCIView from math import sin import objc NUM_POINTS=4 class CIBevelView (SampleCIView): currentPoint = objc.ivar(type=objc._C_INT) points = objc.ivar() angleTime = objc.ivar(type=objc._C_FLT) lineImage = objc.ivar() twirlFilter = objc.ivar() heightFieldFilter = objc.ivar() shadedFilter = objc.ivar() def initWithFrame_(self, frameRect): self = super(CIBevelView, self).initWithFrame_(frameRect) if self is None: return None self.points = [ None ] * NUM_POINTS self.points[0] = CGPointMake(0.5 * frameRect.size.width, frameRect.size.height - 100.0) self.points[1] = CGPointMake(150.0, 100.0) self.points[2] = CGPointMake(frameRect.size.width - 150.0, 100.0) 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) url = NSURL.fileURLWithPath_( NSBundle.mainBundle().pathForResource_ofType_("lightball", "tiff")) self.lightball = CIImage.imageWithContentsOfURL_(url) self.heightFieldFilter = CIFilter.filterWithName_("CIHeightFieldFromMask") self.heightFieldFilter.setDefaults() self.heightFieldFilter.setValue_forKey_(15.0, "inputRadius") self.twirlFilter = CIFilter.filterWithName_("CITwirlDistortion") self.twirlFilter.setDefaults() self.twirlFilter.setValue_forKey_( CIVector.vectorWithX_Y_( 0.5*frameRect.size.width, 0.5*frameRect.size.height), "inputCenter") self.twirlFilter.setValue_forKey_(300.0, "inputRadius") self.twirlFilter.setValue_forKey_(0.0, "inputAngle") self.shadedFilter = CIFilter.filterWithName_("CIShadedMaterial") self.shadedFilter.setDefaults() self.shadedFilter.setValue_forKey_(self.lightball, "inputShadingImage") self.shadedFilter.setValue_forKey_(20.0, "inputScale") # 1/30 second should give us decent animation NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( 1.0/30.0, self, 'changeTwirlAngle:', None, True) return self def changeTwirlAngle_(self, timer): self.angleTime += timer.timeInterval() self.twirlFilter.setValue_forKey_( -0.2 * sin(self.angleTime*5.0), 'inputAngle') self.updateImage() def mouseDragged_(self, event): loc = self.convertPoint_fromView_(event.locationInWindow(), None) self.points[self.currentPoint].x = loc.x self.points[self.currentPoint].y = loc.y self.lineImage = None # normally we'd want this, but the timer will cause us to # redisplay anyway #self.setNeedsDisplay_(True) def mouseDown_(self, event): d = 1e4 loc = self.convertPoint_fromView_(event.locationInWindow(), None) for i in range(NUM_POINTS): x = self.points[i].x - loc.x y = self.points[i].y - loc.y t = x*x + y*y if t < d: self.currentPoint = i d = t self.mouseDragged_(event) def updateImage(self): context = NSGraphicsContext.currentContext().CIContext() if self.lineImage is None: bounds = self.bounds() layer = context.createCGLayerWithSize_info_( CGSizeMake(NSWidth(bounds), NSHeight(bounds)), None) cg = CGLayerGetContext(layer) CGContextSetRGBStrokeColor(cg, 1,1,1,1) CGContextSetLineCap(cg, kCGLineCapRound) CGContextSetLineWidth(cg, 60.0) CGContextMoveToPoint(cg, self.points[0].x, self.points[0].y) for i in range(1, NUM_POINTS): CGContextAddLineToPoint(cg, self.points[i].x, self.points[i].y) CGContextStrokePath(cg) self.lineImage = CIImage.alloc().initWithCGLayer_(layer) self.heightFieldFilter.setValue_forKey_(self.lineImage, "inputImage") self.twirlFilter.setValue_forKey_( self.heightFieldFilter.valueForKey_("outputImage"), "inputImage") self.shadedFilter.setValue_forKey_( self.twirlFilter.valueForKey_("outputImage"), "inputImage") self.setImage_(self.shadedFilter.valueForKey_("outputImage"))