Last active
June 30, 2018 13:23
-
-
Save cruffenach/45a98ce6f2a49b5e859b to your computer and use it in GitHub Desktop.
Guilloche Drawing in Playgrounds
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Playground - noun: a place where people can play | |
import Cocoa | |
import QuartzCore | |
import XCPlayground | |
import Accelerate | |
func showMessage(message : String, messageIndex: Integer) { | |
let attributedString = NSAttributedString(string: message, attributes: NSDictionary(object: NSFont(name: "HelveticaNeue-Light", size: 24), forKey: NSFontAttributeName)) | |
XCPCaptureValue("Message \(messageIndex)", attributedString) | |
} | |
func sinCoefficient(phase: Double, | |
frequency: Double, | |
resolution: Double, | |
resolutionIncrement: Double) -> Double { | |
return sin(phase + | |
frequency * | |
(resolution * | |
resolutionIncrement)); | |
} | |
func xPoint(resolution : Double, | |
resolutionIncrement : Double, | |
amplitude: Double, | |
sinCoefficient: Double) -> Double { | |
var x = (0.5 + | |
0.5 * | |
amplitude * | |
sinCoefficient) * | |
cos(resolution * | |
resolutionIncrement) | |
XCPCaptureValue("X Coordinates", x) | |
return x | |
} | |
func yPoint(resolution : Double, | |
resolutionIncrement : Double, | |
amplitude: Double, | |
sinCoefficient: Double) -> Double { | |
var y = (0.5 + | |
0.5 * | |
amplitude * | |
sinCoefficient) * | |
sin(Double(resolution) * | |
resolutionIncrement) | |
XCPCaptureValue("Y Coordinates", y) | |
return y | |
} | |
func pathPoints(resolution : Double, | |
amplitude: Double, | |
phase: Double, | |
frequency: Double) -> (points: CGPoint[], | |
minX: Double, | |
maxX: Double, | |
minY: Double, | |
maxY: Double) { | |
let resolutionIncrement = (2.0*M_PI)/resolution | |
var points = CGPoint[]() | |
var minX = 1000000.0 | |
var maxX = -1000000.0 | |
var minY = 1000000.0 | |
var maxY = -1000000.0 | |
for var i = 1; i < Int(resolution)+1; i++ { | |
let sinValue = sinCoefficient(phase, frequency, Double(i), resolutionIncrement) | |
let x = xPoint(Double(i), | |
resolutionIncrement, | |
amplitude, | |
sinValue) | |
let y = yPoint(Double(i), | |
resolutionIncrement, | |
amplitude, | |
sinValue) | |
points.append(CGPoint(x: x, y: y)) | |
if (x < minX) { | |
minX = x; | |
} else if (x > maxX) { | |
maxX = x; | |
} | |
if (y < minY) { | |
minY = y; | |
} else if (y > maxY) { | |
maxY = y; | |
} | |
} | |
return (points, minX, maxX, minY, maxY) | |
} | |
func logoPoints(iterations: NSInteger, | |
resolution : Double, | |
amplitude: Double, | |
frequency: Double) -> (points: CGPoint[][], | |
xScaler: Double, | |
yScaler: Double) { | |
var points = CGPoint[][]() | |
var minX = 1000000.0 | |
var maxX = -1000000.0 | |
var minY = 1000000.0 | |
var maxY = -1000000.0 | |
for i in 0..iterations { | |
let logoValues = pathPoints( | |
100.0, | |
amplitude, | |
Double(i)*(2.0*M_PI)/Double(iterations), | |
frequency) | |
points.append(logoValues.points) | |
if (logoValues.minX < minX) { | |
minX = logoValues.minX; | |
} | |
if (logoValues.maxX > maxX) { | |
maxX = logoValues.maxX; | |
} | |
if (logoValues.minY < minY) { | |
minY = logoValues.minY; | |
} | |
if (logoValues.maxY > maxY) { | |
maxY = logoValues.maxY; | |
} | |
} | |
return (points, maxX - minX, maxY - minY) | |
} | |
func logoPaths(points : CGPoint[][], | |
xScaler: Double, | |
yScaler: Double) -> NSBezierPath[] { | |
var paths = NSBezierPath[]() | |
for pointsCollection : CGPoint[] in points { | |
var path = NSBezierPath() | |
for var i = 0; i < pointsCollection.count; i++ { | |
var point = CGPoint(x: (pointsCollection[i].x/xScaler)*100, | |
y: (pointsCollection[i].y/yScaler)*100) | |
var finalPoint = CGPoint(x: point.x+50, y: point.y+50) | |
if i == 0 { | |
path.moveToPoint(finalPoint) | |
} else { | |
path.lineToPoint(finalPoint) | |
} | |
if (i == pointsCollection.count-1) { | |
path.closePath() | |
path | |
} | |
} | |
paths.append(path) | |
XCPCaptureValue("Paths", path) | |
} | |
return paths | |
} | |
class LogoView : NSView { | |
var path : NSBezierPath | |
init(path : NSBezierPath, frame: CGRect) { | |
self.path = path | |
super.init(frame: frame) | |
} | |
override var wantsDefaultClipping : Bool { | |
return false | |
} | |
override func drawRect(dirtyRect: NSRect) { | |
NSColor.blueColor().set() | |
self.path.stroke() | |
super.drawRect(dirtyRect) | |
} | |
} | |
showMessage("Today we are going to draw a Guilloche! ", 1) | |
showMessage("First we need to get the X and Y coordinates that will make up our shape.", 2) | |
let iterations = 5 | |
let defaultAmplitude = 0.4 | |
let defaultFrequency = 4.0 | |
let simpleAmplitude = 0.2 | |
let simpleFrequency = 2.0 | |
let amplitude = defaultAmplitude | |
let frequency = defaultFrequency | |
let logoValues = logoPoints(iterations, 100.0, amplitude, frequency) | |
showMessage("Those look great! Let's turn them into NSBezierPaths now", 3) | |
var paths = logoPaths(logoValues.points, logoValues.xScaler, logoValues.yScaler) | |
var myView = NSView(frame: CGRect(origin: CGPointZero, size: CGSize(width: 110, height: 110))) | |
showMessage("Cool! Let's put all these paths together in a view", 4) | |
for path in paths { | |
var logoView = LogoView(path: path, | |
frame: CGRectInset(myView.frame, 5.0, 5.0)) | |
myView.addSubview(logoView) | |
XCPCaptureValue("ViewBuilder", myView) | |
} | |
XCPShowView("Logo", myView) | |
if (amplitude == defaultAmplitude && frequency == defaultFrequency) { | |
showMessage("You can go back into the code and change the amplitude and frequency to make other shapes on lines 194 and 195", 5) | |
} else if (amplitude == simpleAmplitude && frequency == simpleFrequency) { | |
showMessage("Hot damn! That's a nice looking logo, where have I seen that before?", 5) | |
} else { | |
showMessage("Nice work, that's a sweet design!", 5) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment