/*- * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * * This file is part of CityDoctor2. * * CityDoctor2 is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CityDoctor2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with CityDoctor2. If not, see . */ package de.hft.stuttgart.citydoctor2.datastructure; import java.util.ArrayList; import java.util.List; import de.hft.stuttgart.citydoctor2.check.Check; import de.hft.stuttgart.citydoctor2.math.Vector2d; import de.hft.stuttgart.citydoctor2.math.Vector3d; /** * Represents a linear ring used in polygons. The ring contains the vertices. * * @author Matthias Betz * */ public class LinearRing extends GmlElement { private static final long serialVersionUID = -2488180830514940722L; private LinearRingType type; private Polygon parent; private List vertices = new ArrayList<>(); public enum LinearRingType { EXTERIOR, INTERIOR } public LinearRing(LinearRingType type) { this.type = type; } /** * Checks whether a point is inside this ring. A point on the edge does count as * inside. * * @param v the point. * @return true if the point is inside or on an edge, false if it is outside. */ public boolean isPointInside(Vector3d v) { // project to 2d ring List projectedRing = new ArrayList<>(); Vector2d point; Vector3d normal = calculateNormal(); double x = Math.abs(normal.getX()); double y = Math.abs(normal.getY()); double z = Math.abs(normal.getZ()); if (x > y && x > z) { for (Vertex vert : vertices) { Vector2d projCoords = new Vector2d(vert.getY(), vert.getZ()); projectedRing.add(projCoords); } point = new Vector2d(v.getY(), v.getZ()); } else if (y > x && y > z) { for (Vertex vert : vertices) { Vector2d projCoords = new Vector2d(vert.getX(), vert.getZ()); projectedRing.add(projCoords); } point = new Vector2d(v.getX(), v.getZ()); } else { for (Vertex vert : vertices) { Vector2d projCoords = new Vector2d(vert.getX(), vert.getY()); projectedRing.add(projCoords); } point = new Vector2d(v.getX(), v.getY()); } int t = -1; for (int i = 0; i < projectedRing.size() - 1; i++) { t = t * crossProdTest(point, projectedRing.get(i), projectedRing.get(i + 1)); if (t == 0) { return true; } } return t >= 0; } private int crossProdTest(Vector2d a, Vector2d b, Vector2d c) { if (a.getY() == b.getY() && a.getY() == c.getY()) { if ((b.getX() <= a.getX() && a.getX() <= c.getX()) || (c.getX() <= a.getX() && a.getX() <= b.getX())) { return 0; } else { return 1; } } if (a.getY() == b.getY() && a.getX() == b.getX()) { return 0; } if (b.getY() > c.getY()) { Vector2d temp = b; b = c; c = temp; } if (a.getY() <= b.getY() || a.getY() > c.getY()) { return 1; } return calculateDelta(a, b, c); } private int calculateDelta(Vector2d a, Vector2d b, Vector2d c) { double delta = (b.getX() - a.getX()) * (c.getY() - a.getY()) - (b.getY() - a.getY()) * (c.getX() - a.getX()); if (delta > 0) { return -1; } else if (delta < 0) { return 1; } else { return 0; } } /** * Calculates the normal vector of the ring. Method by Newell. If the Newell * method would return a (0, 0, 0) vector a cross product is formed from the * first 3 vertices. If there are no 3 vertices available (1, 0, 0) is returned. * * @return the normal as a normalized vector */ public Vector3d calculateNormal() { double[] coords = new double[3]; for (int i = 0; i < vertices.size() - 1; i++) { Vertex current = vertices.get(i + 0); Vertex next = vertices.get(i + 1); coords[0] += (current.getZ() + next.getZ()) * (current.getY() - next.getY()); coords[1] += (current.getX() + next.getX()) * (current.getZ() - next.getZ()); coords[2] += (current.getY() + next.getY()) * (current.getX() - next.getX()); } if (coords[0] == 0 && coords[1] == 0 && coords[2] == 0) { // no valid normal vector found if (vertices.size() < 3) { // no three points, return x-axis return new Vector3d(1, 0, 0); } Vertex v1 = vertices.get(0); Vertex v2 = vertices.get(1); Vertex v3 = vertices.get(2); return calculateNormalWithCross(v1, v2, v3); } Vector3d v = new Vector3d(coords); v.normalize(); return v; } private Vector3d calculateNormalWithCross(Vertex v1, Vertex v2, Vertex v3) { Vector3d dir1 = v2.minus(v1); Vector3d dir2 = v3.minus(v1); Vector3d cross = dir1.cross(dir2); return cross.normalize(); } @Override public void accept(Check c) { super.accept(c); if (c.canExecute(this)) { c.check(this); } } @Override public void clearAllContainedCheckResults() { super.clearCheckResults(); } public void setParent(Polygon polygon) { parent = polygon; } public Polygon getParent() { return parent; } public LinearRingType getType() { return type; } public List getVertices() { return vertices; } public void addVertex(Vertex v) { vertices.add(v); if (parent == null) { return; } if (parent.isLinkedTo()) { v.addAdjacentRing(this, parent.getLinkedFromPolygon().getParent()); } v.addAdjacentRing(this, parent.getParent()); } public void addVertex(int i, Vertex v) { vertices.add(i, v); if (parent.isLinkedTo()) { v.addAdjacentRing(this, parent.getLinkedFromPolygon().getParent()); } v.addAdjacentRing(this, parent.getParent()); } public void setVertex(int i, Vertex v) { vertices.set(i, v); if (parent.isLinkedTo()) { v.addAdjacentRing(this, parent.getLinkedFromPolygon().getParent()); } v.addAdjacentRing(this, parent.getParent()); } @Override public String toString() { return "LinearRing [type=" + type + ", gmlId=" + getGmlId() + "]"; } void anonymize() { setGmlId(GmlId.generateId()); } public boolean isRingConnectedViaPoint(LinearRing other) { for (Vertex v : vertices) { if (other.getVertices().contains(v)) { return true; } } return false; } public boolean hasPointAsCorner(Vertex v) { return vertices.contains(v); } public void setType(LinearRingType type) { this.type = type; } public void addAllVertices(List extRing) { vertices.addAll(extRing); } @Override public void prepareForChecking() { parent.getParent().prepareForChecking(); } @Override public void clearMetaInformation() { parent.getParent().clearMetaInformation(); } }