Starsector API
Loading...
Searching...
No Matches
ConstellationGen.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.procgen;
2
3import java.util.ArrayList;
4import java.util.List;
5import java.util.Random;
6
7import org.lwjgl.util.vector.Vector2f;
8
9import com.fs.starfarer.api.campaign.StarSystemAPI;
10import com.fs.starfarer.api.util.Misc;
11
12public class ConstellationGen {
13
14 public static class SpringConnection {
15 public float k;
16 public float d;
17 public boolean push = true;
18 public boolean pull = true;
19 public SpringNode from;
20 public SpringNode to;
21 }
22
23 public static class SpringNode {
24 public Object custom;
25 public float radius;
26 public Vector2f loc = new Vector2f();
27 public Vector2f vel = new Vector2f();
28 public Vector2f force = new Vector2f();
29 public float mass;
30
31 public boolean moveable = true;
32 public List<SpringConnection> connections = new ArrayList<SpringConnection>();
33 }
34
35 public static class SpringSystem {
36 public List<SpringNode> nodes = new ArrayList<SpringNode>();
37 public List<SpringConnection> connections = new ArrayList<SpringConnection>();
38 public int iter = 0;
39
40
41 public SpringNode addNode(Object custom, float radius, float mass, float x, float y) {
42 SpringNode node = new SpringNode();
43 node.custom = custom;
44 node.radius = radius;
45 node.mass = mass;
46 node.loc.x = x;
47 node.loc.y = y;
48 nodes.add(node);
49
50 return node;
51 }
52
53 public boolean connExists(SpringNode from, SpringNode to) {
54 for (SpringConnection conn : connections) {
55 if (conn.from == from && conn.to == to) return true;
56 if (conn.from == to && conn.to == from) return true;
57 }
58 return false;
59 }
60
61
62 public void addConnection(SpringNode from, SpringNode to, float k, float d, boolean push, boolean pull) {
63 if (from == to) return;
64 if (connExists(from, to)) return;
65
66 SpringConnection conn = new SpringConnection();
67 conn.from = from;
68 conn.to = to;
69 conn.k = k;
70 conn.d = d;
71 conn.push = push;
72 conn.pull = pull;
73
74 from.connections.add(conn);
75 to.connections.add(conn);
76
77 connections.add(conn);
78 }
79
80 public void advance(float amount) {
81 if (amount <= 0) return;
82 iter++;
83
84 for (SpringNode node : nodes) {
85 node.force.set(0, 0);
86 }
87
88 for (SpringConnection conn : connections) {
89 float dist = Misc.getDistance(conn.from.loc, conn.to.loc);
90 if (!conn.push && dist < conn.d) continue;
91 if (!conn.pull && dist > conn.d) continue;
92
93 float diff = conn.d - dist;
94
95 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(conn.from.loc, conn.to.loc));
96
97 float force = conn.k * diff;
98
99 conn.to.force.x += dir.x * force;
100 conn.to.force.y += dir.y * force;
101 conn.from.force.x -= dir.x * force;
102 conn.from.force.y -= dir.y * force;
103 }
104
105 float cof = 500f;
106 for (SpringNode node : nodes) {
107 float friction = node.vel.length() * cof;
108 float max = node.vel.length() * node.mass / amount;
109 if (friction > max) friction = max;
110
111 if (friction > 0) {
112 Vector2f dir = new Vector2f(node.vel);
113 dir.negate();
114 dir.normalise();
115 dir.scale(friction);
116 node.force.x += dir.x;
117 node.force.y += dir.y;
118 }
119
120// if (node.force.length() < 10f && node.vel.length() > 2f) {
121// Vector2f dir = new Vector2f(node.vel);
122// dir.negate();
123// dir.normalise();
124//
125// dir.scale(node.vel.length() * node.mass / amount);
126// node.force.set(dir);
127// }
128 }
129
130 for (SpringNode node : nodes) {
131 if (!node.moveable) continue;
132
133 float ax = node.force.x / node.mass;
134 float ay = node.force.y / node.mass;
135
136 node.vel.x += ax * amount;
137 node.vel.y += ay * amount;
138
139 node.loc.x += node.vel.x * amount;
140 node.loc.y += node.vel.y * amount;
141 }
142 }
143
144 public boolean isDone() {
145 for (SpringNode n1 : nodes) {
146 for (SpringNode n2 : nodes) {
147 if (n1 == n2) continue;
148 float dist = Misc.getDistance(n1.loc, n2.loc);
149 if (dist < n1.radius + n2.radius) {
150 return false;
151 }
152 }
153 }
154 for (SpringConnection conn : connections) {
155 if (!conn.pull) continue;
156
157 float dist = Misc.getDistance(conn.from.loc, conn.to.loc);
158 if (dist > conn.d + 1000f) {
159 return false;
160 }
161 }
162 return true;
163 }
164 }
165
166
167
168 public static class StarNode {
169 public StarSystemAPI system;
170 public Vector2f location;
171 public StarNode(StarSystemAPI system, Vector2f location) {
172 this.system = system;
173 this.location = location;
174 }
175 }
176
177// public static void addSpringScript(final List<StarSystemAPI> systems) {
178//
179// final SpringSystem springs = createSpringSystem(systems, new Random());
180//
181// Global.getSector().addScript(new EveryFrameScript() {
182// public boolean runWhilePaused() {
183// return false;
184// }
185// public boolean isDone() {
186// boolean done = springs.isDone();
187// if (done) {
188// for (int i = 0; i < 100; i++) {
189// System.out.println("Done in " + springs.iter + " iterations");
190// }
191// for (SpringNode node : springs.nodes) {
192// StarSystemAPI system = (StarSystemAPI) node.custom;
193// system.getLocation().set(node.loc);
194// }
195// Global.getSector().setPaused(true);
196// Global.getSector().getHyperspace().updateAllOrbits();
197// return true;
198// }
199// return false;
200// }
201// public void advance(float amount) {
202// if (isDone()) return;
203//
204// for (int i = 0; i < 1; i++) {
205// springs.advance(0.25f);
206// }
207//
208// for (SpringNode node : springs.nodes) {
209// StarSystemAPI system = (StarSystemAPI) node.custom;
210// system.getLocation().set(node.loc);
211// }
212// }
213// });
214//
215// }
216
217 public static SpringSystem createSpringSystem(List<StarSystemAPI> systems, Random random) {
218 final SpringSystem springs = new SpringSystem();
219 if (systems.isEmpty()) return springs;
220
221 Vector2f loc = new Vector2f(0, 0);
222 //loc = new Vector2f(systems.get(0).getLocation());
223 boolean first = true;
224 for (StarSystemAPI system : systems) {
225 float radius = system.getMaxRadiusInHyperspace();
226 if (radius < 500) radius = 500;
227
228 float mass = radius;
229 SpringNode node = springs.addNode(system, radius, mass,
230 loc.x + random.nextFloat(), loc.y + random.nextFloat());
231 if (first) {
232 node.moveable = false;
233 first = false;
234 }
235 }
236
237 List<SpringNode> copy = new ArrayList<SpringNode>(springs.nodes);
238 SpringNode curr = copy.get((int) (copy.size() * random.nextDouble()));
239 copy.remove(curr);
240
241 float pullK = 1000f;
242 float pushK = 500f;
243 // create constellation shape via branching spring thing
244 while (!copy.isEmpty()) {
245 int numBranches = random.nextInt(3) + 1;
246 //numBranches = 3;
247
248 for (int i = 0; i < numBranches; i++) {
249 if (copy.isEmpty()) break;
250
251 SpringNode other = copy.get((int) (copy.size() * random.nextDouble()));
252 copy.remove(other);
253
254 //float d = (curr.radius + other.radius) + 250f + random.nextFloat() * 1000f;
255 float d = (curr.radius + other.radius) + StarSystemGenerator.MIN_STAR_DIST +
256 random.nextFloat() * (StarSystemGenerator.MAX_STAR_DIST - StarSystemGenerator.MIN_STAR_DIST);
257 springs.addConnection(curr, other, pullK, d, true, true);
258 //System.out.println("Dist: " + d);
259 if (i == numBranches - 1) {
260 curr = other;
261 }
262 }
263 }
264
265 // and add push-only connections for anything not connected by the main shape
266 for (SpringNode n1 : springs.nodes) {
267 for (SpringNode n2 : springs.nodes) {
268 if (n1 == n2) continue;
269 float d = (n1.radius + n2.radius) + 5000f;
270 springs.addConnection(n1, n2, pushK, d, true, false);
271 }
272 }
273
274 for (SpringNode node : springs.nodes) {
275 StarSystemAPI system = (StarSystemAPI) node.custom;
276 system.getLocation().set(node.loc);
277 }
278 return springs;
279 }
280
281
282 public static SpringSystem doConstellationLayout(List<StarSystemAPI> systems, Random random, Vector2f centerPoint) {
283 SpringSystem springs = createSpringSystem(systems, random);
284
285 for (int i = 0; i < 1000; i++) {
286 springs.advance(0.25f);
287 if (springs.isDone()) break;
288 }
289
290 if (!springs.isDone()) {
291 springs = createSpringSystem(systems, random);
292 for (int i = 0; i < 1000; i++) {
293 springs.advance(0.25f);
294 if (springs.isDone()) break;
295 }
296 }
297
298 //if (!springs.isDone()) return springs;
299
300 float minX = Float.MAX_VALUE;
301 float maxX = -Float.MAX_VALUE;
302 float minY = Float.MAX_VALUE;
303 float maxY = -Float.MAX_VALUE;
304
305 for (SpringNode node : springs.nodes) {
306 float x = node.loc.x;
307 float y = node.loc.y;
308 if (x < minX) minX = x;
309 if (x > maxX) maxX = x;
310 if (y < minY) minY = y;
311 if (y > maxY) maxY = y;
312 }
313
314 float midX = minX + (maxX - minX) * 0.5f;
315 float midY = minY + (maxY - minY) * 0.5f;
316
317 for (SpringNode node : springs.nodes) {
318 node.loc.x = (int)(node.loc.x - midX + centerPoint.x);
319 node.loc.y = (int)(node.loc.y - midY + centerPoint.y);
320 }
321
322
323 for (SpringNode node : springs.nodes) {
324 StarSystemAPI system = (StarSystemAPI) node.custom;
325 system.getLocation().set(node.loc);
326 }
327
328 return springs;
329 }
330
331
332
333
334}
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
static SpringSystem doConstellationLayout(List< StarSystemAPI > systems, Random random, Vector2f centerPoint)
static SpringSystem createSpringSystem(List< StarSystemAPI > systems, Random random)