Files
sunny9898/task8/_ref/xxx_flow-field-pathfinding-processing-master/flow_field/Grid.pde
louiscklaw 5637fbf94f update,
2025-02-01 02:07:58 +08:00

323 lines
11 KiB
Plaintext

class Point {
public int x;
public int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point() {
this(0, 0);
}
public boolean equals(Point other) {
return x == other.x && y == other.y;
}
}
class SectorPath {
int[][] integrationField;
boolean[][] losField;
PVector[][] flowField;
Point sectorDest;
SectorPath(int[][] integrationField, boolean[][] losField, PVector[][] flowField, Point sectorDest) {
this.integrationField = integrationField;
this.losField = losField;
this.flowField = flowField;
this.sectorDest = sectorDest;
}
}
class GridPath {
SectorPath[][] sectorPaths;
GridPath(SectorPath[][] sectorPaths) {
this.sectorPaths = sectorPaths;
}
}
class Grid {
int rows;
int cols;
int sectorSize;
float spaceSizeX;
float spaceSizeY;
private Graph portalGraph;
private Sector[][] sectors;
// First two levels are sector, second two are destination
// within sector
private SectorPath[][][][] generatedPaths;
private byte[][] costField;
private ArrayList<Body> gridBodies;
Grid(int rows, int cols, int sectorSize) {
this.rows = rows;
this.cols = cols;
this.spaceSizeX = (float)RES_X / (float)this.rows;
this.spaceSizeY = (float)RES_Y / (float)this.cols;
this.costField = new byte[rows][cols];
for (int i = 0; i < costField.length; ++i) {
for (int j = 0; j < costField[i].length; ++j) {
costField[i][j] = 1;
}
}
this.gridBodies = new ArrayList<Body>();
this.PrepSectors(sectorSize);
}
void PrepSectors(int sectorSize) {
if (rows % sectorSize != 0 || cols % sectorSize != 0) {
throw new IllegalArgumentException("Bad sector size");
}
this.sectorSize = sectorSize;
this.sectors = new Sector[rows / sectorSize][cols / sectorSize];
for (int i = 0; i < this.sectors.length; ++i) {
for (int j = 0; j < this.sectors[i].length; ++j) {
this.sectors[i][j] = new Sector(new Point(i * sectorSize, j * sectorSize), sectorSize, sectorSize);
}
}
this.CalcPortalGraph();
}
void CalcPortalGraph() {
// Cleanup
for (int i = 0; i < gridBodies.size(); ++i) {
box2d.world.destroyBody(gridBodies.get(i));
}
gridBodies.clear();
this.generatedPaths = new SectorPath[rows / sectorSize][cols / sectorSize][sectorSize][sectorSize];
this.portalGraph = new Graph();
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
// Create a hard barrier for impassable terrain
if (costField[i][j] == Byte.MAX_VALUE) {
// Define a polygon
PolygonShape sd = new PolygonShape();
sd.setAsBox(box2d.scalarPixelsToWorld(spaceSizeX / 2.0),
box2d.scalarPixelsToWorld(spaceSizeY / 2.0));
// Define a fixture
FixtureDef fd = new FixtureDef();
fd.shape = sd;
// Parameters that affect physics
fd.density = 1;
fd.friction = 0.5;
fd.restitution = 0.2;
// Define the body and make it from the shape
BodyDef bd = new BodyDef();
bd.type = BodyType.STATIC;
bd.position.set(box2d.coordPixelsToWorld(
new PVector(CalcScreenCoordsFromPoint(new Point(i, j)).x + (spaceSizeX / 2.0),
CalcScreenCoordsFromPoint(new Point(i, j)).y + (spaceSizeY / 2.0)
)));
Body body = box2d.createBody(bd);
body.createFixture(fd);
gridBodies.add(body);
}
}
}
// Add nodes that have adjacent nodes in other sectors on the right and bottom,
// so as to not have any repeat
ArrayList<ArrayList<ArrayList<Pt>>> sectorPts = new ArrayList<ArrayList<ArrayList<Pt>>>();
for (int i = 0; i < sectors.length; ++i) {
sectorPts.add(new ArrayList<ArrayList<Pt>>());
for (int j = 0; j < sectors[i].length; ++j) {
sectorPts.get(i).add(new ArrayList<Pt>());
}
}
// For each sector, starting with the top left and ending with the bottom right,
// check the right and bottom sides to see if there exists a "portal", two adjacent
// clear spaces between sectors, and add an edge if so
for (int i = 0; i < sectors.length; ++i) {
for (int j = 0; j < sectors[i].length; ++j) {
if (i != sectors.length - 1) {
for (int k = sectors[i][j].start.y; k < sectors[i][j].start.y + sectors[i][j].cols; ++k) {
if (costField[sectors[i][j].start.x + sectors[i][j].rows - 1][k] != Byte.MAX_VALUE &&
costField[sectors[i][j].start.x + sectors[i][j].rows][k] != Byte.MAX_VALUE) {
sectorPts.get(i).get(j).add(portalGraph.addPt(sectors[i][j].start.x + sectors[i][j].rows - 1, k));
sectorPts.get(i+1).get(j).add(portalGraph.addPt(sectors[i][j].start.x + sectors[i][j].rows, k));
portalGraph.addEdge(sectorPts.get(i).get(j).get(sectorPts.get(i).get(j).size()-1).name,
sectorPts.get(i+1).get(j).get(sectorPts.get(i+1).get(j).size()-1).name);
}
}
}
if (j != sectors[i].length - 1) {
for (int k = sectors[i][j].start.x; k < sectors[i][j].start.x + sectors[i][j].rows; ++k) {
if (costField[k][sectors[i][j].start.y + sectors[i][j].cols - 1] != Byte.MAX_VALUE &&
costField[k][sectors[i][j].start.y + sectors[i][j].cols] != Byte.MAX_VALUE) {
sectorPts.get(i).get(j).add(portalGraph.addPt(k, sectors[i][j].start.y + sectors[i][j].cols - 1));
sectorPts.get(i).get(j+1).add(portalGraph.addPt(k, sectors[i][j].start.y + sectors[i][j].cols));
portalGraph.addEdge(sectorPts.get(i).get(j).get(sectorPts.get(i).get(j).size()-1).name,
sectorPts.get(i).get(j+1).get(sectorPts.get(i).get(j+1).size()-1).name);
}
}
}
}
}
// Connect all edges within a sector to each other
for (int i = 0; i < sectorPts.size(); ++i) {
for (int j = 0; j < sectorPts.get(i).size(); ++j) {
ArrayList<Pt> pts = sectorPts.get(i).get(j);
for (int k = 0; k < pts.size(); ++k) {
for (int l = 0; l < pts.size(); ++l) {
if (pts.get(k).name == pts.get(l).name) {
continue;
}
portalGraph.addUndirectedEdge(pts.get(k).name, pts.get(l).name);
}
}
}
}
}
void IncreaseCost(PVector p, byte amount) {
Point a = CalcFromScreenCoords(p);
if ((int)this.costField[a.x][a.y] + (int)amount > (int)Byte.MAX_VALUE) {
this.costField[a.x][a.y] = Byte.MAX_VALUE;
} else {
this.costField[a.x][a.y] += amount;
}
}
void DecreaseCost(PVector p, byte amount) {
Point a = CalcFromScreenCoords(p);
if (this.costField[a.x][a.y] - amount < 0) {
this.costField[a.x][a.y] = 0;
} else {
this.costField[a.x][a.y] -= amount;
}
}
SectorPath GetSectorPath(Point gridDest) {
Point sector = GetSector(gridDest);
Point destInSector = GridToSector(gridDest);
// Check for an existing cached sector path to the same destination
if (this.generatedPaths[sector.x][sector.y][destInSector.x][destInSector.y] != null) {
return this.generatedPaths[sector.x][sector.y][destInSector.x][destInSector.y];
}
int[][] integrationField = new int[sectorSize][sectorSize];
boolean[][] losField = new boolean[sectorSize][sectorSize];
PVector[][] flowField = new PVector[sectorSize][sectorSize];
for (int i = 0; i < sectorSize; ++i) {
for (int j = 0; j < sectorSize; ++j) {
integrationField[i][j] = 0;
losField[i][j] = false;
flowField[i][j] = new PVector(0, 0);
}
}
sectors[sector.x][sector.y].CalcFlowField(destInSector, integrationField, losField, flowField);
this.generatedPaths[sector.x][sector.y][destInSector.x][destInSector.y] =
new SectorPath(integrationField, losField, flowField, destInSector);
return this.generatedPaths[sector.x][sector.y][destInSector.x][destInSector.y];
}
GridPath Pathing(Point a, Point b) {
if (!IsValidSpace(b)) {
return null;
}
ArrayList<Pt> res = portalGraph.aStar(a.x, a.y, b.x, b.y);
SectorPath[][] sectorPaths = new SectorPath[rows / sectorSize][cols / sectorSize];
Point destSector = GetSector(b);
if (res.size() < 2) {
// We are in the sector already so just generate it
sectorPaths[destSector.x][destSector.y] = GetSectorPath(b);
} else {
Point lastSector = new Point(-1, -1);
for (int i = 0; i < res.size(); ++i) {
Point iSector = GetSector(new Point(res.get(i).x, res.get(i).y));
if (iSector.x == destSector.x && iSector.y == destSector.y) {
sectorPaths[destSector.x][destSector.y] = GetSectorPath(b);
lastSector = new Point(destSector.x, destSector.y);
} else if ((lastSector.x != iSector.x || lastSector.y != iSector.y) && i > 0) {
sectorPaths[iSector.x][iSector.y] = GetSectorPath(new Point(res.get(i).x, res.get(i).y));
lastSector = iSector;
// Point the destination in each sector towards the portal to the next sector
Point resSector = GetSector(new Point(res.get(i).x, res.get(i).y));
Point resSectorPoint = GridToSector(new Point(res.get(i).x, res.get(i).y));
PVector finalMove = new PVector(res.get(i-1).x - res.get(i).x, res.get(i-1).y - res.get(i).y);
sectorPaths[resSector.x][resSector.y].flowField[resSectorPoint.x][resSectorPoint.y] = finalMove;
}
}
}
return new GridPath(sectorPaths);
}
boolean IsValidSpace(Point p) {
return p.x >= 0 && p.x < rows && p.y >= 0 && p.y < cols;
}
Point CalcFromScreenCoords(PVector point) {
return new Point((int)(point.x / spaceSizeX), (int)(point.y / spaceSizeY));
}
PVector CalcScreenCoordsFromPoint(Point a) {
return new PVector((float)a.x * spaceSizeX, (float)a.y * spaceSizeY);
}
Point SectorToGrid(Point sectorStart, Point p) {
return new Point(p.x + sectorStart.x, p.y + sectorStart.y);
}
Point GridToSector(Point p) {
Point sector = GetSector(p);
return new Point(p.x - sectors[sector.x][sector.y].start.x, p.y - sectors[sector.x][sector.y].start.y);
}
Point GetSector(Point p) {
return new Point(p.x / (sectorSize), p.y / (sectorSize));
}
void DrawGrid() {
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
if (costField[i][j] < Byte.MAX_VALUE) {
stroke(costField[i][j] * 2, 255, 0);
fill(costField[i][j] * 2, 255, 0);
} else {
stroke(255, 0, 0);
fill(255, 0, 0);
}
PVector a = CalcScreenCoordsFromPoint(new Point(i, j));
rect(a.x, a.y, spaceSizeX, spaceSizeY);
}
}
stroke(0);
for (int i = 1; i <= this.rows; i++) {
line(i*spaceSizeX, 0, i*spaceSizeX, spaceSizeY * this.cols);
line(0, i*spaceSizeY, spaceSizeX * this.rows, i*spaceSizeY);
}
stroke(0);
if (DEBUG_ON) {
portalGraph.draw();
}
}
}