001package com.fs.starfarer.api.impl.campaign.velfield;
002
003import java.util.Random;
004
005import org.lwjgl.util.vector.Vector2f;
006
007import com.fs.starfarer.api.Global;
008import com.fs.starfarer.api.campaign.CampaignFleetAPI;
009import com.fs.starfarer.api.campaign.CampaignTerrainAPI;
010import com.fs.starfarer.api.impl.campaign.ids.Terrain;
011import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamBuilder.StreamType;
012import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamParams2;
013import com.fs.starfarer.api.util.Misc;
014
015/**
016 * 
017 * 4444444444
018 * 3333333333
019 * 2222222222
020 * 1111111111
021 * 0123456789
022 * 
023 * @author Alex
024 *
025 * Copyright 2021 Fractal Softworks, LLC
026 */
027public class VelocityField {
028
029        public static float RAD_PER_DEG = 0.01745329251f;
030        public static Vector2f rotateAroundOrigin(Vector2f v, float cos, float sin) {
031                Vector2f r = new Vector2f();
032                r.x = v.x * cos - v.y * sin;
033                r.y = v.x * sin + v.y * cos;
034                return r;
035        }
036        
037        protected Vector2f[][] field;
038        protected float cellSize;
039        
040        public VelocityField(int width, int height, float cellSize) {
041                field = new Vector2f[width][height];
042                this.cellSize = cellSize;
043                
044                for (int i = 0; i < field.length; i++) {
045                        for (int j = 0; j < field[0].length; j++) {
046                                field[i][j] = new Vector2f();
047                        }
048                }
049        }
050        
051        public Vector2f[][] getField() {
052                return field;
053        }
054
055        public Vector2f getCell(int i, int j) {
056//              if (i < 0 || j < 0) return new Vector2f();
057//              if (i >= field.length || j >= field[0].length) return new Vector2f();
058                if (i < 0) i = 0;
059                if (j < 0) j = 0;
060                if (i >= field.length) i = field.length - 1;
061                if (j >= field[0].length) j = field[0].length - 1;
062                return field[i][j];
063        }
064        
065        public boolean isInsideField(float x, float y, Vector2f bottomLeft, float angle) {
066                updateCacheIfNeeded(angle);
067                
068                x -= bottomLeft.x;
069                y -= bottomLeft.y;
070                Vector2f temp = new Vector2f(x, y);
071                temp = rotateAroundOrigin(temp, cachedCos, -cachedSin);
072                x = temp.x;
073                y = temp.y;
074                
075                if (x < 0) return false;
076                if (y < 0) return false;
077                
078                float w = cellSize * (field.length - 1f);
079                if (x > w) return false;
080                float h = cellSize * (field[0].length - 1f);
081                if (y > h) return false;
082                return true;
083        }
084        
085        transient float cacheKeyAngle;
086        transient float cachedSin;
087        transient float cachedCos;
088        public void updateCacheIfNeeded(float angle) {
089                if (cacheKeyAngle != angle) {
090                        cacheKeyAngle = angle;
091                        cachedCos = (float) Math.cos(angle * RAD_PER_DEG);
092                        cachedSin = (float) Math.sin(angle * RAD_PER_DEG);
093                }
094        }
095        
096        public Vector2f getVelocity(float x, float y, Vector2f bottomLeft, float angle) {
097                updateCacheIfNeeded(angle);
098                
099                x -= bottomLeft.x;
100                y -= bottomLeft.y;
101                Vector2f temp = new Vector2f(x, y);
102                temp = rotateAroundOrigin(temp, cachedCos, -cachedSin);
103                x = temp.x;
104                y = temp.y;
105                
106                int cellX1 = (int) (x / cellSize);
107                int cellY1 = (int) (y / cellSize);
108                if (x < 0) cellX1 = (int) (-1f * Math.abs(x) / cellSize) - 1;
109                if (y < 0) cellY1 = (int) (-1f * Math.abs(y) / cellSize) - 1;
110                
111                int cellX2 = cellX1 + 1;
112                int cellY2 = cellY1 + 1;
113                
114                float px = (x / cellSize) - (float) cellX1;
115                float py = (y / cellSize) - (float) cellY1;
116                
117                // px, py describe where the point (x, y) is in the cell between the vertices of the cell that has 
118                // cellX, cellY as its bottom left corner
119
120                //System.out.println(cellX1 + "," + cellY1 + " from x, cellSize: " + x + "," + cellSize);
121                
122                Vector2f bl = getCell(cellX1, cellY1);
123                Vector2f br = getCell(cellX2, cellY1);
124                Vector2f tl = getCell(cellX1, cellY2);
125                Vector2f tr = getCell(cellX2, cellY2);
126                
127                Vector2f result = new Vector2f();
128                result.x = (1f - py) * (bl.x * (1f - px) + br.x * px) + py * (tl.x * (1f - px) + tr.x * px); 
129                result.y = (1f - px) * (bl.y * (1f - py) + tl.y * py) + px * (br.y * (1f - py) + tr.y * py); 
130                result = rotateAroundOrigin(result, cachedCos, cachedSin);
131                return result;
132        }
133        
134        
135//      public Vector2f getVelocityOld(float x, float y, Vector2f bottomLeft, float angle) {
136//              x -= bottomLeft.x;
137//              y -= bottomLeft.y;
138//              Vector2f temp = new Vector2f(x, y);
139//              temp = rotateAroundOrigin(temp, -angle);
140//              x = temp.x;
141//              y = temp.y;
142//                              
143//              
144//              int cellX1 = (int) (x / cellSize);
145//              int cellY1 = (int) (y / cellSize);
146//              if (x < 0) cellX1 = (int) (-1f * Math.abs(x) / cellSize) - 1;
147//              if (y < 0) cellY1 = (int) (-1f * Math.abs(y) / cellSize) - 1;
148//              
149//              int cellX2 = cellX1 + 1;
150//              int cellY2 = cellY1 + 1;
151//              
152//              float px = (x / cellSize) - (float) cellX1;
153//              float py = (y / cellSize) - (float) cellY1;
154//              
155//              // px, py describe where the point (x, y) is in the cell between the vertices of the cell that has 
156//              // cellX, cellY as its bottom left corner
157//
158//              //System.out.println(cellX1 + "," + cellY1 + " from x, cellSize: " + x + "," + cellSize);
159//              
160//              Vector2f bl = getCell(cellX1, cellY1);
161//              Vector2f br = getCell(cellX2, cellY1);
162//              Vector2f tl = getCell(cellX1, cellY2);
163//              Vector2f tr = getCell(cellX2, cellY2);
164//              
165//              Vector2f result = new Vector2f();
166//              result.x = (1f - py) * (bl.x * (1f - px) + br.x * px) + py * (tl.x * (1f - px) + tr.x * px); 
167//              result.y = (1f - px) * (bl.y * (1f - py) + tl.y * py) + px * (br.y * (1f - py) + tr.y * py); 
168//              result = rotateAroundOrigin(result, angle);
169//              return result;
170//      }
171
172        
173        public float getCellSize() {
174                return cellSize;
175        }
176        
177        public void shiftDown() {
178                for (int i = 0; i < field.length; i++) {
179                        for (int j = 0; j < field[0].length - 1; j++) {
180                                field[i][j] = field[i][j + 1];
181                        }
182                }
183                
184                for (int i = 0; i < field.length; i++) {
185                        field[i][field[0].length - 1] = new Vector2f(); 
186                }
187        }
188
189        public static void spawnTest() {
190                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
191                SlipstreamParams2 params = new SlipstreamParams2();
192                
193                params.burnLevel = 30;
194                params.minSpeed = Misc.getSpeedForBurnLevel(params.burnLevel - 5);
195                params.maxSpeed = Misc.getSpeedForBurnLevel(params.burnLevel + 5);
196                params.lineLengthFractionOfSpeed = 0.25f * Math.max(0.25f, Math.min(1f, 30f / (float) params.burnLevel));
197                
198//              params.minColor = new Color(0.5f, 0.3f, 0.75f, 0.1f);
199//              params.maxColor = new Color(0.5f, 0.6f, 1f, 0.3f);
200                
201                //params.slowDownInWiderSections = true;
202                
203//              params.baseWidth = 1025f;
204//              params.widthForMaxSpeed = 768f;
205                
206//              float width = 512;
207//              params.widthForMaxSpeed = width;
208                
209                //params.numParticles = 500;
210//              CustomCampaignEntityAPI e = 
211//                              Global.getSector().getCurrentLocation().addCustomEntity(null, null, "slipstream2", Factions.NEUTRAL, params);
212//              e.setLocation(pf.getLocation().x + 200f, pf.getLocation().y + 200f);
213                
214                
215                CampaignTerrainAPI slipstream = (CampaignTerrainAPI) Global.getSector().getCurrentLocation().addTerrain(Terrain.SLIPSTREAM, params);
216                slipstream.setLocation(pf.getLocation().x + 200f, pf.getLocation().y + 200f);
217                
218                SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) slipstream.getPlugin();
219                float spacing = 200f;
220                
221                long seed = 23895464576452L + 4384357483229348234L;
222                seed = 1181783497276652981L ^ seed;
223                seed *= 27;
224                Random random = new Random(seed);
225                //random = Misc.random;
226                SlipstreamBuilder builder = new SlipstreamBuilder(slipstream.getLocation(), plugin, StreamType.WIDE, random);
227                //builder.buildTest();
228                Vector2f to = new Vector2f(slipstream.getLocation());
229                to.x += 20000;
230                to.y += 20000;
231//              to.x += 160000;
232//              to.y += 100000;
233                //builder.buildToDestination(to);
234                Vector2f control = new Vector2f(slipstream.getLocation());
235                control.x += 10000f;
236                //control.x += 160000f;
237                builder.buildToDestination(control, to);
238                //builder.buildToDestination(to);
239                
240                //new SlipstreamManager().checkIntersectionsAndFadeSections(plugin);
241                
242                //plugin.despawn(5f);
243                
244//              int iter = 100;
245//              for (int i = 0; i < iter; i++) {
246//                      float yOff = (float) Math.sin(i * 0.05f);
247//                      float w = width + i * 10f;
248//                      //float currSpacing = Math.max(spacing, width / 2f);
249//                      float currSpacing = spacing;
250//                      plugin.addSegment(new Vector2f(slipstream.getLocation().x + i * currSpacing,
251//                      //addSegment(new Vector2f(entity.getLocation().x + i * (spacing + (50 - i) * 5),
252//                                      slipstream.getLocation().y + yOff * 2000f), 
253//                                      //width);
254//                                      //width * (0.7f + (float) Math.random() * 0.7f));
255//                                      w);
256//              }
257                
258                
259                //SlipstreamEntityPlugin plugin = (SlipstreamEntityPlugin) e.getCustomPlugin();
260                //TurbulenceEntityPlugin plugin = (TurbulenceEntityPlugin) e.getCustomPlugin();
261                
262//              VelocityField f = new VelocityField(11, 31, 100f);
263//              plugin.setField(f);
264                
265                
266                
267        }
268
269        
270}
271
272
273