Skip to content

Instantly share code, notes, and snippets.

@cruffenach
Last active June 30, 2018 13:23
Show Gist options
  • Save cruffenach/45a98ce6f2a49b5e859b to your computer and use it in GitHub Desktop.
Save cruffenach/45a98ce6f2a49b5e859b to your computer and use it in GitHub Desktop.
Guilloche Drawing in Playgrounds
// 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