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); /** *
Wyznacznik macierzy (kwadratowej stopnia 3) * [[p.x, p.y, 1], * [q.x, q.y, 1] * [r.x, r.y, 1]] *
* 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); } /** *Wyznacznik macierzy (kwadratowej stopnia 3) * [[p.x, p.y, 1], * [q.x, q.y, 1] * [r.x, r.y, 1]] *
* 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. ** 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(); // } } }