Преглед на файлове

Build graph of satellites

Toni Fadjukoff преди 8 години
родител
ревизия
3482b3cc17
променени са 1 файла, в които са добавени 142 реда и са изтрити 3 реда
  1. 142 3
      src/main/kotlin/name/lamperi/orbital/main.kt

+ 142 - 3
src/main/kotlin/name/lamperi/orbital/main.kt Целия файл

@@ -7,20 +7,89 @@ import java.io.File;
7 7
 
8 8
 val EARTH_RADIUS = 6371.0; // KM
9 9
 
10
+
10 11
 data class Problem (
11 12
         val seed : Double, val satellites : Set<Satellite>, val route : Route)
12 13
 
13 14
 data class Satellite (
14 15
         val id : String, val lat : Double, val lon : Double, val height : Double)
15 16
 
16
-
17 17
 data class Route (
18 18
         val startLat : Double, val startLon : Double, val endLat : Double, val endLon : Double)
19 19
 
20
+data class Point3D (
21
+        val x : Double, val y : Double, val z : Double)
22
+
23
+data class Vec3 (
24
+        val x : Double, val y : Double, val z : Double
25
+) {
26
+    fun len() : Double {
27
+        return Math.sqrt(x * x + y * y + z * z)
28
+    }
29
+
30
+    fun unit() : Vec3 {
31
+        val l = len()
32
+        return Vec3(x/l, y/l, z/l)
33
+    }
34
+
35
+    fun dot(o: Vec3) : Double {
36
+        return x * o.x + y * o.y + z * o.z
37
+    }
38
+}
39
+
40
+fun toVec(point : Point3D) : Vec3 {
41
+    return Vec3(point.x, point.y, point.z)
42
+}
43
+
44
+fun createVec(start : Point3D, end : Point3D) : Vec3 {
45
+    return Vec3(end.x - start.x, end.y - start.y, end.z - start.z)
46
+}
47
+
48
+data class SphericalCoordinate (
49
+        val radius : Double, val theta : Double, val phi : Double)
50
+
51
+data class NamedPoint3D (
52
+        val name : String, val point : Point3D)
53
+
54
+data class Graph (
55
+        val nodes : Map<String,Node>)
56
+
57
+data class Node (
58
+        val info : NamedPoint3D, val neighbors : Set<String>)
59
+
60
+// Coordinate system
61
+// x = + when lon = 0°, - when lon = 180°
62
+// y = + when lon = 90°, - when lon = -90°
63
+// z = + when lat = 90°, - when lat = -90°
64
+
65
+fun rad(degrees : Double) = degrees * Math.PI / 180.0
66
+
67
+// let theta = rad(lon) in (-pi, pi)
68
+// let phi = rad(-lat + 90°) in (0, pi)
69
+fun toSpherical(lat : Double, lon : Double, radius : Double = EARTH_RADIUS)
70
+        =  SphericalCoordinate(radius = radius, theta = rad(lon), phi = rad(-lat + 90.0))
71
+
72
+// Cartesian coordinates can be calculated using
73
+// x = r * sin(theta) * cos(phi)
74
+// y = r * sin(theta) * sin(phi)
75
+// z = r * cos(theta)
76
+fun toCartesian(c : SphericalCoordinate)
77
+        = Point3D(
78
+            x = c.radius * Math.sin(c.theta) * Math.cos(c.phi),
79
+            y = c.radius * Math.sin(c.theta) * Math.sin(c.phi),
80
+            z = c.radius * Math.cos(c.theta))
81
+
82
+fun toCartesian(lat : Double, lon : Double)
83
+        = toCartesian(toSpherical(lat, lon))
84
+
85
+fun toCartesian(lat : Double, lon : Double, height : Double) : Point3D
86
+        = toCartesian(toSpherical(lat, lon, EARTH_RADIUS + height))
87
+
88
+fun named(name : String, point : Point3D) = NamedPoint3D(name = name, point = point)
20 89
 
