001package com.fs.starfarer.api.impl.campaign.velfield;
002
003import java.awt.Color;
004import java.util.ArrayList;
005import java.util.List;
006import java.util.Random;
007
008import org.lwjgl.opengl.GL11;
009import org.lwjgl.util.vector.Vector2f;
010
011import com.fs.starfarer.api.Global;
012import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamParams2;
013import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamSegment;
014import com.fs.starfarer.api.util.Misc;
015import com.fs.starfarer.api.util.WeightedRandomPicker;
016
017public class SlipstreamBuilder {
018        
019        public static enum StreamType {
020                NORMAL,
021                WIDE,
022                NARROW,
023        }
024        
025        public static float MIN_SPACING = 200f;
026        public static float MAX_SPACING = 400f;
027        public static float WIDTH_TO_SPACING_MULT = 0.2f;
028        
029        public static float MIN_NARROW = 0.5f;
030        public static float MAX_NARROW = 0.7f;
031        public static float MIN_NORMAL = 0.9f;
032        public static float MAX_NORMAL = 1.1f;
033        public static float MIN_WIDE = 1.3f;
034        public static float MAX_WIDE = 1.6f;
035        public static float MIN_VERY_WIDE = 1.8f;
036        public static float MAX_VERY_WIDE = 2.1f;
037        
038        public static float WCHANGE_SLOW_T_MULT_MIN = 1f;
039        public static float WCHANGE_SLOW_T_MULT_MIN_MAX = 1.2f;
040        public static float WCHANGE_MEDIUM_T_MULT_MIN = 1.8f;
041        public static float WCHANGE_MEDIUM_T_MULT_MAX = 2.2f;
042        public static float WCHANGE_FAST_T_MULT_MIN = 2.8f;
043        public static float WCHANGE_FAST_T_MULT_MAX = 3.2f;
044        
045        public static float WRAND_NONE_MULT = 0f;
046        public static float WRAND_LOW_MULT = 0.1f;
047        public static float WRAND_MEDIUM_MULT = 0.17f;
048        public static float WRAND_HIGH_MULT = 0.25f;
049        
050        public static float FLUCT_LENGTH_MIN = 2500f;
051        public static float FLUCT_LENGTH_MAX = 4000f;
052        public static float FLUCT_MAG_MIN = 350f;
053        public static float FLUCT_MAG_MAX = 500f;
054        
055        public static float FLUCT_NONE_MULT = 0f;
056        public static float FLUCT_LOW_MULT = 0.33f;
057        public static float FLUCT_MEDIUM_MULT = 0.67f;
058        public static float FLUCT_HIGH_MULT = 1f;
059        
060        
061        // width
062        public static final int WIDTH_NARROW =           0;
063        public static final int WIDTH_NORMAL =           1;
064        public static final int WIDTH_WIDE =             2;
065        public static final int WIDTH_VERY_WIDE =        3;
066        
067        // width randomness
068        public static final int WRAND_NONE =             0;
069        public static final int WRAND_LOW =              1;
070        public static final int WRAND_MEDIUM =           2;
071        public static final int WRAND_HIGH =             3;
072
073        // how quickly width changes
074        public static final int WCHANGE_SLOW =           0;
075        public static final int WCHANGE_MEDIUM =         1;
076        public static final int WCHANGE_FAST =           2;
077        
078        // path fluctuations within sections
079        public static final int FLUCT_NONE =             0;
080        public static final int FLUCT_LOW =              1;
081        public static final int FLUCT_MEDIUM =           2;
082        public static final int FLUCT_HIGH =             3;
083        
084        
085        public void initTransitionMatrices() {
086                widthTM = new float[4][4];
087                if (type == StreamType.NORMAL) {
088                        widthTM[WIDTH_NARROW]    = new float [] {0.5f,   1f, 0.4f, 0.3f};
089                        widthTM[WIDTH_NORMAL]    = new float [] {  1f,   1f, 0.5f, 0.3f};
090                        widthTM[WIDTH_WIDE]      = new float [] {0.7f,   1f, 0.2f, 0.5f};
091                        widthTM[WIDTH_VERY_WIDE] = new float [] {  1f,   1f,   1f, 0.1f};
092                } else if (type == StreamType.WIDE) {
093                        widthTM[WIDTH_NARROW]    = new float [] {0.1f,   1f, 0.6f, 0.4f};
094                        widthTM[WIDTH_NORMAL]    = new float [] {0.2f, 0.5f,   1f, 0.5f};
095                        widthTM[WIDTH_WIDE]      = new float [] {0.1f, 0.5f,   1f, 0.5f};
096                        widthTM[WIDTH_VERY_WIDE] = new float [] {  0f, 0.5f,   1f, 0.3f};
097                } else if (type == StreamType.NARROW) {
098                        widthTM[WIDTH_NARROW]    = new float [] {0.8f,   1f, 0.3f, 0.1f};
099                        widthTM[WIDTH_NORMAL]    = new float [] {  1f,   1f, 0.3f, 0.1f};
100                        widthTM[WIDTH_WIDE]      = new float [] {0.7f,   1f, 0.2f, 0.2f};
101                        widthTM[WIDTH_VERY_WIDE] = new float [] {  1f,   1f,   1f, 0.1f};
102                }
103                
104                wrandTM = new float[4][4];
105                wrandTM[WRAND_NONE]              = new float [] {  1f,   1f, 0.1f, 0.1f};
106                wrandTM[WRAND_LOW]               = new float [] {  1f,   1f,   1f, 0.1f};
107                wrandTM[WRAND_MEDIUM]    = new float [] {0.1f,   1f, 0.5f, 0.2f};
108                wrandTM[WRAND_HIGH]              = new float [] {0.1f, 0.1f,   1f, 0.1f};
109                
110                wchangeTM = new float[3][3];
111                wchangeTM[WCHANGE_SLOW]          = new float [] {  1f,   1f, 0.1f};
112                wchangeTM[WCHANGE_MEDIUM]        = new float [] {  1f,   1f,   1f};
113                wchangeTM[WCHANGE_FAST]          = new float [] {0.1f,   1f,   1f};
114                
115                fluctTM = new float[4][4];
116                fluctTM[FLUCT_NONE]              = new float [] {  1f,   1f, 0.1f, 0.1f};
117                fluctTM[FLUCT_LOW]               = new float [] {  1f,   1f,   1f, 0.1f};
118                fluctTM[FLUCT_MEDIUM]    = new float [] {0.1f,   1f, 0.5f, 0.2f};
119                fluctTM[FLUCT_HIGH]              = new float [] {0.1f, 0.1f,   1f, 0.1f};
120        }
121        
122        
123        public static class SlipstreamSection {
124                public Vector2f from = new Vector2f();
125                public Vector2f to = new Vector2f();
126                public Vector2f control = new Vector2f();
127                
128                public int width;
129                public int wrand;
130                public int wchange;
131                public int fluct;
132                
133                public boolean subdivision = false;
134                public float approxCurveLength;
135        }
136        
137        public static class AddedSegment {
138                public SlipstreamSegment segment;
139                public Vector2f dir;
140                public Vector2f perp;
141        }
142        
143        protected float [][] widthTM; 
144        protected float [][] wrandTM; 
145        protected float [][] wchangeTM; 
146        protected float [][] fluctTM;
147        
148        protected Random random;
149        protected SlipstreamParams2 params;
150        protected SlipstreamTerrainPlugin2 plugin;
151        protected List<SlipstreamSection> sections = new ArrayList<SlipstreamSection>();
152        
153        protected int currWidth = 0;
154        protected int currWRand = 0;
155        protected int currWChange = 0;
156        protected int currFluct = 0;
157        
158        protected Vector2f start;
159        protected StreamType type;
160        
161        public SlipstreamBuilder(Vector2f start, SlipstreamTerrainPlugin2 plugin, StreamType type, Random random) {
162                this.plugin = plugin;
163                this.type = type != null ? type : StreamType.NORMAL;
164                this.params = plugin.getParams();
165                
166                if (random == null) random = Misc.random;
167                this.random = random;
168                this.start = start;
169                initTransitionMatrices();
170                for (int i = 0; i < 10; i++) {
171                        pickAllNext();
172                }
173        }
174        
175        protected float maxAngleVarianceForCurve = 30f;
176        protected float maxAngleVariance = 60f;
177        public float getMaxAngleVariance() {
178                return maxAngleVariance;
179        }
180
181        public void setMaxAngleVariance(float maxAngleVariance) {
182                this.maxAngleVariance = maxAngleVariance;
183        }
184        public float getMaxAngleVarianceForCurve() {
185                return maxAngleVarianceForCurve;
186        }
187        public void setMaxAngleVarianceForCurve(float maxAngleVarianceForCurve) {
188                this.maxAngleVarianceForCurve = maxAngleVarianceForCurve;
189        }
190
191        public void buildToDestination(Vector2f control, Vector2f control2, Vector2f to) {
192                Vector2f p0 = new Vector2f(start);
193                Vector2f p1 = new Vector2f(control);
194                Vector2f p2 = new Vector2f(control2);
195                Vector2f p3 = new Vector2f(to);
196                
197                float len = getApproximateBezierLength(p0, p1, p2, p3);
198                
199                float t = 0f;
200                Vector2f prev = new Vector2f(p0);
201                while (true) {
202                        float length = 3000f + random.nextInt(2000);
203                        float tIncr = length / len;
204                        t += tIncr;
205                        if (t > 1f) t = 1f;
206                        
207                        Vector2f loc = Misc.bezierCubic(p0, p1, p2, p3, t);
208                        boolean wide = currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE;
209                        pickAllNext();
210                        wide |= currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE;
211                        float angle = random.nextFloat() * maxAngleVarianceForCurve - maxAngleVarianceForCurve/2f;
212                        if (wide) angle *= 0.5f;
213                        if (t >= 1f) angle = 0f;
214                        
215                        loc = Misc.rotateAroundOrigin(loc, angle, prev);
216                        float actualDist = Misc.getDistance(prev, loc);
217                        if (actualDist > length * 0.5f) { 
218                                prev.set(loc);
219                                addSection(new Vector2f(loc), true);
220                        }
221                        
222                        if (t >= 1f) break;
223                }
224                
225                generate();
226        }
227        
228        public void buildToDestination(Vector2f control, Vector2f to) {
229                Vector2f p0 = new Vector2f(start);
230                Vector2f p1 = new Vector2f(control);
231                Vector2f p2 = new Vector2f(to);
232                
233                float len = getApproximateBezierLength(p0, p1, p2);
234                
235                float t = 0f;
236                Vector2f prev = new Vector2f(p0);
237                while (true) {
238                        float length = 3000f + random.nextInt(2000);
239                        float tIncr = length / len;
240                        t += tIncr;
241                        if (t > 1f) t = 1f;
242                        
243                        Vector2f loc = Misc.bezier(p0, p1, p2, t);
244                        boolean wide = currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE;
245                        pickAllNext();
246                        wide |= currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE;
247                        
248                        float angle = random.nextFloat() * maxAngleVarianceForCurve - maxAngleVarianceForCurve/2f;
249                        if (wide) angle *= 0.5f;
250                        if (t >= 1f) angle = 0f;
251                        
252                        loc = Misc.rotateAroundOrigin(loc, angle, prev);
253                        float actualDist = Misc.getDistance(prev, loc);
254                        if (actualDist > length * 0.5f) { 
255                                prev.set(loc);
256                                addSection(new Vector2f(loc), true);
257                        }
258                        
259                        if (t >= 1f) break;
260                }
261                
262                generate();
263        }
264        
265        public void buildToDestination(Vector2f to) {
266                Vector2f loc = new Vector2f(start);
267                Vector2f p0 = new Vector2f(loc);
268                Vector2f p1 = new Vector2f(to);
269                
270                float len = Misc.getDistance(p0, p1);
271                Vector2f dir = Misc.getUnitVector(p0, p1);
272                float dirAngle = Misc.getAngleInDegrees(dir);
273                
274                float distSoFar = 0f;
275                float prevAngle = 0f;
276                for (int i = 0; distSoFar < len; i++) {
277                        float angle = random.nextFloat() * maxAngleVariance - maxAngleVariance/2f;
278
279                        boolean wide = currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE;
280                        pickAllNext();
281                        wide |= currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE;
282                        //if (wide) angle *= 0.5f;
283                        
284                        angle += dirAngle;
285                        if (i % 2 == 1) angle = prevAngle;
286                        prevAngle = angle;
287                        //if (i == 0) angle = 90f;
288                        Vector2f add = Misc.getUnitVectorAtDegreeAngle(angle);
289                        float length = 3000f + random.nextInt(2000);
290                        distSoFar += length;
291                        //length *= 0.25f;
292                        add.scale(length);
293                        Vector2f.add(loc, add, loc);
294                        addSection(new Vector2f(loc), true);
295                }
296                Vector2f end = sections.get(sections.size() - 1).to;
297                
298                
299                float actualAngle = Misc.getAngleInDegrees(p0, end);
300                float angleDiff = Misc.getAngleDiff(dirAngle, actualAngle);
301                float turnDir = Misc.getClosestTurnDirection(actualAngle, dirAngle);
302                
303                for (SlipstreamSection section : sections) {
304                        section.from = Misc.rotateAroundOrigin(section.from, angleDiff * turnDir, p0);
305                        section.to = Misc.rotateAroundOrigin(section.to, angleDiff * turnDir, p0);
306                }
307                
308                end = sections.get(sections.size() - 1).to;
309                float actualDist = Misc.getDistance(p0, end);
310                float distToAdd = len - actualDist;
311                
312                //System.out.println("Distance short: " + distToAdd);
313                if (distToAdd > 1000) {
314                        angleDiff = Misc.getAngleDiff(dirAngle, prevAngle);
315                        turnDir = Misc.getClosestTurnDirection(actualAngle, dirAngle);
316                        float turnAmount = Math.min(30f, angleDiff) * turnDir;
317                        //float turnAmount = angleDiff * turnDir;
318                        
319                        Vector2f add = Misc.getUnitVectorAtDegreeAngle(prevAngle + turnAmount);
320                        float length = distToAdd;
321                        add.scale(length);
322                        loc = Vector2f.add(end, add, loc);
323                        addSection(new Vector2f(loc));
324                }
325                
326                generate();
327        }
328        
329        
330        protected float [] buildNoise;
331        public void buildTest() {
332                
333                if (false) {
334                        Vector2f loc = new Vector2f(start);
335                        
336                        Vector2f p0 = loc;
337                        Vector2f p1 = new Vector2f(loc);
338                        float w = 15000;
339                        float h = 15000;
340                        h = 0f;
341                        w = 25000f;
342                        w = 20000f;
343                        p1.x += w;
344                        p1.y += h;
345                        float len = Misc.getDistance(p0, p1);
346                        Vector2f dir = Misc.getUnitVector(p0, p1);
347                        Vector2f perp = new Vector2f(-dir.y, dir.x);
348                        
349                        float averageSection = 4000;
350                        
351                        int numNoisePoints = 32;
352                        while (numNoisePoints < len / averageSection) {
353                                numNoisePoints *= 2f;
354                        }
355                        if (numNoisePoints > 2048) numNoisePoints = 2048;
356                        float spikes = 0.75f;
357                        float [] noise = initNoise1D(random, numNoisePoints, spikes); 
358                        noise[0] = 0.5f;
359                        noise[noise.length - 1] = 0.5f;
360                        genNoise1D(random, noise, numNoisePoints, spikes);
361                        normalizeNoise1D(noise);
362                        buildNoise = noise;
363                        
364                        float startLength = 1500 + random.nextFloat() * 1500f;
365                        float endLength = 1500 + random.nextFloat() * 1500f;
366                        Vector2f curr = new Vector2f(dir);
367                        curr.scale(startLength);
368                        Vector2f.add(curr, p0, curr);
369                        addSection(curr);
370                        
371                        
372                        float mid = len - startLength - endLength;
373                        float remaining = mid;
374                        float distSoFar = startLength;
375                        
376                        //float maxMag = 500f + mid * 0.025f + random.nextFloat() * (2000f + mid * 0.05f);
377                        //float maxMag = 500f + mid * 0.025f + 1f * (2000f + mid * 0.05f);
378                        float maxMag = 500f + mid * 0.01f + 1f * (1000f + mid * 0.025f);
379                        
380                        while (remaining > 0) {
381                                float segLen = 3000f + random.nextFloat() * 2000f;
382                                segLen = Math.min(segLen, remaining);
383                                remaining -= segLen;
384                                if (remaining < segLen * 0.5f) {
385                                        segLen += remaining;
386                                        remaining = 0f;
387                                }
388                                distSoFar += segLen;
389                                
390                                float t = (distSoFar - startLength) / mid;
391                                float n = getInterpNoise(noise, t) - 0.5f;
392                                
393                                if (t < 0.25f) {
394                                        n *= t / 0.25f;
395                                } else if (t > 0.75f) {
396                                        n *= (1f - t) / 0.25f;
397                                }
398                                
399                                n *= 2f;
400                                
401                                curr = new Vector2f(dir);
402                                curr.scale(distSoFar);
403                                Vector2f.add(curr, p0, curr);
404                                curr.x += perp.x * maxMag * n;
405                                curr.y += perp.y * maxMag * n;
406                                addSection(curr);
407                        }
408                        
409                        addSection(p1);
410                        
411                        generate();
412                        return;
413                }
414                
415                if (true) {
416                        Vector2f loc = new Vector2f(start);
417                        Vector2f p0 = new Vector2f(loc);
418                        Vector2f p1 = new Vector2f(loc);
419                        float w = 15000;
420                        float h = 15000;
421                        h = 0f;
422                        w = 25000f;
423                        //w = 80000f;
424                        w = 50000f;
425//                      h = 50000f;
426//                      w = 164000f;
427//                      h = 104000f;
428                        p1.x += w;
429                        p1.y += h;
430                        float len = Misc.getDistance(p0, p1);
431                        Vector2f dir = Misc.getUnitVector(p0, p1);
432                        Vector2f perp = new Vector2f(-dir.y, dir.x);
433                        float dirAngle = Misc.getAngleInDegrees(dir);
434                        
435                        float distSoFar = 0f;
436                        float prevAngle = 0f;
437                        for (int i = 0; distSoFar < len; i++) {
438                                float angle = random.nextFloat() * 60f - 30f;
439                                //float angle = StarSystemGenerator.getNormalRandom(random, -30f, 30f);
440                                angle += dirAngle;
441                                if (i % 2 == 1) angle = prevAngle;
442                                prevAngle = angle;
443                                //if (i == 0) angle = 90f;
444                                Vector2f add = Misc.getUnitVectorAtDegreeAngle(angle);
445                                float length = 3000f + random.nextInt(2000);
446                                distSoFar += length;
447                                //length *= 0.25f;
448                                add.scale(length);
449                                Vector2f.add(loc, add, loc);
450                                addSection(new Vector2f(loc));
451                        }
452                        Vector2f end = sections.get(sections.size() - 1).to;
453                        
454                        
455                        float actualAngle = Misc.getAngleInDegrees(p0, end);
456                        float angleDiff = Misc.getAngleDiff(dirAngle, actualAngle);
457                        float turnDir = Misc.getClosestTurnDirection(actualAngle, dirAngle);
458                        
459                        for (SlipstreamSection section : sections) {
460                                section.from = Misc.rotateAroundOrigin(section.from, angleDiff * turnDir, p0);
461                                section.to = Misc.rotateAroundOrigin(section.to, angleDiff * turnDir, p0);
462                        }
463                        
464                        end = sections.get(sections.size() - 1).to;
465                        float actualDist = Misc.getDistance(p0, end);
466                        float distToAdd = len - actualDist;
467                        //System.out.println("Distance short: " + distToAdd);
468                        if (distToAdd > 1000) {
469                                angleDiff = Misc.getAngleDiff(dirAngle, prevAngle);
470                                turnDir = Misc.getClosestTurnDirection(actualAngle, dirAngle);
471                                float turnAmount = Math.min(30f, angleDiff) * turnDir;
472                                //float turnAmount = angleDiff * turnDir;
473                                
474                                Vector2f add = Misc.getUnitVectorAtDegreeAngle(prevAngle + turnAmount);
475                                float length = distToAdd;
476                                add.scale(length);
477                                loc = Vector2f.add(end, add, loc);
478                                addSection(new Vector2f(loc));
479                        }
480                        
481                        generate();
482                                        
483                        //System.out.println("Segments: " + plugin.getSegments().size());
484                        return;
485                }
486
487                if (true) {
488                        Vector2f loc = new Vector2f(start);
489                        float prevAngle = 0f;
490                        for (int i = 0; i < 20; i++) {
491                                float angle = random.nextFloat() * 60f - 30f;
492                                if (i % 2 == 1) angle = prevAngle;
493                                prevAngle = angle;
494                                //if (i == 0) angle = 90f;
495                                Vector2f add = Misc.getUnitVectorAtDegreeAngle(angle);
496                                float length = 3000f + random.nextInt(2000);
497                                //length *= 0.25f;
498                                add.scale(length);
499                                Vector2f.add(loc, add, loc);
500                                addSection(new Vector2f(loc));
501                        }
502                        generate();
503                        return;
504                }
505                
506                if (true) {
507                        Vector2f loc = new Vector2f(start);
508                        
509                        Vector2f p0 = loc;
510                        Vector2f p1 = new Vector2f(loc);
511                        float w = 15000;
512                        float h = 15000;
513                        h = 0f;
514                        w = 25000f;
515                        w = 80000f;
516                        p1.x += w;
517                        p1.y += h;
518                        float len = Misc.getDistance(p0, p1);
519                        Vector2f dir = Misc.getUnitVector(p0, p1);
520                        Vector2f perp = new Vector2f(-dir.y, dir.x);
521                        
522                        //System.out.println("LEN: " + len);
523                        
524                        if (len <= 7000f) {
525                                addSection(p1);
526                                generate();
527                                return;
528                        }
529                        if (len <= 15000f) {
530                                float midLength = len * (0.4f + random.nextFloat() * 0.2f);
531                                
532                                Vector2f curr = new Vector2f(dir);
533                                curr.scale(1000f + random.nextFloat() * 1000f);
534                                Vector2f.add(curr, p0, curr);
535                                addSection(new Vector2f(curr));
536                                
537                                curr = new Vector2f(dir);
538                                curr.scale(midLength);
539                                
540                                float mag = 0f + random.nextFloat() * 1000f + len * 0.05f;
541                                if (random.nextFloat() < 0.75f) {
542                                        if (random.nextBoolean()) {
543                                                perp.negate();
544                                        }
545                                        curr.x += perp.x * mag;
546                                        curr.y += perp.y * mag;
547                                }
548                                
549                                Vector2f.add(curr, p0, curr);
550                                addSection(curr);
551                                
552                                float endLength = 1000f + random.nextFloat() * 1000f;
553                                curr = new Vector2f(dir);
554                                curr.scale(len - endLength);
555                                Vector2f.add(curr, p0, curr);
556                                addSection(curr);
557                                
558                                addSection(p1);
559                                
560                                generate();
561                                return;
562                        }
563                        
564                        
565                        
566                        float startLength = 1500 + random.nextFloat() * 1500f;
567                        float endLength = 1500 + random.nextFloat() * 1500f;
568                        Vector2f curr = new Vector2f(dir);
569                        curr.scale(startLength);
570                        Vector2f.add(curr, p0, curr);
571                        addSection(curr);
572                        
573                        float remaining = len - startLength - endLength;
574                        float distSoFar = startLength;
575                        while (remaining > 0) {
576                                float lengthForArc = 10000f + random.nextFloat() * 20000f;
577                                lengthForArc = Math.min(lengthForArc, remaining);
578                                remaining -= lengthForArc;
579                                if (remaining < 10000f) {
580                                        lengthForArc += remaining;
581                                        remaining = 0f;
582                                }
583                                
584                                if (random.nextBoolean()) {
585                                        perp.negate();
586                                }
587                                
588                                float maxMag = 500f + random.nextFloat() * (2000f + lengthForArc * 0.1f);
589                                maxMag = 4000; 
590                                float t = 0f;
591                                while (t < 1f) {
592                                        float perIter = 3000f + random.nextFloat() * 2000f;
593                                        float tForIter = perIter / lengthForArc;
594                                        t += tForIter;
595                                        if (t > 1f - tForIter * 0.5f) {
596                                                tForIter += 1f - t;
597                                                t = 1f;
598                                        }
599                                        perIter = tForIter * lengthForArc;
600                                        distSoFar += perIter;
601                                        
602                                        float mag = getFluctuationFunc(t) * maxMag;
603                                        curr = new Vector2f(dir);
604                                        curr.scale(distSoFar);
605                                        Vector2f.add(curr, p0, curr);
606                                        curr.x += perp.x * mag;
607                                        curr.y += perp.y * mag;
608                                        addSection(new Vector2f(curr));
609                                }
610                        }
611                        
612                        addSection(p1);
613                        generate();
614                        return;
615                }
616                
617                
618                if (false) {
619                        Vector2f loc = new Vector2f(start);
620                        
621                        Vector2f p0 = loc;
622                        Vector2f p3 = new Vector2f(loc);
623                        float w = 30000;
624                        float h = 30000;
625                        p3.x += w;
626                        p3.y += h;
627                        float len = Misc.getDistance(p0, p3);
628                        
629//                      Vector2f p1 = new Vector2f(p0);
630//                      Vector2f p2 = new Vector2f(p3);
631                        
632                        Vector2f p1 = Vector2f.add(p0, p3, new Vector2f());
633                        p1.scale(0.5f);
634                        Vector2f p2 = new Vector2f(p1);
635                        
636                        p1.x -= len * 0.2f;
637                        p1.y += len * 0.2f;
638                        p2.x += len * 0.2f;
639                        p2.y -= len * 0.2f;
640                        
641                        float dist1 = len * 0.5f + random.nextFloat() * len * 0.5f;
642                        float dist2 = len * 0.5f + random.nextFloat() * len * 0.5f;
643                        
644                        p1 = Misc.getPointAtRadius(p0, dist1, random);
645                        p2 = Misc.getPointAtRadius(p3, dist2, random);
646                        
647                        p1 = new Vector2f(p0.x, p3.y);
648                        p2 = new Vector2f(p3.x, p0.y);
649                        
650                        if (random.nextBoolean()) {
651                                Vector2f temp = p1;
652                                p1 = p2;
653                                p2 = temp;
654                        }
655                        
656                        float veryApproxPathLength = len * 1.5f;
657                        float tPerIter = 8000f / (veryApproxPathLength + 1f);
658                        
659                        float t = 0f;
660                        while (t < 1f) {
661                                t += tPerIter * (0.8f + 0.4f * random.nextFloat());
662                                if (t > 1f - tPerIter * 0.5f) t = 1f;
663                                
664                                Vector2f curr = Misc.bezierCubic(p0, p1, p2, p3, t); 
665                                addSection(new Vector2f(curr));
666                        }
667                        generate();
668                        return;
669                }
670                
671                if (false) {
672                        Vector2f loc = new Vector2f(start);
673                        
674                        Vector2f p0 = loc;
675                        Vector2f p2 = new Vector2f(loc);
676                        float w = 30000;
677                        float h = 30000;
678                        p2.x += w;
679                        p2.y += h;
680                        float len = Misc.getDistance(p0, p2);
681                        
682                        Vector2f p1 = Vector2f.add(p0, p2, new Vector2f());
683                        p1.scale(0.5f);
684                        p1.x -= len * 0.25f;
685                        p1.y += len * 0.25f;
686                        
687                        float approxPathLength = getApproximateBezierLength(p0, p1, p2);
688                        float tPerIter = 8000f / (approxPathLength + 1f);
689                        
690                        float t = 0f;
691                        while (t < 1f) {
692                                t += tPerIter * (0.8f + 0.4f * random.nextFloat());
693                                if (t > 1f - tPerIter * 0.5f) t = 1f;
694                                
695                                Vector2f curr = Misc.bezier(p0, p1, p2, t); 
696                                addSection(new Vector2f(curr));
697                        }
698                        generate();
699                        return;
700                }
701                
702                
703                if (false) {
704                        Vector2f loc = new Vector2f(start);
705                        loc.x += 5000;
706                        addSection(loc, 1, 0, 0, 3);
707                        loc.x += 5000;
708                        addSection(loc, 1, 0, 0, 3);
709                        loc.x += 5000;
710                        addSection(loc, 1, 0, 0, 3);
711                        loc.x += 5000;
712                        addSection(loc, 1, 0, 0, 3);
713                        
714                        loc.x += 5000;
715                        loc.y += 5000;
716                        addSection(loc, 0, 0, 0, 3);
717                        loc.y += 2000;
718                        addSection(loc, 0, 0, 0, 3);
719                        loc.x -= 5000;
720                        loc.y += 5000;
721                        addSection(loc, 0, 0, 0, 3);
722                        loc.x -= 5000;
723                        addSection(loc, 0, 0, 0, 3);
724                        generate();
725                        return;
726                }
727                
728                Vector2f loc = new Vector2f(start);
729                float prevAngle = 0f;
730                for (int i = 0; i < 20; i++) {
731                        float angle = random.nextFloat() * 60f - 30f;
732                        if (i % 2 == 1) angle = prevAngle;
733                        prevAngle = angle;
734                        //if (i == 0) angle = 90f;
735                        Vector2f add = Misc.getUnitVectorAtDegreeAngle(angle);
736                        float length = 3000f + random.nextInt(2000);
737                        //length *= 0.25f;
738                        add.scale(length);
739                        Vector2f.add(loc, add, loc);
740                        addSection(new Vector2f(loc));
741                }
742                
743                generate();
744        }
745        
746        public void generate() {
747                
748//              MIN_SPACING = 200f;
749//              MAX_SPACING = 400f;
750//              WIDTH_TO_SPACING_MULT = 0.2f;
751//              MIN_SPACING = 300f;
752//              MAX_SPACING = 600f;
753//              WIDTH_TO_SPACING_MULT = 0.4f;
754//              long seed = 23895464576452L + 4384357483229348234L;
755//              seed = 1181783497276652981L ^ seed;
756//              this.random = new Random(seed);
757                
758                computeControlPoints();
759                buildStream();
760        }
761        
762        
763        public void buildStream() {
764                plugin.setBuilder(this);
765                if (sections.isEmpty()) return;
766                
767                
768                float totalCurveLength = 0f;
769                for (SlipstreamSection curr : sections) {
770                        totalCurveLength += curr.approxCurveLength;
771                }
772                
773                int numNoisePoints = 32;
774                while (numNoisePoints * (MIN_SPACING + MAX_SPACING) / 2f < totalCurveLength) {
775                        numNoisePoints *= 2f;
776                }
777                if (numNoisePoints > 2048) numNoisePoints = 2048;
778                
779                float spikes = 0.67f;
780                float [] noiseForWidth = initNoise1D(random, numNoisePoints, spikes); 
781                noiseForWidth[0] = 0.5f;
782                noiseForWidth[noiseForWidth.length - 1] = 0.5f;
783                genNoise1D(random, noiseForWidth, numNoisePoints, spikes);
784                normalizeNoise1D(noiseForWidth);
785                
786                float width = getGoalWidth(sections.get(0));
787                //width *= 3f;
788                //width *= .5f;
789                
790                
791                float curveLengthSoFar = 0f;
792                for (SlipstreamSection curr : sections) {
793                        //curr.fluct = FLUCT_NONE;
794                        //curr.fluct = FLUCT_LOW;
795                        //curr.fluct = FLUCT_MEDIUM;
796                        //curr.fluct = FLUCT_HIGH;
797                        
798                        float startingWidth = width;
799                        float goalWidth = getGoalWidth(curr);
800                        float changeRate = getWChangeMult(curr);
801                        float wrandMult = getWRandMult(curr);
802                        float fluctMult = getFluctMult(curr);
803                        
804                        
805                        float desiredSpacing = Math.max(MIN_SPACING, width * WIDTH_TO_SPACING_MULT);
806                        if (desiredSpacing > MAX_SPACING) desiredSpacing = MAX_SPACING;
807                        
808                        int segments = (int) (curr.approxCurveLength / desiredSpacing);
809                        if (segments < 2) segments = 2;
810                        float spacing = curr.approxCurveLength / segments;
811                        
812                        List<AddedSegment> added = new ArrayList<AddedSegment>(); 
813                        
814                        int startIndex = 1;
815                        if (getPrev(curr) == null) startIndex = 0;
816                        for (int i = startIndex; i < segments; i++) {
817                                float f = (float)i / ((float)segments - 1f);
818                                float f2 = (float)(i + 0.1f) / ((float)segments - 1f);
819                                
820                                width = Misc.interpolate(startingWidth, goalWidth, Math.min(1f, f * f * changeRate));
821                                float t = (curveLengthSoFar + f * curr.approxCurveLength) / totalCurveLength;
822                                float wNoise = (getInterpNoise(noiseForWidth, t) - 0.5f) * 2f;
823                                wNoise *= wrandMult;
824                                //wNoise *= 3f;
825                                width *= (1f + wNoise);
826                                
827                                Vector2f loc = Misc.bezier(curr.from, curr.control, curr.to, f);
828                                Vector2f loc2 = Misc.bezier(curr.from, curr.control, curr.to, f2);
829                                Vector2f dir = Vector2f.sub(loc2, loc, new Vector2f());
830                                Misc.normalise(dir);
831                                Vector2f perp = new Vector2f(-dir.y, dir.x);
832                                
833                                plugin.addSegment(loc, width);
834                                AddedSegment seg = new AddedSegment();
835                                seg.segment = plugin.getSegments().get(plugin.getSegments().size() - 1);
836                                seg.dir = new Vector2f(dir);
837                                seg.perp = new Vector2f(perp);
838                                added.add(seg);
839                        }
840                        
841                        float distPerFluct = FLUCT_LENGTH_MIN + random.nextFloat() * (FLUCT_LENGTH_MAX - FLUCT_LENGTH_MIN);
842                        float fluctAmount = FLUCT_MAG_MIN + random.nextFloat() * (FLUCT_MAG_MAX - FLUCT_MAG_MIN);
843                        float fluctDir = Math.signum(random.nextFloat() - 0.5f);
844                        float distSoFar = 0f;
845                        List<AddedSegment> temp = new ArrayList<AddedSegment>();
846                        AddedSegment prev = null;
847                        for (AddedSegment seg : added) {
848                                if (prev != null) {
849                                        distSoFar += Misc.getDistance(seg.segment.loc, prev.segment.loc);
850                                }
851                                temp.add(seg);
852                                prev = seg;
853                                
854                                if (distSoFar > distPerFluct && temp.size() >= 4) {
855                                        for (int i = 0; i < temp.size(); i++) {
856                                                float t = i / (temp.size() - 1f);
857                                                AddedSegment seg2 = temp.get(i);
858                                                
859                                                float fluctMag = getFluctuationFunc(t);
860                                                fluctMag *= fluctMult;
861                                                //fluctDir = 1f;
862                                                
863                                                fluctMag *= fluctAmount * fluctDir;
864                                                
865                                                seg2.segment.loc.x += seg2.perp.x * fluctMag;
866                                                seg2.segment.loc.y += seg2.perp.y * fluctMag;
867
868                                        }
869                                        temp.clear();
870                                        distSoFar = 0f;
871                                        distPerFluct = FLUCT_LENGTH_MIN + random.nextFloat() * (FLUCT_LENGTH_MAX - FLUCT_LENGTH_MIN);
872                                        fluctAmount = FLUCT_MAG_MIN + random.nextFloat() * (FLUCT_MAG_MAX - FLUCT_MAG_MIN);
873                                        fluctDir = Math.signum(random.nextFloat() - 0.5f);
874                                }
875                        }
876                        
877                        curveLengthSoFar += curr.approxCurveLength;
878                }
879                
880                adjustSharpInflectionPoints();
881                
882                float fadeDist = 500f;
883                float distSoFar = 0f;
884                SlipstreamSegment prev = null;
885                for (int i = 0; i < plugin.getSegments().size(); i++) {
886                        SlipstreamSegment curr = plugin.getSegments().get(i);
887                        if (prev != null) {
888                                distSoFar += Misc.getDistance(prev.loc, curr.loc);
889                        }
890                        if (distSoFar >= fadeDist) {
891                                break;
892                        }
893                        
894                        float b = distSoFar / fadeDist;
895                        if (b < 0f) b = 0f;
896                        if (b > 1f) b = 1f;
897                        curr.bMult = b;
898                        prev = curr;
899                }
900                
901                distSoFar = 0f;
902                prev = null;
903                for (int i = plugin.getSegments().size() - 1; i >= 0; i--) {
904                        SlipstreamSegment curr = plugin.getSegments().get(i);
905                        if (prev != null) {
906                                distSoFar += Misc.getDistance(prev.loc, curr.loc);
907                        }
908                        if (distSoFar >= fadeDist) {
909                                break;
910                        }
911                        
912                        float b = distSoFar / fadeDist;
913                        if (b < 0f) b = 0f;
914                        if (b > 1f) b = 1f;
915                        curr.bMult = b;
916                        prev = curr;
917                }
918        }
919        
920        protected float getFluctuationFunc(float t) {
921                float pi = (float) Math.PI;
922                return ((float)Math.cos(pi + 2f * pi * t) + 1f) * 0.5f;
923        }
924        
925        protected void adjustSharpInflectionPoints() {
926                for (int i = 1; i < plugin.getSegments().size() - 1; i++) {
927                        SlipstreamSegment prev = plugin.getSegments().get(i - 1);                       
928                        SlipstreamSegment curr = plugin.getSegments().get(i);                   
929                        SlipstreamSegment next = plugin.getSegments().get(i + 1);
930                        
931                        float dir1 = Misc.getAngleInDegrees(prev.loc, curr.loc);
932                        float dir2 = Misc.getAngleInDegrees(curr.loc, next.loc);
933                        float diff = Misc.getAngleDiff(dir1, dir2);
934                        if (diff > 5f) {
935                                Vector2f avg = Vector2f.add(prev.loc, next.loc, new Vector2f());
936                                avg.scale(0.5f);
937                                curr.loc.set(Misc.interpolateVector(curr.loc, avg, 0.67f));
938                                //i++;
939                        }
940                }
941        }
942        
943        protected float getWRandMult(SlipstreamSection curr) {
944                float mult = 0f;
945                if (curr.wrand == WRAND_NONE) {
946                        mult = WRAND_NONE_MULT;
947                } else if (curr.wrand == WRAND_LOW) {
948                        mult = WRAND_LOW_MULT;
949                } else if (curr.wrand == WRAND_MEDIUM) {
950                        mult = WRAND_MEDIUM_MULT;
951                } else if (curr.wrand == WRAND_HIGH) {
952                        mult = WRAND_HIGH_MULT;
953                }
954                //mult *= 0.9f + 0.2f * random.nextFloat(); 
955                return mult;
956        }
957        
958        protected float getFluctMult(SlipstreamSection curr) {
959                float mult = 0f;
960                if (curr.fluct == FLUCT_NONE) {
961                        mult = FLUCT_NONE_MULT;
962                } else if (curr.fluct == WRAND_LOW) {
963                        mult = FLUCT_LOW_MULT;
964                } else if (curr.fluct == FLUCT_MEDIUM) {
965                        mult = FLUCT_MEDIUM_MULT;
966                } else if (curr.fluct == FLUCT_HIGH) {
967                        mult = FLUCT_HIGH_MULT;
968                }
969                mult *= 0.9f + 0.2f * random.nextFloat();
970                return mult;
971        }
972        
973        protected float getWChangeMult(SlipstreamSection curr) {
974                float mult = 1f;
975                if (curr.wchange == WCHANGE_SLOW) {
976                        mult = WCHANGE_SLOW_T_MULT_MIN + random.nextFloat() * (WCHANGE_SLOW_T_MULT_MIN_MAX - WCHANGE_SLOW_T_MULT_MIN);
977                } else if (curr.wchange == WCHANGE_MEDIUM) {
978                        mult = WCHANGE_MEDIUM_T_MULT_MIN + random.nextFloat() * (WCHANGE_MEDIUM_T_MULT_MAX - WCHANGE_MEDIUM_T_MULT_MIN);
979                } else if (curr.wchange == WCHANGE_FAST) {
980                        mult = WCHANGE_FAST_T_MULT_MIN + random.nextFloat() * (WCHANGE_FAST_T_MULT_MAX - WCHANGE_FAST_T_MULT_MIN);
981                }
982                return mult;
983        }
984        
985        protected float getGoalWidth(SlipstreamSection curr) {
986                float goalWidth = params.baseWidth;
987                float mult = 1f;
988                if (curr.width == WIDTH_NARROW) {
989                        mult = MIN_NARROW + random.nextFloat() * (MAX_NARROW - MIN_NARROW);
990                } else if (curr.width == WIDTH_NORMAL) {
991                        mult = MIN_NORMAL + random.nextFloat() * (MAX_NORMAL - MIN_NORMAL);
992                } else if (curr.width == WIDTH_WIDE) {
993                        mult = MIN_WIDE + random.nextFloat() * (MAX_WIDE - MIN_WIDE);
994                } else if (curr.width == WIDTH_VERY_WIDE) {
995                        mult = MIN_VERY_WIDE + random.nextFloat() * (MAX_VERY_WIDE - MIN_VERY_WIDE);
996                } 
997                return goalWidth * mult;
998        }
999        
1000        protected void computeControlPoints() {
1001                float angleLimit = 30f;
1002                for (SlipstreamSection curr : sections) {
1003                        SlipstreamSection prev = getPrev(curr);
1004                        SlipstreamSection next = getNext(curr);
1005
1006                        
1007                        if (prev == null && next == null) {
1008                                curr.control = Vector2f.add(curr.from, curr.to, new Vector2f());
1009                                curr.control.scale(0.5f);
1010                        } else if (prev == null && next != null) {
1011                                Vector2f p1 = curr.from;
1012                                Vector2f p2 = curr.to;
1013                                Vector2f p3 = next.to;
1014                                float angleP2ToControl = Misc.getAngleInDegrees(p3, p2);
1015                                Vector2f dirP2ToControl = Misc.getUnitVectorAtDegreeAngle(angleP2ToControl);
1016                                Vector2f p2ToP1 = Vector2f.sub(p1, p2, new Vector2f());
1017                                float angleP2toP1 = Misc.getAngleInDegrees(p2ToP1);
1018                                float angleToControlAndP2toP1 = Vector2f.angle(dirP2ToControl, p2ToP1) * Misc.DEG_PER_RAD;
1019                                if (angleToControlAndP2toP1 > angleLimit) {
1020                                        angleToControlAndP2toP1 = angleLimit;
1021                                        float turnDir = Misc.getClosestTurnDirection(angleP2toP1, angleP2ToControl);
1022                                        angleP2ToControl = angleP2toP1 + turnDir * angleToControlAndP2toP1;
1023                                        dirP2ToControl = Misc.getUnitVectorAtDegreeAngle(angleP2ToControl);
1024                                }
1025                                float dist = Misc.getDistance(p1, p2);
1026                                dist /= 2f;
1027                                float h = dist * (float) Math.tan(angleToControlAndP2toP1 * Misc.RAD_PER_DEG);
1028                                float b = (float) Math.sqrt(dist * dist + h * h);
1029                                dirP2ToControl.scale(b);
1030                                Vector2f.add(dirP2ToControl, p2, curr.control);
1031                        } else {
1032                                Vector2f p1 = prev.control;
1033                                Vector2f p2 = curr.from;
1034                                Vector2f p3 = curr.to;
1035                                float angleP2ToControl = Misc.getAngleInDegrees(p1, p2);
1036                                Vector2f dirP2ToControl = Misc.getUnitVectorAtDegreeAngle(angleP2ToControl);
1037                                Vector2f p2ToP3 = Vector2f.sub(p3, p2, new Vector2f());
1038                                float angleP2ToP3 = Misc.getAngleInDegrees(p2ToP3);
1039                                float angleToControlAndP2toP3 = Vector2f.angle(dirP2ToControl, p2ToP3) * Misc.DEG_PER_RAD;
1040                                if (angleToControlAndP2toP3 > angleLimit) {
1041                                        angleToControlAndP2toP3 = angleLimit;
1042                                        float turnDir = Misc.getClosestTurnDirection(angleP2ToP3, angleP2ToControl);
1043                                        angleP2ToControl = angleP2ToP3 + turnDir * angleToControlAndP2toP3;
1044                                        dirP2ToControl = Misc.getUnitVectorAtDegreeAngle(angleP2ToControl);
1045                                }
1046                                
1047                                float dist = Misc.getDistance(p2, p3);
1048                                dist /= 2f;
1049                                float h = dist * (float) Math.tan(angleToControlAndP2toP3 * Misc.RAD_PER_DEG);
1050                                float b = (float) Math.sqrt(dist * dist + h * h);
1051                                dirP2ToControl.scale(b);
1052                                Vector2f.add(dirP2ToControl, p2, curr.control);
1053                        }
1054                        
1055                        curr.approxCurveLength = getApproximateBezierLength(curr.from, curr.control, curr.to);
1056                }
1057        }
1058        
1059        public void addSection(Vector2f to) {
1060                addSection(to, false);
1061        }
1062        public void addSection(Vector2f to, boolean alreadyPicked) {
1063                if (!alreadyPicked) {
1064                        pickAllNext();
1065                }
1066                addSection(to, currWidth, currWRand, currWChange, currFluct);
1067        }
1068        
1069        public void addSection(Vector2f to, int width, int wrand, int wchange, int fluct) {
1070                addSection(to, width, wrand, wchange, fluct, -1);
1071        }
1072        
1073        public void addSection(Vector2f to, int width, int wrand, int wchange, int fluct, int insertIndex) {
1074                SlipstreamSection s = new SlipstreamSection();
1075                s.to.set(to);
1076                s.width = width;
1077                s.wrand = wrand;
1078                s.wchange = wchange;
1079                s.fluct = fluct;
1080                
1081                if (insertIndex < 0) {
1082                        sections.add(s);
1083                } else {
1084                        sections.add(insertIndex, s);
1085                }
1086                
1087                SlipstreamSection p = getPrev(s);
1088                if (p != null) {
1089                        s.from.set(p.to);
1090                } else {
1091                        s.from.set(start);
1092                }
1093        }
1094        
1095        
1096        public SlipstreamSection getPrev(SlipstreamSection s) {
1097                int index = sections.indexOf(s);
1098                if (index < 1) return null;
1099                return sections.get(index - 1);
1100        }
1101        public SlipstreamSection getNext(SlipstreamSection s) {
1102                int index = sections.indexOf(s);
1103                if (index < 0 || index >= sections.size() - 1) return null;
1104                return sections.get(index + 1);
1105        }
1106        
1107        public void pickAllNext() {
1108                currWidth = pickWidth(currWidth);
1109                currWRand = pickWRand(currWRand);
1110                currWChange = pickWChange(currWChange);
1111                currFluct = pickFluct(currFluct);
1112                
1113                if (currWidth == WIDTH_NARROW) {
1114                        if (currFluct == FLUCT_HIGH) {
1115                                currFluct = FLUCT_LOW;
1116                        } else if (currFluct == FLUCT_MEDIUM) {
1117                                currFluct = FLUCT_NONE;
1118                        }
1119                }
1120        }
1121        
1122        public int pickWidth(int curr) {
1123                return pickNext(curr, widthTM);
1124        }
1125        
1126        public int pickWRand(int curr) {
1127                return pickNext(curr, wrandTM);
1128        }
1129        
1130        public int pickWChange(int curr) {
1131                return pickNext(curr, wchangeTM);
1132        }
1133        
1134        public int pickFluct(int curr) {
1135                return pickNext(curr, fluctTM);
1136        }
1137        
1138        public int pickNext(int curr, float [][] matrix) {
1139                float [] weights = matrix[curr];
1140                WeightedRandomPicker<Integer> picker = new WeightedRandomPicker<Integer>(random);
1141                for (int i = 0; i < weights.length; i++) {
1142                        picker.add(i, weights[i]);
1143                }
1144                return picker.pick();
1145        }
1146        
1147        
1148        public void renderDebug(float alpha) {
1149                //if (true) return;
1150                
1151                GL11.glDisable(GL11.GL_TEXTURE_2D);
1152                GL11.glEnable(GL11.GL_BLEND);
1153                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1154                
1155//              float scale = 0.25f;
1156//              //scale = 1f;
1157//              GL11.glPushMatrix();
1158//              GL11.glTranslatef(sections.get(0).from.x, sections.get(0).from.y, 0f);
1159//              GL11.glScalef(scale, scale, 1f);
1160//              GL11.glTranslatef(sections.get(0).from.x * -scale, sections.get(0).from.y * -scale, 0f);
1161                
1162                if (false) {
1163                        Misc.setColor(Color.white);
1164                        GL11.glEnable(GL11.GL_LINE_SMOOTH);
1165                        GL11.glLineWidth(3f);
1166                        GL11.glBegin(GL11.GL_LINE_STRIP);
1167                        Vector2f p0 = new Vector2f(0, 0);
1168                        Vector2f p1 = new Vector2f(10000, 0);
1169                        Vector2f p2 = new Vector2f(0, 10000);
1170                        Vector2f p3 = new Vector2f(10000, 10000);
1171                        for (float t = 0f; t <= 1f; t += 0.02f) {
1172                                Vector2f p = Misc.bezierCubic(p0, p1, p2, p3, t);
1173                                GL11.glVertex2f(p.x, p.y);
1174                        }
1175                        GL11.glEnd();
1176                }
1177                
1178                if (false) {
1179                        GL11.glEnable(GL11.GL_POINT_SMOOTH);
1180                        GL11.glPointSize(10f);
1181                        GL11.glBegin(GL11.GL_POINTS);
1182                        Misc.setColor(Color.yellow);
1183                        for (SlipstreamSection curr : sections) {
1184                                Misc.setColor(Color.yellow);
1185                                if (curr.subdivision) {
1186                                        Misc.setColor(Color.green);
1187                                }
1188                                Vector2f p = curr.from;
1189                                GL11.glVertex2f(p.x, p.y);
1190                                p = curr.to;
1191                                GL11.glVertex2f(p.x, p.y);
1192                                
1193                                Misc.setColor(Color.cyan);
1194                                p = curr.control;
1195                                GL11.glVertex2f(p.x, p.y);
1196                        }
1197                        GL11.glEnd();
1198                        
1199                        Misc.setColor(Color.white);
1200                        GL11.glEnable(GL11.GL_LINE_SMOOTH);
1201                        GL11.glLineWidth(3f);
1202                        GL11.glBegin(GL11.GL_LINE_STRIP);
1203                        for (SlipstreamSection curr : sections) {
1204                                for (float t = 0f; t <= 1f; t += 0.02f) {
1205                                        Vector2f p = Misc.bezier(curr.from, curr.control, curr.to, t);
1206                                        GL11.glVertex2f(p.x, p.y);
1207                                }
1208                        }
1209                        GL11.glEnd();
1210                }
1211                
1212                for (BoundingBox box : plugin.getBounds()) {
1213                        Misc.setColor(Color.cyan);
1214                        GL11.glEnable(GL11.GL_LINE_SMOOTH);
1215                        GL11.glLineWidth(3f);
1216                        GL11.glBegin(GL11.GL_LINE_LOOP);
1217                        if (box != null) {
1218                                for (Vector2f p : box.box) {
1219                                        GL11.glVertex2f(p.x, p.y);
1220                                }
1221                        }
1222                        GL11.glEnd();
1223                }
1224                
1225                GL11.glPopMatrix();
1226                
1227                if (false) {
1228                GL11.glPushMatrix();
1229                Vector2f loc = Global.getSector().getPlayerFleet().getLocation();
1230                GL11.glTranslatef(loc.x - 1000, loc.y + 100, 0f);
1231//              
1232//              long seed = 23895464576452L + 4384357483229348234L;
1233//              //seed += System.nanoTime() / 1000000000L;
1234//              seed = 1181783497276652981L ^ seed;
1235//              Random random = new Random(seed);
1236//              float spikes = 0.67f;
1237//              float [] noise = initNoise1D(random, 128, spikes); 
1238//              noise[0] = 0.5f;
1239//              noise[noise.length - 1] = 0.5f;
1240//              genNoise1D(random, noise, 128, spikes);
1241//              normalizeNoise1D(noise);
1242                
1243                Misc.setColor(Color.orange);
1244                GL11.glEnable(GL11.GL_LINE_SMOOTH);
1245                GL11.glLineWidth(3f);
1246                GL11.glBegin(GL11.GL_LINE_STRIP);
1247                float horz = 50f;
1248                float vert = 500f;
1249                for (int i = 0; i < buildNoise.length; i++) {
1250                        float f = buildNoise[i];
1251                        GL11.glVertex2f(i * horz, f * vert); 
1252                }
1253                GL11.glEnd();
1254//              GL11.glBegin(GL11.GL_LINE_STRIP);
1255//              float iter = 2000f;
1256//              for (int i = 0; i < iter; i++) {
1257//                      float f = getInterpNoise(noise, i / iter);
1258//                      GL11.glVertex2f(i, f * vert); 
1259//              }
1260//              GL11.glEnd();
1261                Misc.setColor(Color.white);
1262                GL11.glBegin(GL11.GL_LINES);
1263                GL11.glVertex2f(0, 0);
1264                GL11.glVertex2f(buildNoise.length * horz, 0);
1265                GL11.glVertex2f(0, 0);
1266                GL11.glVertex2f(0, vert);
1267                GL11.glEnd();
1268                
1269                GL11.glPopMatrix();
1270                }
1271        }
1272        
1273        public static float getInterpNoise(float [] noise, float t) {
1274                t *= noise.length;
1275                int index = (int) t;
1276                if (index >= noise.length) index = noise.length - 1;
1277                if (index < 0) index = 0;
1278                
1279                int index2 = index + 1;
1280                if (index2 >= noise.length) index2 = index - 1; 
1281                
1282                float f = t - index;
1283                
1284                return Misc.interpolate(noise[index], noise[index2], f);
1285        }
1286        
1287        
1288        /**
1289         * To [0, 1]
1290         * @param noise
1291         */
1292        public static void normalizeNoise1D(float [] noise) {
1293                float min = Float.MAX_VALUE;
1294                float max = -Float.MAX_VALUE;
1295                for (float f : noise) {
1296                        if (f < min) min = f;
1297                        if (f > max) max = f;
1298                }
1299                
1300                if (max <= min) {
1301                        for (int i = 0; i < noise.length; i++) {
1302                                noise[i] = 0.5f;
1303                        }
1304                        return;
1305                }
1306                
1307                float range = max - min;
1308                for (int i = 0; i < noise.length; i++) {
1309                        noise[i] = (noise[i] - min) / range;
1310                }
1311        }
1312        
1313        public static float [] initNoise1D(Random random, int size, float spikes) {
1314                float [] noise = new float[size];
1315                for (int i = 0; i < noise.length; i++) {
1316                        noise[i] = -1f;
1317                }
1318                noise[0] = random.nextFloat() * spikes;
1319                noise[noise.length - 1] = random.nextFloat() * spikes;
1320                return noise;
1321        }
1322        public static void genNoise1D(Random random, float [] noise, int size, float spikes) {
1323                genNoise1DFill(random, noise, 0, noise.length - 1, 1, spikes);
1324        }
1325        
1326        public static void genNoise1DFill(Random random, float [] noise, int x1, int x2, int iter, float spikes) {
1327                if (x1 + 1 >= x2) return;
1328
1329                int midX = (x1 + x2) / 2;
1330
1331                float avg = (noise[x1] + noise[x2]) / 2f;
1332                noise[midX] = avg + ((float) Math.pow(spikes, (iter)) * (float) (random.nextFloat() - .5f));
1333                
1334                genNoise1DFill(random, noise, x1, midX, iter + 1, spikes);
1335                genNoise1DFill(random, noise, midX, x2, iter + 1, spikes);
1336        }
1337
1338        public static float getApproximateBezierLength(Vector2f p0, Vector2f p1, Vector2f p2) {
1339                float total = 0f;
1340                Vector2f prev = p0;
1341                for (float f = 0; f <= 1.01f; f += 0.1f) {
1342                        Vector2f curr = Misc.bezier(p0, p1, p2, f);
1343                        total += Misc.getDistance(prev, curr);
1344                        prev = curr;
1345                }
1346                return total;
1347        }
1348        public static float getApproximateBezierLength(Vector2f p0, Vector2f p1, Vector2f p2, Vector2f p3) {
1349                float total = 0f;
1350                Vector2f prev = p0;
1351                for (float f = 0; f <= 1.01f; f += 0.05f) {
1352                        Vector2f curr = Misc.bezierCubic(p0, p1, p2, p3, f);
1353                        total += Misc.getDistance(prev, curr);
1354                        prev = curr;
1355                }
1356                return total;
1357        }
1358}
1359
1360
1361
1362
1363