/*-
 *  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 <https://www.gnu.org/licenses/>.
 */
package de.hft.stuttgart.citydoctor2.checks.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.Collections;
import java.util.List;

import org.junit.Test;

import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.checks.geometry.RingSelfIntCheck;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType;
import de.hft.stuttgart.citydoctor2.datastructure.Lod;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.Segment3d;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection.IntersectionType;

/**
 * 
 * @author Matthias Betz
 *
 */
public class SelfIntersectionUtilTest {

	@Test
	public void testNonIntersection1() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(0, 0, 1);
		Vertex v4 = new Vertex(10, 0, 1);
		Vertex v5 = new Vertex(10, 10, 1);

		Polygon p2 = new ConcretePolygon();

		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.NONE, intersection.getType());
	}

	@Test
	public void testNonIntersection2() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(0, 0, -1);
		Vertex v4 = new Vertex(0, 10, -1);
		Vertex v5 = new Vertex(10, 10, -1);

		Polygon p2 = new ConcretePolygon();

		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.NONE, intersection.getType());
	}

	@Test
	public void testLineIntersectionTouchingPolygonsWithoutCommonEdge() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(7, 7, 5);
		Vertex v4 = new Vertex(5, 5, -5);
		Vertex v5 = new Vertex(7, 7, -5);

		Polygon p2 = new ConcretePolygon();

		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.NONE, intersection.getType());
