001package com.fs.starfarer.api.impl.campaign.procgen;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.Random;
006
007import org.lwjgl.util.vector.Vector2f;
008
009import com.fs.starfarer.api.campaign.StarSystemAPI;
010import com.fs.starfarer.api.util.Misc;
011
012public class ConstellationGen {
013        
014        public static class SpringConnection {
015                public float k;
016                public float d;
017                public boolean push = true;
018                public boolean pull = true;
019                public SpringNode from;
020                public SpringNode to;
021        }
022        
023        public static class SpringNode {
024                public Object custom;
025                public float radius;
026                public Vector2f loc = new Vector2f();
027                public Vector2f vel = new Vector2f();
028                public Vector2f force = new Vector2f();
029                public float mass;
030                
031                public boolean moveable = true;
032                public List<SpringConnection> connections = new ArrayList<SpringConnection>(); 
033        }
034        
035        public static class SpringSystem {
036                public List<SpringNode> nodes = new ArrayList<SpringNode>();
037                public List<SpringConnection> connections = new ArrayList<SpringConnection>();
038                public int iter = 0;
039                
040                
041                public SpringNode addNode(Object custom, float radius, float mass, float x, float y) {
042                        SpringNode node = new SpringNode();
043                        node.custom = custom;
044                        node.radius = radius;
045                        node.mass = mass;
046                        node.loc.x = x;
047                        node.loc.y = y;
048                        nodes.add(node);
049                        
050                        return node;
051                }
052
053                public boolean connExists(SpringNode from, SpringNode to) {
054                        for (SpringConnection conn : connections) {
055                                if (conn.from == from && conn.to == to) return true;
056                                if (conn.from == to && conn.to == from) return true;
057                        }
058                        return false;
059                }
060                
061                
062                public void addConnection(SpringNode from, SpringNode to, float k, float d, boolean push, boolean pull) {
063                        if (from == to) return;
064                        if (connExists(from, to)) return;
065                        
066                        SpringConnection conn = new SpringConnection();
067                        conn.from = from;
068                        conn.to = to;
069                        conn.k = k;
070                        conn.d = d;
071                        conn.push = push;
072                        conn.pull = pull;
073                        
074                        from.connections.add(conn);
075                        to.connections.add(conn);
076                        
077                        connections.add(conn);
078                }
079                
080                public void advance(float amount) {
081                        if (amount <= 0) return;
082                        iter++;
083                        
084                        for (SpringNode node : nodes) {
085                                node.force.set(0, 0);
086                        }
087                        
088                        for (SpringConnection conn : connections) {
089                                float dist = Misc.getDistance(conn.from.loc, conn.to.loc);
090                                if (!conn.push && dist < conn.d) continue;
091                                if (!conn.pull && dist > conn.d) continue;
092                                
093                                float diff = conn.d - dist;
094                                
095                                Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(conn.from.loc, conn.to.loc));
096                                
097                                float force = conn.k * diff;
098                                
099                                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