Skip to content

Commit

Permalink
Updated Diagrams playground
Browse files Browse the repository at this point in the history
  • Loading branch information
alskipp committed Sep 20, 2016
1 parent 9c6db69 commit 32ff97d
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 96 deletions.
Expand Up @@ -4,15 +4,15 @@
import CoreGraphics

extension Diagram {
func ring(radius radius: CGFloat, number: Int) -> Diagram {
let angles = 0.0.stride(to: 360.0, by: 360.0/Double(number))
func ring(radius: CGFloat, number: Int) -> Diagram {
let angles = stride(from: 0.0, to: 360.0, by: 360.0/Double(number))
return diagrams(angles.map {
self.translate(x: 0, y: radius).rotate(CGFloat($0))
translate(x: 0, y: radius).rotate(CGFloat($0))
}
)
}

func iterateScale(s: CGFloat, offset: Point = (0,0), iterate: Int) -> Diagram {
func iterateScale(_ s: CGFloat, offset: Point = (0,0), iterate: Int) -> Diagram {
if iterate == 0 { return self }
return self + scale(s)
.translate(x: offset.x, y: offset.y)
Expand All @@ -26,7 +26,7 @@ let triangleRing = triangle.ring(radius: 220, number: 27)
let diagram = triangleRing.iterateScale(0.618, offset: (15, 30), iterate: 7)

showCoreGraphicsDiagram(size: CGSize(width: 600, height: 500)) {
drawDiagram(diagram)(context: $0)
drawDiagram(diagram, context: $0)
}
/*:
To see the result, View>Assistant Editor>Show Assistant Editor (opt-cmd-Return).
Expand Down
Expand Up @@ -3,15 +3,15 @@
import CoreGraphics

extension Diagram {
func ring(radius radius: CGFloat, number: Int) -> Diagram {
let angles = 0.0.stride(to: 360.0, by: 360.0/Double(number))
func ring(radius: CGFloat, number: Int) -> Diagram {
let angles = stride(from: 0.0, to: 360.0, by: 360.0/Double(number))
return diagrams(angles.map {
translate(x: 0, y: radius).rotate(CGFloat($0))
}
)
}

func iterateScale(s: CGFloat, iterate: Int) -> Diagram {
func iterateScale(_ s: CGFloat, iterate: Int) -> Diagram {
if iterate == 0 { return self }
return self + scale(s).iterateScale(s, iterate: iterate - 1)
}
Expand All @@ -26,7 +26,7 @@ let starRing = star.ring(radius: 185, number: 16)
let diagram = starRing.iterateScale(0.74, iterate: 6)

showCoreGraphicsDiagram(size: CGSize(width: 600, height: 500)) {
drawDiagram(diagram)(context: $0)
drawDiagram(diagram, context: $0)
}
/*:
To see the result, View>Assistant Editor>Show Assistant Editor (opt-cmd-Return).
Expand Down
Expand Up @@ -3,16 +3,8 @@
## The Larch
*/
import CoreGraphics

func displayDiagram(diagram: Diagram) {
showCoreGraphicsDiagram(size: CGSize(width: 600, height: 600)) {
drawDiagram(diagram)(context: $0)
}
}

extension Diagram {
func tree(n: Int) -> Diagram {
func tree(_ n: Int) -> Diagram {
if n == 0 { return self }

let smallTree = tree(n - 1).scale(x: 0.33, y: 0.45)
Expand Down
10 changes: 1 addition & 9 deletions Diagrams.playground/Pages/Tree.xcplaygroundpage/Contents.swift
Expand Up @@ -3,16 +3,8 @@
## I want to be a tree
*/
import CoreGraphics

func displayDiagram(diagram: Diagram) {
showCoreGraphicsDiagram(size: CGSize(width: 600, height: 600)) {
drawDiagram(diagram)(context: $0)
}
}

extension Diagram {
func tree(n: Int) -> Diagram {
func tree(_ n: Int) -> Diagram {
if n == 0 { return self }

let smallTree = tree(n - 1).scale(x: 0.6, y: 0.67)
Expand Down
48 changes: 23 additions & 25 deletions Diagrams.playground/Sources/CGContextExtension.swift
@@ -1,49 +1,47 @@
import CoreGraphics

public extension CGContext {
func moveTo(position: CGPoint) {
CGContextMoveToPoint(self, position.x, position.y)
}
func lineTo(position: CGPoint) {
CGContextAddLineToPoint(self, position.x, position.y)
}
func drawPath(points: [CGPoint]) {
if let p = points.first { moveTo(p) }
points.forEach(lineTo)
func drawPath(_ points: [CGPoint]) {
if let p = points.first {
move(to: p)
points.forEach { addLine(to: $0) }
}
}
func drawPolygon(points: [CGPoint]) {
if let p = points.last { moveTo(p) }
points.forEach(lineTo)
func drawPolygon(_ points: [CGPoint]) {
if let p = points.last {
move(to: p)
points.forEach { addLine(to: $0) }
}
}
func arc(radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat) {
let arc = CGPathCreateMutable()
CGPathAddArc(arc, nil, 0, 0, radius, startAngle, endAngle, false)
CGContextAddPath(self, arc)
let arc = CGMutablePath()
arc.addArc(center: .zero, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
addPath(arc)
}
func circle(radius: CGFloat) {
arc(radius, startAngle: 0.0, endAngle: twoPi)
arc(radius: radius, startAngle: 0.0, endAngle: twoPi)
}
func saveContext(operation: () -> ()) {
CGContextSaveGState(self)
saveGState()
operation()
CGContextRestoreGState(self)
restoreGState()
}
func scale(x: CGFloat, _ y: CGFloat, operation: CGContext -> ()) {
func scale(x: CGFloat, y: CGFloat, operation: (CGContext) -> ()) {
saveContext {
CGContextScaleCTM(self, x, y)
scaleBy(x: x, y: y)
operation(self)
}
}
func translate(x: CGFloat, _ y: CGFloat, operation: CGContext -> ()) {
func translate(x: CGFloat, y: CGFloat, operation: (CGContext) -> ()) {
saveContext {
CGContextTranslateCTM(self, x, y)
translateBy(x: x, y: y)
operation(self)
}
}
func rotate(angle: CGFloat, operation: CGContext -> ()) {
func rotate(angle: CGFloat, operation: (CGContext) -> ()) {
saveContext {
CGContextRotateCTM(self, angle)
rotate(by: angle)
operation(self)
}
}
}
}
34 changes: 20 additions & 14 deletions Diagrams.playground/Sources/CoreGraphicsDiagramView.swift
@@ -1,24 +1,24 @@
import UIKit
import XCPlayground
import PlaygroundSupport

/// `CoreGraphicsDiagramView` is a `UIView` that draws itself by calling a
/// user-supplied function to generate paths in a `CGContext`, then strokes
/// the context's current path, creating lines in a pleasing shade of blue.
class CoreGraphicsDiagramView : UIView {
override func drawRect(rect: CGRect) {
override func draw(_ rect: CGRect) {
if let context = UIGraphicsGetCurrentContext() {
CGContextSaveGState(context)
let flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, frame.height)
CGContextConcatCTM(context, flipVertical);
CGContextTranslateCTM(context, frame.width/2, frame.height/2)
context.saveGState()
let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: frame.height)
context.concatenate(flipVertical)
context.translateBy(x: frame.width/2, y: frame.height/2)

draw(context)

let lightBlue = UIColor(red: 0.222, green: 0.617, blue: 0.976, alpha: 1.0).CGColor
CGContextSetStrokeColorWithColor(context, lightBlue)
CGContextSetLineWidth(context, 2)
CGContextStrokePath(context)
CGContextRestoreGState(context)
let lightBlue = UIColor(red: 0.222, green: 0.617, blue: 0.976, alpha: 1.0).cgColor
context.setStrokeColor(lightBlue)
context.setLineWidth(2)
context.strokePath()
context.restoreGState()
}
}

Expand All @@ -28,9 +28,15 @@ class CoreGraphicsDiagramView : UIView {
/// Shows a `UIView` in the current playground that draws itself by invoking
/// `draw` on a `CGContext`, then stroking the context's current path in a
/// pleasing light blue.
public func showCoreGraphicsDiagram(size size: CGSize, draw: (CGContext)->()) {
let diagramView = CoreGraphicsDiagramView(frame: CGRect(origin: CGPointZero, size: size))
public func showCoreGraphicsDiagram(size: CGSize, draw: @escaping (CGContext)->()) {
let diagramView = CoreGraphicsDiagramView(frame: CGRect(origin: .zero, size: size))
diagramView.draw = draw
diagramView.setNeedsDisplay()
XCPlaygroundPage.currentPage.liveView = diagramView
PlaygroundPage.current.liveView = diagramView
}

public func displayDiagram(_ diagram: Diagram) {
showCoreGraphicsDiagram(size: CGSize(width: 600, height: 600)) {
drawDiagram(diagram, context: $0)
}
}
59 changes: 29 additions & 30 deletions Diagrams.playground/Sources/Diagram.swift
Expand Up @@ -5,61 +5,61 @@ let toRadians = { $0 * CGFloat(M_PI / 180) }
public typealias Point = (x: CGFloat, y: CGFloat)

//: A `Diagram` as a recursive enum
public indirect enum Diagram {
case Polygon(corners: [CGPoint])
case Line(points: [CGPoint])
public enum Diagram {
case Polygon([CGPoint])
case Line([CGPoint])
case Arc(radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat)
case Circle(radius: CGFloat)
case Scale(x: CGFloat, y: CGFloat, diagram: Diagram)
case Translate(x: CGFloat, y: CGFloat, diagram: Diagram)
case Rotate(angle: CGFloat, diagram: Diagram)
indirect case Scale(x: CGFloat, y: CGFloat, diagram: Diagram)
indirect case Translate(x: CGFloat, y: CGFloat, diagram: Diagram)
indirect case Rotate(angle: CGFloat, diagram: Diagram)
case Diagrams([Diagram])
}

// convenience methods to allow chaining of transformations
public extension Diagram {
func scale(s: CGFloat) -> Diagram {
func scale(_ s: CGFloat) -> Diagram {
return .Scale(x: s, y: s, diagram: self)
}

func scale(x x: CGFloat, y: CGFloat) -> Diagram {
func scale(x: CGFloat, y: CGFloat) -> Diagram {
return .Scale(x: x, y: y, diagram: self)
}

func translate(x x: CGFloat, y: CGFloat) -> Diagram {
func translate(x: CGFloat, y: CGFloat) -> Diagram {
return .Translate(x: x, y: y, diagram: self)
}

func rotate(x: CGFloat) -> Diagram {
func rotate(_ x: CGFloat) -> Diagram {
return .Rotate(angle: x, diagram: self)
}
}

// convenience functions to handle conversion from `Point` to `CGPoint`
// (defining an Array of `CGpoint`s is really verbose – hence the use of the `tuple` typealias `Point`)
public func line(ps: [Point]) -> Diagram {
return .Line(points: ps.map { x, y in CGPoint(x: x, y: y) })
return .Line(ps.map { x, y in CGPoint(x: x, y: y) })
}

public func polygon(ps: [Point]) -> Diagram {
return .Polygon(corners: ps.map { x, y in CGPoint(x: x, y: y) })
public func polygon(_ ps: [Point]) -> Diagram {
return .Polygon(ps.map { x, y in CGPoint(x: x, y: y) })
}

public func rectanglePath(width: CGFloat, _ height: CGFloat) -> [Point] {
public func rectanglePath(width: CGFloat, height: CGFloat) -> [Point] {
let sx = width / 2
let sy = height / 2
return [(-sx, -sy), (-sx, sy), (sx, sy), (sx, -sy)]
}

public func rectangle(x: CGFloat, _ y: CGFloat) -> Diagram {
return polygon(rectanglePath(x, y))
return polygon(rectanglePath(width: x, height: y))
}

public func circle(radius: CGFloat) -> Diagram {
public func circle(_ radius: CGFloat) -> Diagram {
return .Circle(radius: radius)
}

public func diagrams(diagrams: [Diagram]) -> Diagram {
public func diagrams(_ diagrams: [Diagram]) -> Diagram {
return .Diagrams(diagrams)
}

Expand Down Expand Up @@ -97,14 +97,14 @@ public func == (lhs: Diagram, rhs: Diagram) -> Bool {
}

//: Infix operator for combining Diagrams
public func + (d1:Diagram, d2:Diagram) -> Diagram {
public func + (d1: Diagram, d2: Diagram) -> Diagram {
return diagrams([d1, d2])
}

//: ## Do some drawing!
//:
//: A recursive function responsible for drawing a diagram into a CGContext
public func drawDiagram(diagram: Diagram)(context: CGContext) -> () {
public func drawDiagram(_ diagram: Diagram, context: CGContext) -> () {
switch diagram {
case let .Polygon(corners):
context.drawPolygon(corners)
Expand All @@ -113,28 +113,27 @@ public func drawDiagram(diagram: Diagram)(context: CGContext) -> () {
context.drawPath(points)

case let .Arc(r, a1, a2):
context.arc(r, startAngle: toRadians(a1), endAngle: toRadians(a2))
context.arc(radius: r, startAngle: toRadians(a1), endAngle: toRadians(a2))

case let .Circle(radius):
context.circle(radius)
context.circle(radius: radius)

case let .Scale(x, y, diagram):
context.scale(x, y) {
drawDiagram(diagram)(context: $0)
context.scale(x: x, y: y) {
drawDiagram(diagram, context: $0)
}

case let .Translate(x, y, diagram):
context.translate(x, y) {
drawDiagram(diagram)(context: $0)
context.translate(x: x, y: y) {
drawDiagram(diagram, context: $0)
}

case let .Rotate(angle, diagram):
context.rotate(toRadians(angle)) {
drawDiagram(diagram)(context: $0)
context.rotate(angle: toRadians(angle)) {
drawDiagram(diagram, context: $0)
}

case let .Diagrams(diagrams):
diagrams.forEach { d in drawDiagram(d)(context: context) }
diagrams.forEach { d in drawDiagram(d, context: context) }
}
}

0 comments on commit 32ff97d

Please sign in to comment.