21 90
 fun parseFile(file : java.io.File) : Problem {
22 91
     val contents : String = file.readText()
23
-    var seed : Double = contents.splitToSequence('\n').first().splitToSequence(':').last().toDouble()
92
+    val seed : Double = contents.splitToSequence('\n').first().splitToSequence(':').last().toDouble()
24 93
     val satellites : Set<Satellite> =  contents.splitToSequence('\n')
25 94
             .filter { it.startsWith("SAT") }
26 95
             .map {
@@ -28,7 +97,7 @@ fun parseFile(file : java.io.File) : Problem {
28 97
                 Satellite(id = parts[0], lat = parts[1].toDouble(),
29 98
                     lon = parts[2].toDouble(), height = parts[3].toDouble())
30 99
             }.toSet()
31
-    var route : Route = contents.splitToSequence('\n')
100
+    val route : Route = contents.splitToSequence('\n')
32 101
             .filter { it.startsWith("ROUTE") }
33 102
             .map {
34 103
                 val parts = it.split(',')
@@ -38,8 +107,78 @@ fun parseFile(file : java.io.File) : Problem {
38 107
     return Problem(seed = seed, satellites = satellites, route = route)
39 108
 }
40 109
 
110
+// https://en.wikipedia.org/wiki/Line–sphere_intersection
111
+// Solutions are of quadratic form
112
+// l = unit vector from point 0 to point 1
113
+// o = point 0
114
+// c = [0,0,0]
115
+// r = EARTH_RADIUS
116
+// Calculate (l · o)^2 - (o · o)  + r^2
117
+// If it is non-zero, then the route cannot be made
118
+fun canRoute(p0 : Point3D, p1 : Point3D) : Boolean {
119
+    val l = createVec(p0, p1).unit()
120
+    val o = toVec(p0)
121
+
122
+    val l_dot_o = l.dot(o)
123
+    val o_dot_o = o.dot(o)
124
+    val result = l_dot_o * l_dot_o - o_dot_o + EARTH_RADIUS * EARTH_RADIUS
125
+    if (result <= 0) {
126
+        println("$p0 $p1 => $result")
127
+    }
128
+    return result > 0
129
+}
130
+
131
+fun <E> concat(set : Collection<E>, vararg more : E) : Set<E> {
132
+    val mutable = set.toMutableSet()
133
+    for (e in more) mutable.add(e)
134
+    return mutable.toSet()
135
+}
136
+
137
+fun buildGraph(problem : Problem) : Graph {
138
+    val start = named("Start", toCartesian(problem.route.startLat, problem.route.startLon))
139
+    val end = named("End", toCartesian(problem.route.endLat, problem.route.endLon))
140
+
141
+    val satellites = problem.satellites.map { sat ->
142
+        named(sat.id, toCartesian(sat.lat, sat.lon, sat.height))
143
+    }.toList()
144
+
145
+    val startNode = Node(info = start, neighbors = satellites.filter {
146
+        canRoute(start.point, it.point)
147
+    }.map { it.name }.toSet())
148
+
149
+    val allPoints = concat(satellites, start, end)
150
+
151
+    val otherNodes = satellites.map { sat ->
152
+        Node(info = sat, neighbors = allPoints.filter { other ->
153
+            !sat.name.equals(other.name) && canRoute(sat.point, other.point)
154
+        }.map { it.name }.toSet())
155
+    }
156
+
157
+    val endNode = Node(info = end, neighbors = satellites.filter {
158
+        canRoute(it.point, end.point)
159
+    }.map { it.name }.toSet())
160
+
161
+    val nodeMap = concat(otherNodes, startNode, endNode).associateBy({ it.info.name }, { it })
162
+    return Graph(nodeMap)
163
+}
164
+
165
+
41 166
 fun main(args : Array<String>) {
42 167
     val file = File("src/main/resources/satellites.txt")
43 168
     val problem = parseFile(file)
44 169
     println(problem)
170
+
171
+    val graph = buildGraph(problem)
172
+    println(graph)
173
+
174
+    val startNode = graph.nodes["Start"]
175
+    if (startNode != null) {
176
+        startNode.neighbors.forEach { n ->
177
+            val neighbor = graph.nodes[n]
178
+            if (neighbor != null) {
179
+                println("${startNode.info.point} vs ${neighbor.info.point}")
180
+            }
181
+        }
182
+    }
183
+
45 184
 }