]> git.r.bdr.sh - rbdr/map/blob - Map/MapRender.swift
Initial commit
[rbdr/map] / Map / MapRender.swift
1 //
2 // ContentView.swift
3 // Map
4 //
5 // Created by Ruben Beltran del Rio on 2/1/21.
6 //
7
8 import SwiftUI
9 import CoreData
10 import CoreGraphics
11
12 struct MapRenderView: View {
13 @ObservedObject var map: Map
14 let evolution: Stage
15
16 let VERTEX_SIZE = CGSize(width: 25.0, height: 25.0)
17 let ARROWHEAD_SIZE = CGFloat(10.0)
18 let LINE_WIDTH = CGFloat(1.0)
19 let PADDING = CGFloat(4.0)
20 let STAGE_FRAME = CGSize(width: 245, height: 50)
21
22 var parsedMap: ParsedMap {
23 return map.parse()
24 }
25
26 var body: some View {
27 ZStack(alignment: .topLeading) {
28 ZStack (alignment: .topLeading) {
29 // The Axes
30 Path { path in
31 path.move(to: CGPoint(x: 0, y: 0))
32 path.addLine(to: CGPoint(x: 0, y: 1000))
33 path.addLine(to: CGPoint(x: 1000, y: 1000))
34 path.move(to: CGPoint(x: 1000, y: 1000))
35 path.closeSubpath()
36
37 }.strokedPath(StrokeStyle(lineWidth: LINE_WIDTH * 2))
38 Text("Visible").font(.title3).rotationEffect(Angle(degrees: -90.0)).offset(CGSize(width: -35.0, height: 0.0))
39 Text("Value Chain").font(.title).rotationEffect(Angle(degrees: -90.0)).offset(CGSize(width: -72.0, height: 480.0))
40 Text("Invisible").font(.title3).rotationEffect(Angle(degrees: -90.0)).offset(CGSize(width: -40.0, height: 980.0))
41 Text(evolution.I).font(.title3).frame(width: STAGE_FRAME.width, height: STAGE_FRAME.height, alignment: .topLeading).offset(CGSize(width: 0.0, height: 1000.0))
42 Text(evolution.II).font(.title3).frame(width: STAGE_FRAME.width, height: STAGE_FRAME.height, alignment: .topLeading).offset(CGSize(width: 250.0, height: 1000.0))
43 Text(evolution.III).font(.title3).frame(width: STAGE_FRAME.width, height: STAGE_FRAME.height, alignment: .topLeading).offset(CGSize(width: 500.0, height: 1000.0))
44 Text(evolution.IV).font(.title3).frame(width: STAGE_FRAME.width, height: STAGE_FRAME.height, alignment: .topLeading).offset(CGSize(width: 750.0, height: 1000.0))
45 }.offset(CGSize(width: 0.0, height: 0.0))
46
47 // The Lanes
48 Path { path in
49 path.move(to: CGPoint(x: 250, y: 0))
50 path.addLine(to: CGPoint(x: 250, y: 1000))
51 path.move(to: CGPoint(x: 500, y: 0))
52 path.addLine(to: CGPoint(x: 500, y: 1000))
53 path.move(to: CGPoint(x: 750, y: 0))
54 path.addLine(to: CGPoint(x: 750, y: 1000))
55 path.move(to: CGPoint(x: 250, y: 0))
56 path.closeSubpath()
57 }.strokedPath(StrokeStyle(lineWidth: LINE_WIDTH, dash: [10.0]))
58
59 Path { path in
60 path.addRect(CGRect(x: 0, y: 0, width: 250, height: 1000))
61 }.fill(Color.red).opacity(0.1)
62 Path { path in
63 path.addRect(CGRect(x: 250, y: 0, width: 250, height: 1000))
64 }.fill(Color.orange).opacity(0.1)
65 Path { path in
66 path.addRect(CGRect(x: 500, y: 0, width: 250, height: 1000))
67 }.fill(Color.yellow).opacity(0.1)
68 Path { path in
69 path.addRect(CGRect(x: 750, y: 0, width: 250, height: 1000))
70 }.fill(Color.green).opacity(0.1)
71
72 // The Vertices
73
74 ForEach(parsedMap.vertices, id: \.id) { vertex in
75 Path { path in
76 path.addEllipse(in: CGRect(
77 origin: vertex.position, size: VERTEX_SIZE
78 ))
79 }
80 Text(vertex.label).foregroundColor(Color.gray).offset(CGSize(
81 width: vertex.position.x + VERTEX_SIZE.width + PADDING,
82 height: vertex.position.y))
83 }
84
85 // The Edges
86
87 ForEach(parsedMap.edges, id: \.id) { edge in
88 Path { path in
89
90 let slope = (edge.destination.y - edge.origin.y) / (edge.destination.x - edge.origin.x)
91 let angle = atan(slope)
92 let multiplier = CGFloat(slope < 0 ? -1.0 : 1.0)
93 let upperAngle = angle - CGFloat.pi / 4.0
94 let lowerAngle = angle + CGFloat.pi / 4.0
95
96 let offsetOrigin = CGPoint(x: edge.origin.x + multiplier * (VERTEX_SIZE.width / 2.0) * cos(angle), y: edge.origin.y + multiplier * (VERTEX_SIZE.height / 2.0) * sin(angle))
97 let offsetDestination = CGPoint(x: edge.destination.x - multiplier * (VERTEX_SIZE.width / 2.0) * cos(angle), y: edge.destination.y - multiplier * (VERTEX_SIZE.height / 2.0) * sin(angle))
98
99 path.move(to: offsetOrigin)
100 path.addLine(to: offsetDestination)
101
102 // Arrowheads
103 path.move(to: offsetDestination)
104 path.addLine(to: CGPoint(x: offsetDestination.x - multiplier * ARROWHEAD_SIZE * cos(upperAngle), y:
105 offsetDestination.y - multiplier * ARROWHEAD_SIZE * sin(upperAngle)))
106
107 path.move(to: offsetDestination)
108 path.addLine(to: CGPoint(x: offsetDestination.x - multiplier * ARROWHEAD_SIZE * cos(lowerAngle), y:
109 offsetDestination.y - multiplier * ARROWHEAD_SIZE * sin(lowerAngle)))
110 path.move(to: offsetDestination)
111 path.closeSubpath()
112 }.applying(CGAffineTransform(translationX: VERTEX_SIZE.width / 2.0, y: VERTEX_SIZE.height / 2.0)).strokedPath(StrokeStyle(lineWidth: LINE_WIDTH))
113 }
114 }.frame(
115 minWidth: 1050.0, maxWidth: .infinity,
116 minHeight: 1050.0, maxHeight: .infinity, alignment: .topLeading
117 ).padding(30.0)
118 }
119 }
120
121 struct MapRenderView_Previews: PreviewProvider {
122 static var previews: some View {
123 MapDetailView(map: Map()).environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
124 }
125 }