Files
terrain-utilities/src/main/java/pl/wat/ms4ds/terrain/GeomUtils.java

1255 lines
44 KiB
Java

package pl.wat.ms4ds.terrain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.wat.ms4ds.common.EGeoDirection;
public class GeomUtils {
private static final Logger logger = LoggerFactory.getLogger(GeomUtils.class);
/**
* <p> Wyznacznik macierzy (kwadratowej stopnia 3)
* [[p.x, p.y, 1],
* [q.x, q.y, 1]
* [r.x, r.y, 1]]
* </p>
* Pozwala na określenie położenie punktu względem wektora.
*
* @param p Wsp poczatku wektora
* @param q Wsp konca wektora
* @param r Wsp testowanego punktu
* @return det3 == 0, gdy punkt jest wspolliniowy, det3 wieksze od 0, gdy punkt lezy po lewej stronie wektora,
* det3 mniejsze od 0, gdy punkt lezy po prawej stronie wektora,
*/
public static long det3(Coord.Grid p, Coord.Grid q, Coord.Grid r) {
return p.x * (q.y - r.y) + q.x * (r.y - p.y) + r.x * (p.y - q.y);
}
/**
* <p> Wyznacznik macierzy (kwadratowej stopnia 3)
* [[p.x, p.y, 1],
* [q.x, q.y, 1]
* [r.x, r.y, 1]]
* </p>
* Pozwala na określenie położenie punktu względem wektora.
*
* @param px Wsp x poczatku wektora
* @param py Wsp y poczatku wektora
* @param qx Wsp x końca wektora
* @param qy Wsp y końca wektora
* @param rx Wsp x testowanego punktu
* @param ry Wsp y testowanego punktu
* @return det3 == 0, gdy punkt jest wspolliniowy, det3 wieksze od 0, gdy punkt lezy po lewej stronie wektora,
* det3 mniejsze od 0, gdy punkt lezy po prawej stronie wektora,
*/
public static long det3(int px, int py, int qx, int qy, int rx, int ry) {
return px * (qy - ry) + qx * (ry - py) + rx * (py - qy);
}
/**
* Sprawdza, czy punkt r należy do odcinka p-q
*
* @param p Poczatek odcinka
* @param q Koniec odcinka
* @param r Punkt testowany
* @return true, jesli punkt r lezy na odcinku
*/
public static boolean include(Coord.Grid p, Coord.Grid q, Coord.Grid r) {
return det3(p, q, r) == 0 &&
Math.min(p.x, q.x) <= r.x &&
r.x <= Math.max(p.x, q.x) &&
Math.min(p.y, q.y) <= r.y &&
r.y <= Math.max(p.y, q.y);
}
/**
* Sprawdza, czy punkt r należy do odcinka p-q
*
* @param px Wsp x poczatku wektora
* @param py Wsp y poczatku wektora
* @param qx Wsp x końca wektora
* @param qy Wsp y końca wektora
* @param rx Wsp x testowanego punktu
* @param ry Wsp y testowanego punktu
* @return true, jesli punkt r lezy na odcinku
*/
public static boolean include(int px, int py, int qx, int qy, int rx, int ry) {
return Math.min(px, qx) <= rx &&
rx <= Math.max(px, qx) &&
Math.min(py, qy) <= ry &&
ry <= Math.max(py, qy) &&
det3(px, py, qx, qy, rx, ry) == 0;
}
/**
* Sprawdza, czy punkt r należy do poziomego odcinka p-q
*
* @param p Poczatek odcinka
* @param q Koniec odcinka
* @param r Punkt testowany
* @return true, jeśli punkt r należy do odcinka p-q
*/
public static boolean includeHorizontaly(Coord.Grid p, Coord.Grid q, Coord.Grid r) {
return p.y == r.y &&
Math.min(p.x, q.x) <= r.x &&
r.x <= Math.max(p.x, q.x);
// return det3(p, q, r) == 0 &&
// Math.min(p.x, q.x) <= r.x &&
// r.x <= Math.max(p.x, q.x) &&
// Math.min(p.y, q.y) <= r.y &&
// r.y <= Math.max(p.y, q.y);
}
/**
* Sprawdza, czy punkt r należy do poziomego odcinka p-q
*
* @param px Wsp x poczatku wektora
* @param py Wsp y poczatku wektora
* @param qx Wsp x końca wektora
* @param qy Wsp y końca wektora
* @param rx Wsp x testowanego punktu
* @param ry Wsp y testowanego punktu
* @return true, jeśli punkt r należy do odcinka p-q
*/
public static boolean includeHorizontaly(int px, int py, int qx, int qy, int rx, int ry) {
return py == ry &&
Math.min(px, qx) <= rx &&
rx <= Math.max(px, qx);
}
/**
* Określenie znaku liczby
*
* @param x Testowana liczba
* @return 0, gdy x == 0, 1 gdy x wieksze od 0, -1 gdy x mniejsze od 0
*/
public static int sgn(long x) {
if (x == 0) {
return 0;
}
if (x > 0) {
return 1;
}
return -1;
}
/**
* Sprawdza, czy odcinki p-q i r-s przecinają się wewnetrznie tzn.
* mają dokładnie jeden punkt wspólny różny od każdego z konców odcinków.
*
* @param p punkt początkowy pierwszego odcinka
* @param q punkt końcowy pierwszego odcinka
* @param r punkt początkowy drugiego odcinka
* @param s punkt końcowy drugiego odcinka
* @return true, jeśli odcinki przecinają się wewnętrznie
*/
public static boolean cross(Coord.Grid p, Coord.Grid q, Coord.Grid r, Coord.Grid s) {
long det3_pqr = det3(p, q, r);
long det3_pqs = det3(p, q, s);
long det3_rsp = det3(r, s, p);
long det3_rsq = det3(r, s, q);
return sgn(det3_pqr) != sgn(det3_pqs) &&
sgn(det3_rsp) != sgn(det3_rsq) &&
det3_pqr != 0 &&
det3_pqs != 0 &&
det3_rsp != 0 &&
det3_rsq != 0;
// return sgn(det3(p, q, r)) != sgn(det3(p, q, s)) &&
// sgn(det3(r, s, p)) != sgn(det3(r, s, q)) &&
// !include(p, q, r) &&
// !include(p, q, s) &&
// !include(r, s, p) &&
// !include(r, s, q);
}
/**
* Sprawdza, czy odcinki p-q i r-s przecinają się wewnetrznie tzn.
* mają dokładnie jeden punkt wspólny różny od każdego z konców odcinków.
*
* @param px wsp x punktu początkowego pierwszego odcinka
* @param py wsp y punktu początkowego pierwszego odcinka
* @param qx wsp x punktu końcowego pierwszego odcinka
* @param qy wsp y punktu końcowego pierwszego odcinka
* @param rx wsp x punktu początkowego drugiego odcinka
* @param ry wsp y punktu początkowego drugiego odcinka
* @param sx wsp x punktu końcowego drugiego odcinka
* @param sy wsp y punktu końcowego drugiego odcinka
* @return true, jeśli odcinki przecinają się wewnętrznie
*/
public static boolean cross(int px, int py, int qx, int qy, int rx, int ry, int sx, int sy) {
long det3_pqr = det3(px, py, qx, qy, rx, ry);
long det3_pqs = det3(px, py, qx, qy, sx, sy);
long det3_rsp = det3(rx, ry, sx, sy, px, py);
long det3_rsq = det3(rx, ry, sx, sy, qx, qy);
return sgn(det3_pqr) != sgn(det3_pqs) &&
sgn(det3_rsp) != sgn(det3_rsq) &&
det3_pqr != 0 &&
det3_pqs != 0 &&
det3_rsp != 0 &&
det3_rsq != 0;
// return sgn(det3(p, q, r)) != sgn(det3(p, q, s)) &&
// sgn(det3(r, s, p)) != sgn(det3(r, s, q)) &&
// !include(p, q, r) &&
// !include(p, q, s) &&
// !include(r, s, p) &&
// !include(r, s, q);
}
/**
* Sprawdza, czy odcinki p-q i r-s mają dowolną część wspólną.
*
* @param p punkt początkowy pierwszego odcinka
* @param q punkt końcowy pierwszego odcinka
* @param r punkt początkowy drugiego odcinka
* @param s punkt końcowy drugiego odcinka
* @return true, jeśli odcinki mają część wspólną
*/
public static boolean intersection(Coord.Grid p, Coord.Grid q, Coord.Grid r, Coord.Grid s) {
long det3_pqr = det3(p, q, r);
long det3_pqs = det3(p, q, s);
long det3_rsp = det3(r, s, p);
long det3_rsq = det3(r, s, q);
return (sgn(det3_pqr) != sgn(det3_pqs) && sgn(det3_rsp) != sgn(det3_rsq)) ||
(det3_pqr == 0 && det3_pqs == 0 &&
((Math.min(p.x, q.x) <= r.x &&
r.x <= Math.max(p.x, q.x) &&
Math.min(p.y, q.y) <= r.y &&
r.y <= Math.max(p.y, q.y)) ||
(Math.min(p.x, q.x) <= s.x &&
s.x <= Math.max(p.x, q.x) &&
Math.min(p.y, q.y) <= s.y &&
s.y <= Math.max(p.y, q.y))));
}
/**
* Wyznacza ograniczenia dolne i górne współrzędnych: xMin, yMin, xMax, yMax.
*
* @param vertices
* @return vertices bounds: 0 - xMin, 1 - yMin, 2 - xMax, 3 - yMax
*/
public static int[] getBounds(Coord.Grid[] vertices) {
int xMin = Integer.MAX_VALUE;
int yMin = Integer.MAX_VALUE;
int xMax = Integer.MIN_VALUE;
int yMax = Integer.MIN_VALUE;
for (Coord.Grid v : vertices) {
xMin = Math.min(xMin, v.x);
yMin = Math.min(yMin, v.y);
xMax = Math.max(xMax, v.x);
yMax = Math.max(yMax, v.y);
}
return new int[]{xMin, yMin, xMax, yMax};
}
/**
* Funkcja bada zawieranie się punktu w ramach wielokąta.
*
* @param polygon
* @param x
* @param y
* @return
*/
public static boolean insidePolygon(Coord.Grid[] polygon, int x, int y) {
// Sprawdzenie należenia punktu (x, y) do dowolnej krawędzi wielokąta.
//
for (int i = 0; i < polygon.length; i++) {
int x1 = polygon[i].x;
int y1 = polygon[i].y;
int ii = (i + 1) % polygon.length;
int x2 = polygon[ii].x;
int y2 = polygon[ii].y;
if (include(x1, y1, x2, y2, x, y)) {
return true;
}
}
// Sprawdzenie należenia punktu (x, y) do wnętrza wielokąta. Zliczanie liczby przecięć promienia idącego od
// punktu w prawo równolegle do osi OX.
// Nieparzysta liczba oznacza należenie.
//
int count = 0;
for (int i = 0; i < polygon.length; i++) {
int x1 = polygon[i].x;
int y1 = polygon[i].y;
int ii = (i + 1) % polygon.length;
int x2 = polygon[ii].x;
int y2 = polygon[ii].y;
if (y < y1 != y < y2) {
int dy = y2 - y1;
int a = (x - x1) * dy;
int b = (y - y1) * (x2 - x1);
if (dy >= 0) {
if (a < b) {
count++;
}
} else {
if (b < a) {
count++;
}
}
}
}
return (count % 2) == 1;
}
/**
* Funkcja bada należenie punktu do prostokąta zorientowanego równolegle do osi współrzędnych.
*
* @param xMin minimalna współrzędna X prostokąta (lewego dolnego rogu)
* @param yMin minimalna współrzędna Y prostokąta (lewego dolnego rogu)
* @param xMax maksymalna współrzędna X prostokąta (prawego górnego rogu)
* @param yMax maksymalna współrzędna Y prostokąta (prawego górnego rogu)
* @param xTest współrzędna X punktu testowanego
* @param yTest współrzędna Y punktu testowanego
* @return true, jeśli testowany punkt należy do prostokąta
*/
public static boolean insideRectangle(int xMin, int yMin, int xMax, int yMax, int xTest, int yTest) {
if (xTest < xMin || xTest > xMax) {
return false;
}
return yTest >= yMin && yTest <= yMax;
}
// tangens kąta alfa = 22.5 stopnia
static final double TANG_22_5_DEG = Math.tan(Math.PI / 8);
// tangens kąta alfa = 22.5 stopnia
static final double TANG_67_5_DEG = Math.tan(3 * Math.PI / 8);
/**
* Wyznacza najbliższy kierunek geograficzny dla zadanego wektora.
*
* @param start kwadrat początkowy wektora
* @param stop kwadrat końcowy wektora
* @return kierunek geograficzny klasy EGeoDirection
*/
public static EGeoDirection estimateDirection(Coord.Grid start, Coord.Grid stop) {
if (start.x == stop.x && start.y == stop.y) {
return EGeoDirection.UNDEFINED;
}
// polnoc-poludnie
if (start.x == stop.x) {
if (start.y < stop.y) {
return EGeoDirection.NORTH; //na polnoc OK
} else {
// Start.Y >= Stop.Y
return EGeoDirection.SOUTH; //na poludnie OK
}
}
// wschod-zachod
if (start.y == stop.y) {
if (start.x < stop.x) {
return EGeoDirection.EAST; //na wschod OK
} else {
// Start.X >= Stop.X
return EGeoDirection.WEST; //na zachod OK
}
}
// pozostale kierunki
double dx = stop.x - start.x;
double dy = stop.y - start.y;
if (dx > 0) {
double tgAlfa = dy / dx;
if (tgAlfa >= TANG_67_5_DEG) {
return EGeoDirection.NORTH;
} else if (tgAlfa >= TANG_22_5_DEG) {
return EGeoDirection.NORTHEAST;
} else if (tgAlfa >= -TANG_22_5_DEG) {
return EGeoDirection.EAST;
} else if (tgAlfa >= -TANG_67_5_DEG) {
return EGeoDirection.SOUTHEAST;
} else {
return EGeoDirection.SOUTH;
}
} else {
double tgAlfa = dy / -dx;
if (tgAlfa >= TANG_67_5_DEG) {
return EGeoDirection.NORTH;
} else if (tgAlfa >= TANG_22_5_DEG) {
return EGeoDirection.NORTHWEST;
} else if (tgAlfa >= -TANG_22_5_DEG) {
return EGeoDirection.WEST;
} else if (tgAlfa >= -TANG_67_5_DEG) {
return EGeoDirection.SOUTHWEST;
} else {
return EGeoDirection.SOUTH;
}
}
}
private static final EGeoDirection[][] NEIGHBOR_DIRECTIONS = new EGeoDirection[][]{
{EGeoDirection.SOUTHWEST, EGeoDirection.WEST, EGeoDirection.NORTHWEST},
{EGeoDirection.SOUTH, EGeoDirection.UNDEFINED, EGeoDirection.NORTH},
{EGeoDirection.SOUTHEAST, EGeoDirection.EAST, EGeoDirection.NORTHEAST},
};
public static EGeoDirection neighborDirection(int xStart, int yStart, int xEnd, int yEnd) {
// Przesuwam różnice współrzędnych o jeden tzn. {-1, 0, 1} -> {0, 1, 2}, aby użyć do indeksowania.
//
int dx = xEnd - xStart + 1;
int dy = yEnd - yStart + 1;
return NEIGHBOR_DIRECTIONS[dx][dy];
}
/**
* Funkcja zwraca kierunek geograficzny dla wektora opisanego na sasiednich kwadratach.
*
* @param start współrzędne (X, Y) siatki kwadratu początkowego
* @param stop współrzędne (X, Y) siatki kwadratu końcowego
* @return kierunek geograficzny klasy EGeoDirection dla wektora zadanego przez kwadrat początkowy
* i końcowy lub UNDEFINED dla kwadratów niesąsiednich
*/
public static EGeoDirection kierunekDlaSasiada(Coord.Grid start, Coord.Grid stop) {
int d = Math.abs(start.x - stop.x) + Math.abs(start.y - stop.y);
if (d == 1 || d == 2) {
if (start.x == stop.x) {
if (start.y < stop.y) {
return EGeoDirection.NORTH;
}
return EGeoDirection.SOUTH;
}
if (start.y == stop.y) {
if (start.x < stop.x) {
return EGeoDirection.EAST;
}
return EGeoDirection.WEST;
}
if (start.x < stop.x) {
if (start.y < stop.y) {
return EGeoDirection.NORTHEAST;
}
return EGeoDirection.SOUTHEAST;
}
if (start.y < stop.y) {
return EGeoDirection.NORTHWEST;
}
return EGeoDirection.SOUTHWEST;
} else {
return EGeoDirection.UNDEFINED;
}
}
/**
* Funkcja zwraca kierunek geograficzny dla wektora opisanego na sasiednich kwadratach.
*
* @param xStart współrzędne X siatki kwadratu początkowego
* @param yStart współrzędne Y siatki kwadratu początkowego
* @param xStop współrzędne X siatki kwadratu końcowego
* @param yStop współrzędne Y siatki kwadratu końcowego
* @return kierunek geograficzny dla wektora zadanego przez kwadrat początkowy i końcowy lub UNDEFINED dla kwadratów niesąsiednich
*/
public static EGeoDirection kierunekDlaSasiada(int xStart, int yStart, int xStop, int yStop) {
int d = Math.abs(xStart - xStop) + Math.abs(yStart - yStop);
if (d == 1 || d == 2) {
if (xStart == xStop) {
if (yStart < yStop) {
return EGeoDirection.NORTH;
}
return EGeoDirection.SOUTH;
}
if (yStart == yStop) {
if (xStart < xStop) {
return EGeoDirection.EAST;
}
return EGeoDirection.WEST;
}
if (xStart < xStop) {
if (yStart < yStop) {
return EGeoDirection.NORTHEAST;
}
return EGeoDirection.SOUTHEAST;
}
if (yStart < yStop) {
return EGeoDirection.NORTHWEST;
}
return EGeoDirection.SOUTHWEST;
} else {
return EGeoDirection.UNDEFINED;
}
}
private static final float PRZEKATNA_MK = MapConsts.SS_SIZE * (float) Math.sqrt(2);
/**
* Funkcja wyznacza odległość między środkami kwadratów sąsiednich na zadanym kierunku
*
* @param kierunek zadany kierunek sąsiedztwa
* @return odległość między środkami kwadratów sąsiednich [m]
*/
public static float odlegloscKwadratowSasiednich(EGeoDirection kierunek) {
switch (kierunek) {
case NORTH:
case SOUTH:
case EAST:
case WEST:
return MapConsts.SS_SIZE;
case NORTHEAST:
case NORTHWEST:
case SOUTHEAST:
case SOUTHWEST:
return PRZEKATNA_MK;
default:
return 0.0f;
}
}
/**
* Funkcja wyznacza sumaryczną długość drogi podanej jako ciąg sąsiednich kwadratów
*
* @param road droga jako ciąg sąsiednich kwadratów
* @return sumaryczna długość drogi [m]
*/
public static float dlugoscDrogiPoKwadratch(Coord.Grid[] road) {
float dl = 0;
for (int i = 1; i < road.length; i++) {
dl += Coord.Grid.distance(road[i - 1], road[i]);
}
return dl;
}
/**
* Metoda generuje tablicę współrzędnych gridowych kolejnych punktów/kwadratów podanego odcinka.
* <p>
* Wykorzystuje algorytm Bresenhama.
*
* @param x1 współrzędna x początku
* @param y1 współrzędna y początku
* @param x2 współrzędna x końca
* @param y2 współrzędna y końca
* @return tablicę kolejnych kwadratów tworzących odcinek o zadanych końcach
*/
public static Coord.Grid[] generateSquaresOfSegment(int x1, int y1, int x2, int y2) {
int x = x1;
int y = y1;
int dx = x2 - x1;
int dy = y2 - y1;
final int sx = Integer.compare(dx, 0);
final int sy = Integer.compare(dy, 0);
if (dx < 0) {
dx = -dx;
}
if (dy < 0) {
dy = -dy;
}
final int ddx = 2 * dx;
final int ddy = 2 * dy;
int p;
if (dx >= dy) {
// poruszamy się po x
p = ddy - dx;
Coord.Grid[] res = new Coord.Grid[dx + 1];
for (int i = 0; i <= dx; i++) {
res[i] = new Coord.Grid(x, y);
if (p > 0) {
y += sy;
p -= ddx;
}
x += sx;
p += ddy;
}
return res;
} else {
// poruszamy sie po y
p = ddx - dy;
Coord.Grid[] res = new Coord.Grid[dy + 1];
for (int i = 0; i <= dy; i++) {
res[i] = new Coord.Grid(x, y);
if (p > 0) {
x += sx;
p -= ddy;
}
y += sy;
p += ddx;
}
return res;
}
}
public static int[] generateSquaresOfSegment2(int x1, int y1, int x2, int y2) {
int x = x1;
int y = y1;
int dx = x2 - x1;
int dy = y2 - y1;
final int sx = Integer.compare(dx, 0);
final int sy = Integer.compare(dy, 0);
if (dx < 0) {
dx = -dx;
}
if (dy < 0) {
dy = -dy;
}
final int ddx = 2 * dx;
final int ddy = 2 * dy;
int p;
int size;
int[] res;
if (dx >= dy) {
// poruszamy się po x
p = ddy - dx;
// rozmiar tablicy 2 razy liczba współrzędnych
size = (dx + 1) << 1;
res = new int[size];
for (int i = 0; i < size; ) {
res[i++] = x;
res[i++] = y;
if (p > 0) {
y += sy;
p -= ddx;
}
x += sx;
p += ddy;
}
return res;
} else {
// poruszamy sie po y
p = ddx - dy;
// rozmiar tablicy 2 razy liczba współrzędnych
size = (dy + 1) << 1;
res = new int[size];
for (int i = 0; i < size; ) {
res[i++] = x;
res[i++] = y;
if (p > 0) {
x += sx;
p -= ddy;
}
y += sy;
p += ddx;
}
return res;
}
}
static void test(int... args) {
int z = args[0];
}
/**
* Funkcja wyznacza tablicę kwadratów leżących na łamanej.
*
* @param vertices tablica wspolrzednych wierzcholkow tworzacych lamana
* @return tablica (ciąg) sąsiednich kwadratów leżących na łamanej
*/
public static Coord.Grid[] generateSquaresOfSegments(Coord.Grid[] vertices) {
if (vertices.length == 2) {
return generateSquaresOfSegment(vertices[0].x, vertices[0].y, vertices[1].x, vertices[1].y);
}
Coord.Grid[][] tempSegments = new Coord.Grid[vertices.length - 1][];
int size = 0;
for (int i = 0; i < tempSegments.length; i++) {
tempSegments[i] = generateSquaresOfSegment(vertices[i].x, vertices[i].y, vertices[i + 1].x, vertices[i + 1].y);
if (i == 0) {
// będą kopiowane wszystkie kwadraty
size += tempSegments[i].length;
} else {
// kopiowane wszystkie poza pierwszym
size += tempSegments[i].length - 1;
}
}
Coord.Grid[] res = new Coord.Grid[size];
int destPos = 0;
for (int i = 0; i < tempSegments.length; i++) {
int len;
int srcPos;
if (i == 0) {
// Pierwszy segment łamanej (zawiera wszystkie kwadraty).
srcPos = 0;
len = tempSegments[i].length;
} else {
// Kolejny segment łamanej (bez pierwszego kwadratu).
srcPos = 1;
len = tempSegments[i].length - 1;
}
System.arraycopy(tempSegments[i], srcPos, res, destPos, len);
destPos += len;
}
return res;
}
/**
* Funkcja wykrywa orientację wierzchołków: zgodność z ruchem wskazówek zegara.
*
* @param poly
* @return
*/
public static boolean isClockwise(Coord.Grid[] poly) {
if (poly.length <= 2) {
return true;
}
long sum = 0;
for (int i = 0; i < poly.length; i++) {
int next = (i + 1) % poly.length;
sum += (long) (poly[next].x - poly[i].x) * (poly[next].y + poly[i].y);
}
// If sum > 0, it is clockwise. If < 0, it is counter-clockwise.
return sum >= 0;
}
/**
* Funkcja usuwa z tablicy wejściowej rejon wierzchołków obszaru powtarzające się sąsiednie wierzchołki.
*
* @param vertices tablica wierzchołków
* @return poprawioną tablicę wierzchołków
*/
public static Coord.Grid[] removeAdjacentDuplicates(Coord.Grid[] vertices) {
if (vertices == null) {
return null;
}
if (vertices.length < 2) {
return vertices;
}
int i = 0;
for (int j = i + 1; j < vertices.length; j++) {
if (!vertices[i].equals(vertices[j])) {
i++;
vertices[i] = vertices[j];
}
}
if (i > 0 && vertices[0].equals(vertices[i])) {
// Pominięcie ostatniego wierzchołka identycznego z pierwszym (zamykający wielokąt).
i--;
}
Coord.Grid[] newArr = new Coord.Grid[i + 1];
System.arraycopy(vertices, 0, newArr, 0, newArr.length);
return newArr;
}
/**
* Funkcja usuwa z listy wierzchołki pośrednie tworzące "schodek". Wykorzystywana w przypadku obiektów liniowych
* (łamanych).
*
* @param vertices
* @return
*/
public static Coord.Grid[] removeSteps(Coord.Grid[] vertices) {
if (vertices.length < 3) {
return vertices;
}
boolean[] toRemove = new boolean[vertices.length];
Coord.Grid node_i;
Coord.Grid node_ii;
Coord.Grid node_iii;
boolean stepDetected = false;
int count = 0;
for (int i = 0; i < vertices.length - 2; i++) {
node_i = vertices[i];
node_ii = vertices[i + 1];
node_iii = vertices[i + 2];
int absX_i_ii = Math.abs(node_ii.x - node_i.x);
int absY_i_ii = Math.abs(node_ii.y - node_i.y);
int absX_ii_iii = Math.abs(node_iii.x - node_ii.x);
int absY_ii_iii = Math.abs(node_iii.y - node_ii.y);
if (absX_i_ii + absY_i_ii + absX_ii_iii + absY_ii_iii == 2) {
// the nodes can form a step
if (absX_i_ii + absX_ii_iii == 1) {
// the nodes form a step, so the middle node of the step should be removed
toRemove[i + 1] = true;
count++;
stepDetected = true;
// przeskok o usuwany wezel
i++;
}
}
}
if (stepDetected) {
Coord.Grid[] newArr = new Coord.Grid[vertices.length - count];
int j = 0;
for (int i = 0; i < vertices.length; i++) {
if (!toRemove[i]) {
newArr[j++] = vertices[i];
}
}
vertices = newArr;
return removeSteps(vertices);
}
return vertices;
}
public static Coord.Grid[] removeCollinearity(Coord.Grid[] vertices, boolean isPolygon) {
if (vertices.length < 3) {
return vertices;
}
boolean[] toDelete = new boolean[vertices.length];
Coord.Grid curr_node;
Coord.Grid next_node;
Coord.Grid prev_node;
boolean collinearFound = false;
int lenMax = vertices.length;
int iStart = 0;
if (!isPolygon) {
// łamana otwarta
lenMax--;
iStart = 1;
}
final int len = vertices.length;
int count = 0;
for (int curr = iStart; curr < lenMax; curr++) {
int next = (curr + 1) % len;
int prev = (curr + len - 1) % len;
curr_node = vertices[curr];
next_node = vertices[next];
prev_node = vertices[prev];
if (GeomUtils.include(prev_node.x, prev_node.y, next_node.x, next_node.y, curr_node.x, curr_node.y)) {
// i-ty do usuniecia
toDelete[curr] = true;
collinearFound = true;
count++;
}
}
if (collinearFound) {
Coord.Grid[] newArr = new Coord.Grid[len - count];
int j = 0;
for (int i = 0; i < len; i++) {
if (!toDelete[i]) {
newArr[j++] = vertices[i];
}
}
vertices = newArr;
return removeCollinearity(vertices, isPolygon);
}
return vertices;
}
/**
* Funkcja odwraca kolejność wierzchołków w tablicy.
*
* @param vertices
* @return
*/
public static Coord.Grid[] reverse(Coord.Grid[] vertices) {
if (vertices == null || vertices.length == 0) {
return vertices;
}
int mid = vertices.length / 2;
int last = vertices.length - 1;
for (int i = 0; i < mid; i++) {
vertices[i] = vertices[last - i];
}
return vertices;
}
/**
* Funkcja wyznacza srodek zadanego odcinka
*
*/
public static Coord.Grid srodekOdcinka(int x1, int y1, int x2, int y2) {
int xsr = (x1 + x2) / 2;
int ysr = (y1 + y2) / 2;
Coord.Grid srodek = new Coord.Grid(xsr, ysr);
return srodek;
}
/**
* Funkcja zwraca odległość między środkami małych kwadratów [m].
*
* @param x1 Wsp X id malego kwadratu pocz.
* @param y1 Wsp Y id malego kwadratu pocz.
* @param x2 Wsp X id malego kwadratu konc.
* @param y2 Wsp Y id malego kwadratu konc.
* @return Odległość miedzy srodkami malych kwadratow [m].
*/
public static float odleglosc(int x1, int y1, int x2, int y2) {
// odleglosc miedzy srodkami malych kwadratow
int xx = x1 - x2;
xx *= xx;
int yy = y1 - y2;
yy *= yy;
float odl = (float) Math.sqrt(xx + yy);
odl *= MapConsts.SS_SIZE;
return odl;
}
public static void print(Coord.Grid[] segment) {
StringBuilder sb = new StringBuilder(300);
sb.append('[');
int last = segment.length - 1;
for (int i = 0; i < segment.length; i++) {
sb.append('(');
sb.append(segment[i].x);
sb.append(',');
sb.append(segment[i].y);
sb.append(')');
if (i < last) {
sb.append('|');
}
}
sb.append(']');
System.out.println(sb);
}
// =====================================================================
public static void main(String[] args) {
// Coord.Grid[] segments = new Coord.Grid[4];
// segments[0] = new Coord.Grid(1, 1);
// segments[1] = new Coord.Grid(6, 4);
// segments[2] = new Coord.Grid(7, 3);
// segments[3] = new Coord.Grid(8, 9);
//
// Coord.Grid[] squares = generateSquaresOfSegments(segments);
// print(squares);
int x0 = 10;
int y0 = 10;
int x1 = 10;
int y1 = 10;
EGeoDirection d = neighborDirection(x0, y0, x1, y1);
x1 = 9;
y1 = 11;
d = neighborDirection(x0, y0, x1, y1);
x1 = 10;
y1 = 11;
d = neighborDirection(x0, y0, x1, y1);
x1 = 11;
y1 = 11;
d = neighborDirection(x0, y0, x1, y1);
x1 = 11;
y1 = 10;
d = neighborDirection(x0, y0, x1, y1);
x1 = 9;
x1 = 11;
y1 = 9;
d = neighborDirection(x0, y0, x1, y1);
x1 = 10;
y1 = 9;
d = neighborDirection(x0, y0, x1, y1);
x1 = 9;
y1 = 9;
d = neighborDirection(x0, y0, x1, y1);
x1 = 9;
y1 = 10;
d = neighborDirection(x0, y0, x1, y1);
Coord.Grid[] segments = new Coord.Grid[12];
segments[0] = new Coord.Grid(1, 2);
segments[1] = new Coord.Grid(2, 3);
segments[2] = new Coord.Grid(2, 4);
segments[3] = new Coord.Grid(2, 5);
segments[4] = new Coord.Grid(3, 5);
segments[5] = new Coord.Grid(4, 5);
segments[6] = new Coord.Grid(5, 6);
segments[7] = new Coord.Grid(6, 6);
segments[8] = new Coord.Grid(6, 7);
segments[9] = new Coord.Grid(7, 8);
segments[10] = new Coord.Grid(8, 8);
segments[11] = new Coord.Grid(8, 7);
Coord.Grid[] squares = removeSteps(segments);
print(segments);
print(squares);
double odl = Coord.Grid.distance(1, 1, 2, 2);
double odl2 = Coord.Grid.distanceApprox(1, 1, 2, 2);
logger.debug("dist = {}, dist2 = {}, DELTA = {}, delta = {}", odl, odl2, odl - odl2, (odl - odl2) / odl);
odl = Coord.Grid.distance(1, 1, 21, 21);
odl2 = Coord.Grid.distanceApprox(1, 1, 21, 21);
logger.debug("dist = {}, dist2 = {}, DELTA = {}, delta = {}", odl, odl2, odl - odl2, (odl - odl2) / odl);
odl = Coord.Grid.distance(1, 1, 1, 21);
odl2 = Coord.Grid.distanceApprox(1, 1, 1, 21);
logger.debug("dist = {}, dist2 = {}, DELTA = {}, delta = {}", odl, odl2, odl - odl2, (odl - odl2) / odl);
odl = Coord.Grid.distance(1, 1, 10, 21);
odl2 = Coord.Grid.distanceApprox(1, 1, 10, 21);
logger.debug("dist = {}, dist2 = {}, DELTA = {}, delta = {}", odl, odl2, odl - odl2, (odl - odl2) / odl);
odl = Coord.Grid.distance(1, 1, 100, 210);
odl2 = Coord.Grid.distanceApprox(1, 1, 100, 210);
logger.debug("dist = {}, dist2 = {}, DELTA = {}, delta = {}", odl, odl2, odl - odl2, (odl - odl2) / odl);
odl = Coord.Grid.distance(1, 1, 5, 210);
odl2 = Coord.Grid.distanceApprox(1, 1, 5, 210);
logger.debug("dist = {}, dist2 = {}, DELTA = {}, delta = {}", odl, odl2, odl - odl2, (odl - odl2) / odl);
Coord.Grid p = new Coord.Grid(4, 6);
Coord.Grid q = new Coord.Grid(8, 2);
Coord.Grid r = new Coord.Grid(6, 4);
Coord.Grid s = new Coord.Grid(8, 6);
Coord.Grid startCoord = new Coord.Grid(10, 10);
Coord.Grid stopCoord = new Coord.Grid();
for (int y = 20; y >= 0; y--) {
for (int x = 0; x <= 20; x++) {
stopCoord.set(x, y);
EGeoDirection kier = estimateDirection(startCoord, stopCoord);
switch (kier) {
case NORTH:
System.out.print('|');
break;
case NORTHEAST:
System.out.print('/');
break;
case EAST:
System.out.print('-');
break;
case SOUTHEAST:
System.out.print('\\');
break;
case SOUTH:
System.out.print('|');
break;
case SOUTHWEST:
System.out.print('/');
break;
case WEST:
System.out.print('-');
break;
case NORTHWEST:
System.out.print('\\');
break;
case UNDEFINED:
System.out.print('O');
break;
}
}
System.out.println();
}
System.out.println();
EGeoDirection kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(10, 15));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(10, 5));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 10));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 10));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(9, 15));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(11, 15));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(9, 5));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(11, 5));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 9));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 11));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 9));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 11));
System.out.println(kier);
// Kierunki pośrednie: PLN-WSC, PLD-WSC, PLN-ZAC, PLD-ZAC
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 15));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 5));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 15));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 5));
System.out.println(kier);
// PLN-WSC
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 16));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 14));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(16, 15));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(14, 15));
System.out.println(kier);
// PLD-WSC
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 6));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(15, 4));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(16, 5));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(14, 5));
System.out.println(kier);
//PLN-ZAC
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 16));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 14));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(6, 15));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(4, 15));
System.out.println(kier);
// PLD-ZAC
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 6));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(5, 4));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(6, 5));
System.out.println(kier);
kier = estimateDirection(new Coord.Grid(10, 10), new Coord.Grid(4, 5));
System.out.println(kier);
boolean przeciecie = intersection(p, q, r, s);
r.set(8, 2);
s.set(11, 5);
przeciecie = intersection(p, q, r, s);
r.set(6, 4);
s.set(7, 3);
przeciecie = intersection(p, q, r, s);
r.set(6, 4);
s.set(10, 0);
przeciecie = intersection(p, q, r, s);
r.set(4, 4);
s.set(6, 6);
przeciecie = intersection(p, q, r, s);
r.set(4, 8);
s.set(6, 10);
przeciecie = intersection(p, q, r, s);
r.set(2, 8);
s.set(1, 9);
przeciecie = intersection(p, q, r, s);
Coord.Grid[] lamana = {new Coord.Grid(8, 8), new Coord.Grid(3, 4), new Coord.Grid(6, 11)};
long det = det3(1, 0, 4, 0, 2, 2);
det = det3(1, 0, 4, 0, 2, -2);
det = det3(1, 1, 1, 4, 0, 2);
det = det3(1, 1, 1, 4, 2, 2);
det = det3(1, 1, 4, 4, 4, 3);
det = det3(1, 1, 4, 4, 3, 4);
// Coord.GridCoord[] polygon = new Coord.GridCoord[21];
// polygon[0] = new Coord.GridCoord(12, 18);
// polygon[1] = new Coord.GridCoord(16, 18);
// polygon[2] = new Coord.GridCoord(18, 13);
// polygon[3] = new Coord.GridCoord(21, 13);
// polygon[4] = new Coord.GridCoord(22, 9);
// polygon[5] = new Coord.GridCoord(20, 7);
// polygon[6] = new Coord.GridCoord(17, 7);
// polygon[7] = new Coord.GridCoord(15, 9);
// polygon[8] = new Coord.GridCoord(13, 7);
// polygon[9] = new Coord.GridCoord(15, 5);
// polygon[10] = new Coord.GridCoord(17, 5);
// polygon[11] = new Coord.GridCoord(14, 2);
// polygon[12] = new Coord.GridCoord(8, 8);
// polygon[13] = new Coord.GridCoord(9, 3);
// polygon[14] = new Coord.GridCoord(6, 4);
// polygon[15] = new Coord.GridCoord(3, 4);
// polygon[16] = new Coord.GridCoord(2, 10);
// polygon[17] = new Coord.GridCoord(6, 11);
// polygon[18] = new Coord.GridCoord(6, 13);
// polygon[19] = new Coord.GridCoord(7, 15);
// polygon[20] = new Coord.GridCoord(10, 13);
//
//
// // przesunięcie wielokata o wektor
// for (int k = 0; k < polygon.length; k++) {
// polygon[k].x += 1000;
// polygon[k].y += 1000;
// }
// Coord.GridCoord p = new Coord.GridCoord();
// for (int j = 20; j >= 0; j--) {
// for (int i = 0; i < 25; i++) {
// p.x = i + 1000;
// p.y = j + 1000;
// char c = ' ';
// if (insidePolygon(polygon, p)) {
// c = 'O';
// }
// for (int k = 0; k < polygon.length; k++) {
// if (polygon[k].equals(p)) {
// c = 'X';
// break;
// }
// }
// System.out.print(c);
// System.out.print(' ');
// }
// System.out.println();
// }
// int lp = args.length / 2;
// for (int i = 0; i < lp; i++) {
// p.x = Integer.parseInt(args[2 * i]);
// p.y = Integer.parseInt(args[2 * i + 1]);
// if (insidePolygon(polygon, p)) {
// System.out.println("Punkt " + p + " leży wewnatrz");
// } else {
// System.out.println("Punkt " + p + " leży na zewnatrz");
// }
// }
// float odl = odleglosc(5000, 4000, 2, 2);
// odl = odleglosc(1, 1000, 3999, 3);
// odl = odleglosc(5, 1, 27, 11);
// odl = odleglosc(10, 1, 2, 20);
// odl = odleglosc(1, 1, 2, 2);
//
// Coord.GridCoord[] rejon = new Coord.GridCoord[4];
// rejon[0] = new Coord.GridCoord(20, 18);
// rejon[1] = new Coord.GridCoord(27, 9);
// rejon[2] = new Coord.GridCoord(9, 1);
// rejon[3] = new Coord.GridCoord(2, 15);
//
// ArrayList< Coord.GridCoord> kwadraty = kwadratyLamanej(rejon);
// System.out.println(kwadraty);
// try {
// BufferedWriter wyj = new BufferedWriter(new FileWriter("A.txt"));
// if (args.length == 5 ) {
// int xp = Integer.parseInt(args[1]);
// int yp = Integer.parseInt(args[2]);
// int xk = Integer.parseInt(args[3]);
// int yk = Integer.parseInt(args[4]);
/*
* int xp = 50000; int yp = 8000; int xk = 20; int yk = 1;
*/
// Coord.GridCoord[] rejon = new Coord.GridCoord[4];
// rejon[0] = new Coord.GridCoord();
// rejon[0].x = 20;
// rejon[0].y = 18;
// rejon[1] = new Coord.GridCoord();
// rejon[1].x = 27;
// rejon[1].y = 9;
// rejon[2] = new Coord.GridCoord();
// rejon[2].x = 9;
// rejon[2].y = 1;
// rejon[3] = new Coord.GridCoord();
// rejon[3].x = 2;
// rejon[3].y = 15;
// Coord.GridCoord[] kwadraty = kwadratyObszaru(rejon);
/*
* WspXY[] kwadraty = kwadratyOdcinka(xp, yp, xk, yk);
* wyj.write("pocz: ("); wyj.write(Integer.toString(xp));
* wyj.write(", "); wyj.write(Integer.toString(yp)); wyj.write(")");
* wyj.newLine(); wyj.write("kon: (");
* wyj.write(Integer.toString(xk)); wyj.write(", ");
* wyj.write(Integer.toString(yk)); wyj.write(")"); wyj.newLine();
*/
// wyj.write(Integer.toString(kwadraty.length));
// wyj.newLine();
// for (int i = 0; i < kwadraty.length; i++) {
// wyj.write("(");
// wyj.write(Integer.toString(kwadraty[i].x));
// wyj.write(", ");
// wyj.write(Integer.toString(kwadraty[i].y));
// wyj.write(")");
// wyj.newLine();
// }
//
// wyj.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
}
}