//		Segment3d line = intersection.getLines().get(0);
//		assertEquals(new Vector3d(6, 6, 0), line.getPointA());
//		assertEquals(new Vector3d(7, 7, 0), line.getPointB());
	}
	
	@Test
	public void testNonIntersection3() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(7, 7, 5);
		Vertex v4 = new Vertex(0, 0, 0);
		Vertex v5 = new Vertex(10, 10, 0);

		Polygon p2 = new ConcretePolygon();

		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.NONE, intersection.getType());
	}

	@Test
	public void testIntersection1() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(7, 6, 5);
		Vertex v4 = new Vertex(5, 5, -5);
		Vertex v5 = new Vertex(7, 7, -5);

		Polygon p2 = new ConcretePolygon();

		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.LINE, intersection.getType());
		Vector3d point1 = new Vector3d(6, 5.5, 0);
		Vector3d point2 = new Vector3d(7, 6.5, 0);
		List<Segment3d> lines = intersection.getLines();
		assertNotNull(lines);
		assertEquals(1, lines.size());
		Segment3d segment = lines.get(0);
		assertTrue(point1.equals(segment.getPointA()) || point1.equals(segment.getPointB()));
		assertTrue(point2.equals(segment.getPointA()) || point2.equals(segment.getPointB()));
	}

	@Test
	public void testIntersection2() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(7, 0, 5);
		Vertex v4 = new Vertex(5, -5, -5);
		Vertex v5 = new Vertex(7, 7, -5);

		Polygon p2 = new ConcretePolygon();

		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.LINE, intersection.getType());
		Vector3d point1 = new Vector3d(6.41666, 0, 0);
		Vector3d point2 = new Vector3d(7, 3.5, 0);
		List<Segment3d> lines = intersection.getLines();
		assertNotNull(lines);
		assertEquals(1, lines.size());
		Segment3d segment = lines.get(0);
		assertTrue(point1.equalsWithEpsilon(segment.getPointA(), 0.01)
				|| point1.equalsWithEpsilon(segment.getPointB(), 0.01));
		assertTrue(point2.equalsWithEpsilon(segment.getPointA(), 0.01)
				|| point2.equalsWithEpsilon(segment.getPointB(), 0.01));
	}

	@Test
	public void testIntersectionWithTwoLineSegments() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(7, 0, 5);
		Vertex v4 = new Vertex(5, -5, -5);
		Vertex v5 = new Vertex(9, 3, -5);
		Vertex v6 = new Vertex(9, 3, 5);
		Vertex v7 = new Vertex(9, 2, -3);

		Polygon p2 = new ConcretePolygon();

		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v6);
		r2.addVertex(v7);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.LINE, intersection.getType());
		List<Segment3d> lines = intersection.getLines();
		assertNotNull(lines);
		assertEquals(2, lines.size());
		Segment3d segment1 = lines.get(0);
		Vector3d point1 = new Vector3d(7.40, 0, 0);
		Vector3d point2 = new Vector3d(8.25, 1.25, 0);
		assertTrue(point1.equalsWithEpsilon(segment1.getPointA(), 0.01)
				|| point1.equalsWithEpsilon(segment1.getPointB(), 0.01));
		assertTrue(point2.equalsWithEpsilon(segment1.getPointA(), 0.01)
				|| point2.equalsWithEpsilon(segment1.getPointB(), 0.01));

		Segment3d segment2 = lines.get(1);
		point1 = new Vector3d(9, 2.375, 0);
		point2 = new Vector3d(9, 3.0, 0);
		assertTrue(point1.equalsWithEpsilon(segment2.getPointA(), 0.01)
				|| point1.equalsWithEpsilon(segment2.getPointB(), 0.01));
		assertTrue(point2.equalsWithEpsilon(segment2.getPointA(), 0.01)
				|| point2.equalsWithEpsilon(segment2.getPointB(), 0.01));
	}

	@Test
	public void testIntersectionWithInnerRings() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex i0 = new Vertex(7, 1, 0);
		Vertex i1 = new Vertex(7, 0.5, 0);
		Vertex i2 = new Vertex(9, 0.5, 0);
		Vertex i3 = new Vertex(9, 1, 0);
		LinearRing ri = new LinearRing(LinearRingType.INTERIOR);
		p1.addInteriorRing(ri);
		ri.addVertex(i0);
		ri.addVertex(i1);
		ri.addVertex(i2);
		ri.addVertex(i3);
		ri.addVertex(i0);

		Vertex v3 = new Vertex(7, 0, 5);
		Vertex v4 = new Vertex(5, -5, -5);
		Vertex v5 = new Vertex(9, 3, -5);
		Vertex v6 = new Vertex(9, 3, 5);
		Vertex v7 = new Vertex(9, 2, -3);

		Polygon p2 = new ConcretePolygon();

		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v6);
		r2.addVertex(v7);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.LINE, intersection.getType());
		List<Segment3d> lines = intersection.getLines();
		assertNotNull(lines);
		assertEquals(3, lines.size());
		Segment3d segment1 = lines.get(0);
		Vector3d point1 = new Vector3d(7.40, 0, 0);
		Vector3d point2 = new Vector3d(7.684, 0.5, 0);
		assertTrue(point1.equalsWithEpsilon(segment1.getPointA(), 0.01)
				|| point1.equalsWithEpsilon(segment1.getPointB(), 0.01));
		assertTrue(point2.equalsWithEpsilon(segment1.getPointA(), 0.01)
				|| point2.equalsWithEpsilon(segment1.getPointB(), 0.01));

		Segment3d segment2 = lines.get(1);
		point1 = new Vector3d(7.96, 1, 0);
		point2 = new Vector3d(8.25, 1.25, 0);
		assertTrue(point1.equalsWithEpsilon(segment2.getPointA(), 0.01)
				|| point1.equalsWithEpsilon(segment2.getPointB(), 0.01));
		assertTrue(point2.equalsWithEpsilon(segment2.getPointA(), 0.01)
				|| point2.equalsWithEpsilon(segment2.getPointB(), 0.01));

		Segment3d segment3 = lines.get(2);
		point1 = new Vector3d(9, 2.375, 0);
		point2 = new Vector3d(9, 3.0, 0);
		assertTrue(point1.equalsWithEpsilon(segment3.getPointA(), 0.01)
				|| point1.equalsWithEpsilon(segment3.getPointB(), 0.01));
		assertTrue(point2.equalsWithEpsilon(segment3.getPointA(), 0.01)
				|| point2.equalsWithEpsilon(segment3.getPointB(), 0.01));
	}
	
	@Test
	public void testIntersectionWithInnerRingsOneLineInInnerRing() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex i0 = new Vertex(7, 2, 0);
		Vertex i1 = new Vertex(7, 5, 0);
		Vertex i2 = new Vertex(9.5, 5, 0);
		Vertex i3 = new Vertex(9.5, 2, 0);
		LinearRing ri = new LinearRing(LinearRingType.INTERIOR);
		p1.addInteriorRing(ri);
		ri.addVertex(i0);
		ri.addVertex(i1);
		ri.addVertex(i2);
		ri.addVertex(i3);
		ri.addVertex(i0);

		Vertex v3 = new Vertex(7, 0, 5);
		Vertex v4 = new Vertex(5, -5, -5);
		Vertex v5 = new Vertex(9, 3, -5);
		Vertex v6 = new Vertex(9, 3, 5);
		Vertex v7 = new Vertex(9, 2, -3);

		Polygon p2 = new ConcretePolygon();

		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v6);
		r2.addVertex(v7);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.LINE, intersection.getType());
		List<Segment3d> lines = intersection.getLines();
		assertNotNull(lines);
		assertEquals(1, lines.size());
		Segment3d segment1 = lines.get(0);
		Vector3d point1 = new Vector3d(7.40, 0, 0);
		Vector3d point2 = new Vector3d(8.25, 1.25, 0);
		assertTrue(point1.equalsWithEpsilon(segment1.getPointA(), 0.01)
				|| point1.equalsWithEpsilon(segment1.getPointB(), 0.01));
		assertTrue(point2.equalsWithEpsilon(segment1.getPointA(), 0.01)
				|| point2.equalsWithEpsilon(segment1.getPointB(), 0.01));
	}
	
	@Test
	public void testIntersectionNoPlanarIntersectionTouching() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(0, 0, 0);
		Vertex v4 = new Vertex(0, 10, 0);
		Vertex v5 = new Vertex(10, 10, 0);

		Polygon p2 = new ConcretePolygon();
		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.NONE, intersection.getType());
	}
	
	@Test
	public void testIntersectionNoPlanarIntersectionTouchingInOnePoint() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(0, 0, 0);
		Vertex v4 = new Vertex(0, 10, 0);
		Vertex v5 = new Vertex(9, 10, 0);

		Polygon p2 = new ConcretePolygon();
		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.NONE, intersection.getType());
	}
	
	@Test
	public void testIntersectionNoPlanarIntersectionNotTouching() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(0, 1, 0);
		Vertex v4 = new Vertex(0, 11, 0);
		Vertex v5 = new Vertex(10, 11, 0);

		Polygon p2 = new ConcretePolygon();
		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.NONE, intersection.getType());
	}
	
	@Test
	public void testIntersectionPlanarIntersection1() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 10, 10);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(5, 5, 5);
		Vertex v4 = new Vertex(5, 10, 10);
		Vertex v5 = new Vertex(8, 10, 10);
		Vertex v6 = new Vertex(8, 5, 5);

		Polygon p2 = new ConcretePolygon();
		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v6);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.POLYGON, intersection.getType());
		List<Vertex> vertices = intersection.getIntersectionPolygon().getExteriorRing().getVertices();
		assertEquals(new Vertex(5, 5, 5), vertices.get(0));
		assertEquals(new Vertex(8, 8, 7.999999999999999), vertices.get(1));
		assertEquals(new Vertex(8, 5, 4.999999999999999), vertices.get(2));
		assertEquals(new Vertex(5, 5, 5), vertices.get(3));
	}
	
	@Test
	public void testIntersectionPlanarIntersection2() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 0, 0);
		Vertex v2 = new Vertex(10, 0, 10);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(5, 0, 5);
		Vertex v4 = new Vertex(5, 0, 10);
		Vertex v5 = new Vertex(8, 0, 10);
		Vertex v6 = new Vertex(8, 0, 5);

		Polygon p2 = new ConcretePolygon();
		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v6);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.POLYGON, intersection.getType());
		List<Vertex> vertices = intersection.getIntersectionPolygon().getExteriorRing().getVertices();
		assertEquals(new Vertex(5, 0, 5), vertices.get(0));
		assertEquals(new Vertex(8, 0, 8), vertices.get(1));
		assertEquals(new Vertex(8, 0, 5), vertices.get(2));
		assertEquals(new Vertex(5, 0, 5), vertices.get(3));
	}
	
	@Test
	public void testIntersectionPlanarIntersection3() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(0, 10, 0);
		Vertex v2 = new Vertex(0, 10, 10);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(0, 5, 5);
		Vertex v4 = new Vertex(0, 5, 10);
		Vertex v5 = new Vertex(0, 8, 10);
		Vertex v6 = new Vertex(0, 8, 5);

		Polygon p2 = new ConcretePolygon();
		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v6);
		r2.addVertex(v3);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.POLYGON, intersection.getType());
		List<Vertex> polygon = intersection.getIntersectionPolygon().getExteriorRing().getVertices();
		assertEquals(new Vertex(0, 5, 5), polygon.get(0));
		assertEquals(new Vertex(0, 8, 8), polygon.get(1));
		assertEquals(new Vertex(0, 8, 5), polygon.get(2));
		assertEquals(new Vertex(0, 5, 5), polygon.get(3));
	}
	
	@Test
	public void testIntersectionPlanarIntersection4() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 10, 0);
		Vertex v2 = new Vertex(10, 0, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(5, 2, 0);
		Vertex v4 = new Vertex(5, 10, 0);
		Vertex v5 = new Vertex(8, 10, 0);
		Vertex v6 = new Vertex(8, 2, 0);

		Polygon p2 = new ConcretePolygon();
		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v6);
		r2.addVertex(v3);
		
		Vertex i0 = new Vertex(6, 3, 0);
		Vertex i1 = new Vertex(6, 4, 0);
		Vertex i2 = new Vertex(7, 4, 0);
		Vertex i3 = new Vertex(7, 3, 0);
		LinearRing r3 = new LinearRing(LinearRingType.INTERIOR);
		p1.addInteriorRing(r3);
		r3.addVertex(i0);
		r3.addVertex(i1);
		r3.addVertex(i2);
		r3.addVertex(i3);
		r3.addVertex(i0);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.POLYGON, intersection.getType());
		List<Vertex> ext = intersection.getIntersectionPolygon().getExteriorRing().getVertices();
		assertEquals(new Vertex(5, 5, 0), ext.get(0));
		assertEquals(new Vertex(8, 8, 0), ext.get(1));
		assertEquals(new Vertex(8, 2, 0), ext.get(2));
		assertEquals(new Vertex(5, 2, 0), ext.get(3));
		assertEquals(new Vertex(5, 5, 0), ext.get(4));
		
		List<Vertex> inner = intersection.getIntersectionPolygon().getInnerRings().get(0).getVertices();
		assertEquals(new Vertex(6, 3, 0), inner.get(0));
		assertEquals(new Vertex(7, 3, 0), inner.get(1));
		assertEquals(new Vertex(7, 4, 0), inner.get(2));
		assertEquals(new Vertex(6, 4, 0), inner.get(3));
		assertEquals(new Vertex(6, 3, 0), inner.get(4));
	}
	
	@Test
	public void testIntersectionPlanarIntersection5() {
		Vertex v0 = new Vertex(0, 0, 0);
		Vertex v1 = new Vertex(10, 10, 0);
		Vertex v2 = new Vertex(10, 0, 0);

		Polygon p1 = new ConcretePolygon();

		LinearRing r1 = new LinearRing(LinearRingType.EXTERIOR);
		p1.setExteriorRing(r1);
		r1.addVertex(v0);
		r1.addVertex(v1);
		r1.addVertex(v2);
		r1.addVertex(v0);

		Vertex v3 = new Vertex(5, 2, 0);
		Vertex v4 = new Vertex(5, 10, 0);
		Vertex v5 = new Vertex(8, 10, 0);
		Vertex v6 = new Vertex(8, 2, 0);

		Polygon p2 = new ConcretePolygon();
		LinearRing r2 = new LinearRing(LinearRingType.EXTERIOR);
		p2.setExteriorRing(r2);
		r2.addVertex(v3);
		r2.addVertex(v4);
		r2.addVertex(v5);
		r2.addVertex(v6);
		r2.addVertex(v3);
		
		Vertex i0 = new Vertex(6, 3, 0);
		Vertex i1 = new Vertex(6, 4, 0);
		Vertex i2 = new Vertex(7, 4, 0);
		Vertex i3 = new Vertex(7, 3, 0);
		LinearRing r3 = new LinearRing(LinearRingType.INTERIOR);
		p1.addInteriorRing(r3);
		r3.addVertex(i0);
		r3.addVertex(i1);
		r3.addVertex(i2);
		r3.addVertex(i3);
		r3.addVertex(i0);
		
		Vertex i5 = new Vertex(6, 6, 0);
		Vertex i6 = new Vertex(6, 7, 0);
		Vertex i7 = new Vertex(7, 7, 0);
		Vertex i8 = new Vertex(7, 6, 0);
		LinearRing r4 = new LinearRing(LinearRingType.INTERIOR);
		p2.addInteriorRing(r4);
		r4.addVertex(i5);
		r4.addVertex(i6);
		r4.addVertex(i7);
		r4.addVertex(i8);
		r4.addVertex(i5);

		PolygonIntersection intersection = SelfIntersectionUtil.polygonIntersection(p1, p2, 1, 0.0001);
		assertNotNull(intersection);
		assertEquals(IntersectionType.POLYGON, intersection.getType());
		List<Vertex> ext = intersection.getIntersectionPolygon().getExteriorRing().getVertices();
		assertEquals(new Vertex(5, 5, 0), ext.get(0));
		assertEquals(new Vertex(6, 6, 0), ext.get(1));
		assertEquals(new Vertex(7, 6, 0), ext.get(2));
		assertEquals(new Vertex(7, 7, 0), ext.get(3));
		assertEquals(new Vertex(8, 8, 0), ext.get(4));
		assertEquals(new Vertex(8, 2, 0), ext.get(5));
		assertEquals(new Vertex(5, 2, 0), ext.get(6));
		assertEquals(new Vertex(5, 5, 0), ext.get(7));
		
		List<Vertex> inner = intersection.getIntersectionPolygon().getInnerRings().get(0).getVertices();
		assertEquals(new Vertex(6, 3, 0), inner.get(0));
		assertEquals(new Vertex(7, 3, 0), inner.get(1));
		assertEquals(new Vertex(7, 4, 0), inner.get(2));
		assertEquals(new Vertex(6, 4, 0), inner.get(3));
		assertEquals(new Vertex(6, 3, 0), inner.get(4));
	}

	@Test
	public void testIsSimpleGood() {
		Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
		Polygon poly = new ConcretePolygon();
		geom.addPolygon(poly);

		LinearRing lr = new LinearRing(LinearRingType.EXTERIOR);
		poly.setExteriorRing(lr);

		Vertex v0 = new Vertex(0, 0, 0);
		lr.addVertex(v0);

		Vertex v1 = new Vertex(0, 10, 0);
		lr.addVertex(v1);

		Vertex v2 = new Vertex(0, 10, 10);
		lr.addVertex(v2);

		Vertex v3 = new Vertex(0, 0, 10);
		lr.addVertex(v3);
		lr.addVertex(v0);

		geom.updateEdgesAndVertices();

		RingSelfIntCheck check = new RingSelfIntCheck();
		check.init(Collections.emptyMap(), new ParserConfiguration(8, false));

		check.check(lr);
		assertEquals(ResultStatus.OK, lr.getCheckResult(check).getResultStatus());
	}

	@Test
	public void testIsSimpleFail() {
		Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
		Polygon poly = new ConcretePolygon();
		geom.addPolygon(poly);

		LinearRing lr = new LinearRing(LinearRingType.EXTERIOR);
		poly.setExteriorRing(lr);
		lr.setParent(poly);

		Vertex v0 = new Vertex(0, 0, 0);
		lr.addVertex(v0);

		Vertex v1 = new Vertex(0, 10, 10);
		lr.addVertex(v1);

		Vertex v2 = new Vertex(0, 10, 0);
		lr.addVertex(v2);

		Vertex v3 = new Vertex(0, 0, 10);
		lr.addVertex(v3);
		lr.addVertex(v0);
		geom.updateEdgesAndVertices();

		RingSelfIntCheck check = new RingSelfIntCheck();
		check.init(Collections.emptyMap(), new ParserConfiguration(8, false));
		check.check(lr);
		assertEquals(ResultStatus.ERROR, lr.getCheckResult(check).getResultStatus());
	}

	@Test
	public void testIsSimpleGood2() {
		Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
		Polygon poly = new ConcretePolygon();
		geom.addPolygon(poly);

		LinearRing lr = new LinearRing(LinearRingType.EXTERIOR);
		poly.setExteriorRing(lr);
		lr.setParent(poly);

		Vertex v0 = new Vertex(0, 2, 0);
		lr.addVertex(v0);

		Vertex v1 = new Vertex(0, 0, 0);
		lr.addVertex(v1);

		Vertex v2 = new Vertex(0, 0, 3);
		lr.addVertex(v2);
		lr.addVertex(v0);
		geom.updateEdgesAndVertices();

		RingSelfIntCheck check = new RingSelfIntCheck();
		check.init(Collections.emptyMap(), new ParserConfiguration(8, false));
		check.check(lr);
		assertEquals(ResultStatus.OK, lr.getCheckResult(check).getResultStatus());
	}

}
