]> git.r.bdr.sh - rbdr/map/blob - Map/Presentation/Base Components/MapRender/MapVertices.swift
5bc0a967aef078789bbf1bd1a56aa9f05d898e9a
[rbdr/map] / Map / Presentation / Base Components / MapRender / MapVertices.swift
1 import SwiftUI
2
3 struct MapVertices: View {
4
5 let mapSize: CGSize
6 let vertexSize: CGSize
7 let vertices: [Vertex]
8 let padding = CGFloat(5.0)
9
10 var onDragVertex: (Vertex, CGFloat, CGFloat) -> Void = { _, _, _ in }
11
12 var body: some View {
13 ZStack(alignment: .topLeading) {
14 ForEach(vertices, id: \.id) { vertex in
15 ZStack(alignment: .topLeading) {
16 getVertexShape(vertex).fill(Color.map.vertexColor)
17 Text(vertex.label.replacingOccurrences(of: "\\n", with: "\n")).font(.theme.vertexLabel)
18 .foregroundColor(.map.labelColor)
19 .shadow(color: .white, radius: 0, x: -0.5, y: -0.5)
20 .shadow(color: .white, radius: 0, x: 0.5, y: 0.5)
21 .offset(
22 CGSize(
23 width: w(vertex.position.x) + vertexSize.width + padding,
24 height: h(vertex.position.y) + 7.0))
25 }.gesture(
26 DragGesture()
27 .onChanged { value in
28 let deltaX = value.startLocation.x - value.location.x
29 let deltaY = value.startLocation.y - value.location.y
30 onDragVertex(vertex, deltaX, deltaY)
31 }
32 )
33 }
34 }
35 }
36
37 func h(_ dimension: CGFloat) -> CGFloat {
38 max(0.0, min(mapSize.height, dimension * mapSize.height / 100.0))
39 }
40
41 func w(_ dimension: CGFloat) -> CGFloat {
42 max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0))
43 }
44
45 func getVertexShape(_ vertex: Vertex) -> Path {
46 switch vertex.shape {
47 case .circle:
48 return Path { path in
49 path.addEllipse(
50 in: CGRect(
51 origin: CGPoint(x: w(vertex.position.x), y: h(vertex.position.y)), size: vertexSize
52 ))
53 }
54 case .square:
55 return Path { path in
56 path.addRect(
57 CGRect(
58 x: w(vertex.position.x), y: h(vertex.position.y), width: vertexSize.width,
59 height: vertexSize.height
60 ))
61 }
62 case .triangle:
63 return Path { path in
64 path.move(to: CGPoint(x: w(vertex.position.x), y: h(vertex.position.y) + vertexSize.height))
65 path.addLine(
66 to: CGPoint(
67 x: w(vertex.position.x) + vertexSize.width, y: h(vertex.position.y) + vertexSize.height)
68 )
69 path.addLine(
70 to: CGPoint(x: w(vertex.position.x) + vertexSize.width / 2.0, y: h(vertex.position.y)))
71 path.addLine(
72 to: CGPoint(x: w(vertex.position.x), y: h(vertex.position.y) + vertexSize.height))
73 path.closeSubpath()
74 }
75 case .x:
76 return Path { path in
77 path.move(to: CGPoint(x: w(vertex.position.x), y: h(vertex.position.y)))
78 path.addLine(
79 to: CGPoint(
80 x: w(vertex.position.x) + vertexSize.width, y: h(vertex.position.y) + vertexSize.height)
81 )
82 path.closeSubpath()
83 path.move(to: CGPoint(x: w(vertex.position.x) + vertexSize.width, y: h(vertex.position.y)))
84 path.addLine(
85 to: CGPoint(x: w(vertex.position.x), y: h(vertex.position.y) + vertexSize.height))
86 path.closeSubpath()
87 }.strokedPath(StrokeStyle(lineWidth: 2.0, lineCap: .butt))
88 }
89 }
90 }
91
92 #Preview {
93 MapVertices(
94 mapSize: CGSize(width: 400.0, height: 400.0), vertexSize: CGSize(width: 25.0, height: 25.0),
95 vertices: [
96 Vertex(id: 0, label: "A Circle", position: CGPoint(x: 50.0, y: 50.0)),
97 Vertex(id: 1, label: "A Square", position: CGPoint(x: 10.0, y: 20.0), shape: .square),
98 Vertex(id: 2, label: "A triangle", position: CGPoint(x: 25, y: 32.0), shape: .triangle),
99 Vertex(id: 3, label: "An X", position: CGPoint(x: 70.0, y: 70.0), shape: .x),
100 ])
101 }