Advertisement
Guest User

Untitled

a guest
Aug 30th, 2016
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.26 KB | None | 0 0
  1. import CoreGraphics
  2. let twoPi = CGFloat(M_PI * 2)
  3.  
  4. //: A protocol for types that respond to primitive graphics commands. We
  5. //: start with the basics:
  6. protocol Renderer {
  7. /// Moves the pen to `position` without drawing anything.
  8. func moveTo(position: CGPoint)
  9.  
  10. /// Draws a line from the pen's current position to `position`, updating
  11. /// the pen position.
  12. func lineTo(position: CGPoint)
  13.  
  14. /// Draws the fragment of the circle centered at `c` having the given
  15. /// `radius`, that lies between `startAngle` and `endAngle`, measured in
  16. /// radians.
  17. func arcAt(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat)
  18. }
  19.  
  20. //: A `Renderer` that prints to the console.
  21. //:
  22. //: Printing the drawing commands comes in handy for debugging; you
  23. //: can't always see everything by looking at graphics. For an
  24. //: example, see the "nested diagram" section below.
  25. struct TestRenderer : Renderer {
  26. func moveTo(p: CGPoint) { print("moveTo(\(p.x), \(p.y))") }
  27.  
  28. func lineTo(p: CGPoint) { print("lineTo(\(p.x), \(p.y))") }
  29.  
  30. func arcAt(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat) {
  31. print("arcAt(\(center), radius: \(radius)," + " startAngle: \(startAngle), endAngle: \(endAngle))")
  32. }
  33. }
  34.  
  35. //: An element of a `Diagram`. Concrete examples follow.
  36. protocol Drawable {
  37. /// Issues drawing commands to `renderer` to represent `self`.
  38. func draw(renderer: Renderer)
  39. }
  40.  
  41. //: Basic `Drawable`s
  42. struct Polygon : Drawable {
  43. func draw(renderer: Renderer) {
  44. renderer.moveTo(corners.last!)
  45. for p in corners { renderer.lineTo(p) }
  46. }
  47. var corners: [CGPoint] = []
  48. }
  49.  
  50. struct Circle : Drawable {
  51. func draw(renderer: Renderer) {
  52. renderer.arcAt(center, radius: radius, startAngle: 0.0, endAngle: twoPi)
  53. }
  54. var center: CGPoint
  55. var radius: CGFloat
  56. }
  57.  
  58. //: Now a `Diagram`, which contains a heterogeneous array of `Drawable`s
  59. /// A group of `Drawable`s
  60. struct Diagram : Drawable {
  61. func draw(renderer: Renderer) {
  62. for f in elements {
  63. f.draw(renderer)
  64. }
  65. }
  66. mutating func add(other: Drawable) {
  67. elements.append(other)
  68. }
  69. var elements: [Drawable] = []
  70. }
  71.  
  72. //: ## Retroactive Modeling
  73. //:
  74. //: Here we extend `CGContext` to make it a `Renderer`. This would
  75. //: not be possible if `Renderer` were a base class rather than a
  76. //: protocol.
  77. extension CGContext : Renderer {
  78. func moveTo(position: CGPoint) {
  79. CGContextMoveToPoint(self, position.x, position.y)
  80. }
  81. func lineTo(position: CGPoint) {
  82. CGContextAddLineToPoint(self, position.x, position.y)
  83. }
  84. func arcAt(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat) {
  85. let arc = CGPathCreateMutable()
  86. CGPathAddArc(arc, nil, center.x, center.y, radius, startAngle, endAngle, true)
  87. CGContextAddPath(self, arc)
  88. }
  89. }
  90.  
  91. var circle = Circle(center: CGPoint(x: 187.5, y: 333.5), radius: 93.75)
  92.  
  93. var triangle = Polygon(corners: [
  94. CGPoint(x: 187.5, y: 427.25),
  95. CGPoint(x: 268.69, y: 286.625),
  96. CGPoint(x: 106.31, y: 286.625)])
  97.  
  98. var diagram = Diagram(elements: [circle, triangle])
  99.  
  100. //: ## Putting a `Diagram` inside itself
  101. //:
  102. //: If `Diagram`s had reference semantics, we could easily cause an infinite
  103. //: recursion in drawing just by inserting a `Diagram` into its own array of
  104. //: `Drawable`s. However, value semantics make this operation entirely
  105. //: benign.
  106. //:
  107. //: To ensure that the result can be observed visually, we need to
  108. //: alter the inserted diagram somehow; otherwise, all the elements
  109. //: would line up exactly with existing ones. This is a nice
  110. //: demonstration of generic adapters in action.
  111. //:
  112. //: We start by creating a `Drawable` wrapper that applies scaling to
  113. //: some underlying `Drawable` instance; then we can wrap it around
  114. //: the diagram.
  115.  
  116. /// A `Renderer` that passes drawing commands through to some `base`
  117. /// renderer, after applying uniform scaling to all distances.
  118. struct ScaledRenderer : Renderer {
  119. let base: Renderer
  120. let scale: CGFloat
  121.  
  122. func moveTo(p: CGPoint) {
  123. base.moveTo(CGPoint(x: p.x * scale, y: p.y * scale))
  124. }
  125.  
  126. func lineTo(p: CGPoint) {
  127. base.lineTo(CGPoint(x: p.x * scale, y: p.y * scale))
  128. }
  129.  
  130. func arcAt(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat) {
  131. let scaledCenter = CGPoint(x: center.x * scale, y: center.y * scale)
  132. base.arcAt(scaledCenter, radius: radius * scale, startAngle: startAngle, endAngle: endAngle)
  133. }
  134. }
  135.  
  136. /// A `Drawable` that scales an instance of `Base`
  137. struct Scaled<Base: Drawable> : Drawable {
  138. var scale: CGFloat
  139. var subject: Base
  140.  
  141. func draw(renderer: Renderer) {
  142. subject.draw(ScaledRenderer(base: renderer, scale: scale))
  143. }
  144. }
  145.  
  146. // Now insert it.
  147. diagram.elements.append(Scaled(scale: 0.3, subject: diagram))
  148.  
  149. // Dump the diagram to the console. Use View>Debug Area>Show Debug
  150. // Area (shift-cmd-Y) to observe the output.
  151. diagram.draw(TestRenderer())
  152.  
  153. // Also show it in the view. To see the result, View>Assistant
  154. // Editor>Show Assistant Editor (opt-cmd-Return).
  155. showCoreGraphicsDiagram("Diagram") { diagram.draw($0) }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement