import time import math import array from objc import YES, NO from Foundation import * from AppKit import * def bluefunc(x): return 1.0 / (1.0 + math.exp(-10*(x-0.6))) def redfunc(x): return 1.0 / (1.0 + math.exp(10*(x-0.5))) def greenfunc(x): return 1 - pow(redfunc(x+0.2),2) - bluefunc(x-0.3) def genColors(n=100): out = [None]*n; for i in range(n): x = float(i)/n out[i] = (redfunc(x) * 255, greenfunc(x) * 255, bluefunc(x) * 255) return out class HopView(NSView): def initWithFrame_(self, aFrame): super(HopView, self).initWithFrame_(aFrame) self.drawing = 0 self.pointCount = 0 self.passCount = 0 self.pointsCountsAndColors = [] self.backingStore = None self.iterationsPerCycle = 100000 return self def startCalculation(self): self.window().useOptimizedDrawing_(YES) self.drawing = 1 self.pointCount = 0 self.passCount = 0 self.hopGenerator = hopalong(iterations=self.iterationsPerCycle) self.performSelector_withObject_afterDelay_("doOnePass:", None, 0.0) self.performSelector_withObject_afterDelay_("updateDisplay:", None, 0.5) def resetCalculation(self): self.stopCalculation() self.backingStore = None self.startCalculation() def updateDisplay_(self, sender): if self.drawing: self.performSelector_withObject_afterDelay_("updateDisplay:", None, 2.0) self.setNeedsDisplay_(YES) def stopCalculation(self): self.drawing = 0 self.hopGenerator = None def startStopAction_(self, sender): if self.drawing: self.stopCalculation() else: self.startCalculation() def drawRect_(self, aRect): if self.backingStore: self.backingStore.draw() else: self.eraseView_(aRect) for pointArray, rectCount, color in self.pointsCountsAndColors: color.set() NSRectFillList(pointArray, len(pointArray) / 4) self.pointsCountsAndColors = [] self.backingStore = NSBitmapImageRep.alloc().initWithFocusedViewRect_(self.bounds()) if self.pointCount > 100000: self.pointCount = 0 self.passCount = self.passCount + 1 if self.passCount > 100: self.stopCalculation() def eraseView_(self, aRect): NSColor.blackColor().set() NSRectFill(self.bounds()) def isOpaque(self): return YES def doOnePass_(self, sender): try: if not self.drawing: return color, points = self.hopGenerator.next() except StopIteration: self.startStopAction_(sender) return self.pointCount = self.pointCount + len(points) pixelsWide = self.bounds()[1][0] pixelsHigh = self.bounds()[1][1] aColor = NSColor.colorWithDeviceRed_green_blue_alpha_(color[0] / 256.0, color[1] / 256.0, color[2] / 256.0, 1.0) self.pointsCountsAndColors.append((points, -1, aColor)) self.performSelector_withObject_afterDelay_("doOnePass:", None, 0.0) def hopalong(a=30.0, b=1.0, c=0.9, iterations=1000000, numColors=1000, scale=10.0, xoffset=150, yoffset=150): x = 0.0 y = 0.0 colors = genColors(numColors) colorIter = iterations / len(colors) while 1: for aColor in colors: points = array.array('f', [0.0,0.0,1.0,1.0] * colorIter) for j in range(colorIter): if x < 0: temp = y + math.sqrt(abs(b * x - c)) else: temp = y - math.sqrt(abs(b * x - c)) y = a - x x = temp points[j*4] = xoffset + (scale * x) points[(j*4) + 1] = yoffset + (scale * y) yield (aColor, points)