]> git.r.bdr.sh - rbdr/map/blame - Map/Extensions/Map+parse.swift
Add code for first release
[rbdr/map] / Map / Extensions / Map+parse.swift
CommitLineData
1b85f723
RBR
1import CoreGraphics
2import Foundation
3
5e8ff485
RBR
4let vertexPattern =
5 "([^\\(]+?)[\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)"
6let edgePattern = "(.+?)[\\s]*-([->])[\\s]*(.+)"
7let blockerPattern = "\\[Blocker\\][\\s]*(.+)"
8let opportunityPattern = "\\[Opportunity\\][\\s]*(.+)[\\s]+([-+])[\\s]*([0-9]+.?[0-9]*)"
9let stagePattern = "\\[(I{1,3})\\][\\s]*([0-9]+.?[0-9]*)"
1b85f723
RBR
10
11struct ParsedMap {
5e8ff485
RBR
12 let vertices: [Vertex]
13 let edges: [MapEdge]
14 let blockers: [Blocker]
15 let opportunities: [Opportunity]
16 let stages: [CGFloat]
1b85f723
RBR
17}
18
19struct Vertex {
5e8ff485
RBR
20 let id: Int
21 let label: String
22 let position: CGPoint
1b85f723
RBR
23}
24
25struct MapEdge {
5e8ff485
RBR
26 let id: Int
27 let origin: CGPoint
28 let destination: CGPoint
29 let arrowhead: Bool
1b85f723
RBR
30}
31
5e8ff485
RBR
32struct Blocker {
33 let id: Int
34 let position: CGPoint
35}
36
37struct Opportunity {
38 let id: Int
39 let origin: CGPoint
40 let destination: CGPoint
41}
42
43let defaultDimensions: [CGFloat] = [
44 25.0,
45 50.0,
46 75.0,
47]
48
1b85f723
RBR
49// Extracts the vertices from the text
50
51func parseVertices(_ text: String) -> [String: CGPoint] {
5e8ff485
RBR
52
53 var result: [String: CGPoint] = [:]
54 let regex = try! NSRegularExpression(pattern: vertexPattern, options: .caseInsensitive)
55
56 let lines = text.split(whereSeparator: \.isNewline)
57
58 for line in lines {
59 let range = NSRange(location: 0, length: line.utf16.count)
60 let matches = regex.matches(in: String(line), options: [], range: range)
61
62 if matches.count > 0 && matches[0].numberOfRanges == 4 {
63
64 let match = matches[0]
65 let key = String(line[Range(match.range(at: 1), in: line)!])
66 let xString = String(line[Range(match.range(at: 2), in: line)!])
67 let yString = String(line[Range(match.range(at: 3), in: line)!])
68 let x = CGFloat(truncating: NumberFormatter().number(from: xString) ?? 0.0)
69 let y = CGFloat(truncating: NumberFormatter().number(from: yString) ?? 0.0)
70 let point = CGPoint(x: x, y: y)
71
72 result[key] = point
1b85f723 73 }
5e8ff485 74 }
1b85f723 75
5e8ff485
RBR
76 return result
77}
1b85f723
RBR
78
79func parseEdges(_ text: String, vertices: [String: CGPoint]) -> [MapEdge] {
5e8ff485
RBR
80
81 var result: [MapEdge] = []
82 let regex = try! NSRegularExpression(pattern: edgePattern, options: .caseInsensitive)
83
84 let lines = text.split(whereSeparator: \.isNewline)
85
86 for (index, line) in lines.enumerated() {
87 let range = NSRange(location: 0, length: line.utf16.count)
88 let matches = regex.matches(in: String(line), options: [], range: range)
89
90 if matches.count > 0 && matches[0].numberOfRanges == 4 {
91
92 let match = matches[0]
93 let arrowhead = String(line[Range(match.range(at: 2), in: line)!]) == ">"
94 let vertexA = String(line[Range(match.range(at: 1), in: line)!])
95 let vertexB = String(line[Range(match.range(at: 3), in: line)!])
96
97 if let origin = vertices[vertexA] {
98 if let destination = vertices[vertexB] {
99 result.append(
100 MapEdge(id: index, origin: origin, destination: destination, arrowhead: arrowhead))
1b85f723 101 }
5e8ff485 102 }
1b85f723 103 }
5e8ff485
RBR
104 }
105
106 return result
107}
108
109func parseOpportunities(_ text: String, vertices: [String: CGPoint]) -> [Opportunity] {
110
111 var result: [Opportunity] = []
112 let regex = try! NSRegularExpression(pattern: opportunityPattern, options: .caseInsensitive)
113
114 let lines = text.split(whereSeparator: \.isNewline)
115
116 for (index, line) in lines.enumerated() {
117 let range = NSRange(location: 0, length: line.utf16.count)
118 let matches = regex.matches(in: String(line), options: [], range: range)
119
120 if matches.count > 0 && matches[0].numberOfRanges == 4 {
121
122 let match = matches[0]
123 let multiplier = CGFloat(
124 String(line[Range(match.range(at: 2), in: line)!]) == "-" ? -1.0 : 1.0)
125 let vertex = String(line[Range(match.range(at: 1), in: line)!])
126 let opportunityString = String(line[Range(match.range(at: 3), in: line)!])
127 let opportunity = CGFloat(
128 truncating: NumberFormatter().number(from: opportunityString) ?? 0.0)
129
130 if let origin = vertices[vertex] {
131 let destination = CGPoint(x: origin.x + opportunity * multiplier, y: origin.y)
132 result.append(Opportunity(id: index, origin: origin, destination: destination))
133 }
134 }
135 }
136
137 return result
138}
139
140func parseBlockers(_ text: String, vertices: [String: CGPoint]) -> [Blocker] {
141
142 var result: [Blocker] = []
143 let regex = try! NSRegularExpression(pattern: blockerPattern, options: .caseInsensitive)
144
145 let lines = text.split(whereSeparator: \.isNewline)
146
147 for (index, line) in lines.enumerated() {
148 let range = NSRange(location: 0, length: line.utf16.count)
149 let matches = regex.matches(in: String(line), options: [], range: range)
150
151 if matches.count > 0 && matches[0].numberOfRanges == 2 {
152
153 let match = matches[0]
154 let vertexA = String(line[Range(match.range(at: 1), in: line)!])
155
156 if let position = vertices[vertexA] {
157 result.append(Blocker(id: index, position: position))
158 }
159 }
160 }
161
162 return result
163}
164
165func parseStages(_ text: String) -> [CGFloat] {
166
167 var result = defaultDimensions
168 let regex = try! NSRegularExpression(pattern: stagePattern, options: .caseInsensitive)
169
170 let lines = text.split(whereSeparator: \.isNewline)
171
172 for line in lines {
173 let range = NSRange(location: 0, length: line.utf16.count)
174 let matches = regex.matches(in: String(line), options: [], range: range)
175
176 if matches.count > 0 && matches[0].numberOfRanges == 3 {
177
178 let match = matches[0]
179 let stage = String(line[Range(match.range(at: 1), in: line)!])
180 let dimensionsString = String(line[Range(match.range(at: 2), in: line)!])
181 let dimensions = CGFloat(truncating: NumberFormatter().number(from: dimensionsString) ?? 0.0)
182
183 result[stage.count - 1] = dimensions
184 }
185 }
186
187 return result
1b85f723
RBR
188}
189
190// Converts vetex dictionary to array
191
192func mapVertices(_ vertices: [String: CGPoint]) -> [Vertex] {
5e8ff485
RBR
193 var i = 0
194 return vertices.map { label, position in
195 i += 1
196 return Vertex(id: i, label: label, position: position)
197 }
1b85f723
RBR
198}
199
200extension Map {
5e8ff485
RBR
201 func parse() -> ParsedMap {
202
203 let text = self.content ?? ""
204 let vertices = parseVertices(text)
205 let mappedVertices = mapVertices(vertices)
206 let edges = parseEdges(text, vertices: vertices)
207 let blockers = parseBlockers(text, vertices: vertices)
208 let opportunities = parseOpportunities(text, vertices: vertices)
209 let stages = parseStages(text)
210
211 return ParsedMap(
212 vertices: mappedVertices, edges: edges, blockers: blockers, opportunities: opportunities,
213 stages: stages)
214 }
1b85f723 215}