/*-
 *  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.math;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

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

	/**
	 * Simple square
	 */
	@Test
	public void testIntersection1() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(1, 0, 0);
		Vector3d v2 = new Vector3d(1, 1, 0);
		Vector3d v3 = new Vector3d(0, 1, 0);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v0, v2, v3);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * triangle in x-z plane bordering triangle in y-z plane
	 */
	@Test
	public void testIntersection2() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(1, 0, 0);
		Vector3d v2 = new Vector3d(0, 0, 1);
		Vector3d v3 = new Vector3d(0, 1, 0);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v0, v2, v3);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * triangle in x-z plane intersecting with triangle in y-z plane in one point
	 */
	@Test
	public void testIntersection3() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(1, 0, 0);
		Vector3d v2 = new Vector3d(0, 0, 1);
		Vector3d v3 = new Vector3d(0, 1, 0);
		Vector3d v4 = new Vector3d(0, 1, 1);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v0, v4, v3);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * triangle in x-z plane bordering triangle in y-z plane
	 */
	@Test
	public void testIntersection4() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(1, 0, 0);
		Vector3d v2 = new Vector3d(0, 0, 1);
		Vector3d v3 = new Vector3d(0, 1, 0);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v0, v2, v3);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * triangle in x-z plane intersecting with triangle in y-z plane in one corner point
	 */
	@Test
	public void testIntersection5() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(1, 0, 0);
		Vector3d v2 = new Vector3d(0, 0, 1);
		Vector3d v3 = new Vector3d(-0.5, 1, 0);
		Vector3d v4 = new Vector3d(-0.5, 1, 1);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v0, v4, v3);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * triangle in x-z plane not intersecting with triangle in y-z plane
	 */
	@Test
	public void testIntersection6() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(1, 0, 0);
		Vector3d v2 = new Vector3d(0, 0, 1);
		Vector3d v3 = new Vector3d(-0.5, 1, 0);
		Vector3d v4 = new Vector3d(-0.5, 1, 1);
		Vector3d v5 = new Vector3d(-0.5, -1, 0);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v5, v4, v3);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * triangle in x-z plane intersecting with other triangle
	 */
	@Test
	public void testIntersection7() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(1, 0, 0);
		Vector3d v2 = new Vector3d(0, 0, 1);
		Vector3d v3 = new Vector3d(-0.5, 1, 0);
		Vector3d v4 = new Vector3d(-0.5, 1, 1);
		Vector3d v5 = new Vector3d(1, -1, 0);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v5, v4, v3);
		
		assertTrue(t1.doesIntersect(t2));
		assertTrue(t2.doesIntersect(t1));
	}
	
	/**
	 * triangle in x-z plane not intersecting with triangle in y-z plane
	 */
	@Test
	public void testIntersection8() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(1, 0, 0);
		Vector3d v2 = new Vector3d(0, 0, 1);
		Vector3d v3 = new Vector3d(0.5, 1, 0);
		Vector3d v4 = new Vector3d(0.5, 1, 1);
		Vector3d v5 = new Vector3d(1, 1, 0);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v5, v4, v3);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * two triangles bordering a third, but not sharing an exact edge
	 */
	@Test
	public void testIntersection9() {
		Vector3d v0 = new Vector3d(1, 1, 0);
		Vector3d v1 = new Vector3d(2, 0, 0);
		Vector3d v2 = new Vector3d(3, 1, 0);
		Vector3d v3 = new Vector3d(1.5, 2, 0);
		Vector3d v4 = new Vector3d(2, 1, 0);
		Vector3d v5 = new Vector3d(2.5, 2, 0);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v0, v4, v3);
		Triangle3d t3 = new Triangle3d(v4, v2, v5);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t1.doesIntersect(t3));
		assertFalse(t2.doesIntersect(t3));
		assertFalse(t2.doesIntersect(t1));
		assertFalse(t2.doesIntersect(t1));
		assertFalse(t3.doesIntersect(t2));
	}
	
	/**
	 * two triangles bordering a third, but not sharing an exact edge, one intersecting into the third
	 */
	@Test
	public void testIntersection10() {
		Vector3d v0 = new Vector3d(1, 1, 0);
		Vector3d v1 = new Vector3d(2, 0, 0);
		Vector3d v2 = new Vector3d(3, 1, 0);
		Vector3d v3 = new Vector3d(1.5, 2, 0);
		Vector3d v4 = new Vector3d(2, 1, 0);
		Vector3d v5 = new Vector3d(2.5, 2, 0);
		Vector3d v6 = new Vector3d(3, 0.5, 0);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v0, v4, v3);
		Triangle3d t3 = new Triangle3d(v4, v6, v5);
		
		assertFalse(t1.doesIntersect(t2));
		assertTrue(t1.doesIntersect(t3));
		assertFalse(t2.doesIntersect(t3));
		assertFalse(t2.doesIntersect(t1));
		assertTrue(t3.doesIntersect(t1));
		assertFalse(t3.doesIntersect(t2));
	}
	
	/**
	 * two triangles intersecting
	 */
	@Test
	public void testIntersection11() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(2, 0, 0);
		Vector3d v2 = new Vector3d(0, 0, 4);
		Vector3d v3 = new Vector3d(-1, 2, 2);
		Vector3d v4 = new Vector3d(-1, -2, 2);
		Vector3d v5 = new Vector3d(1.5, 2, 2);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v3, v4, v5);
		
		assertTrue(t1.doesIntersect(t2));
		assertTrue(t2.doesIntersect(t1));
	}
	
	/**
	 * two triangles not intersecting
	 */
	@Test
	public void testIntersection12() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(2, 0, 0);
		Vector3d v2 = new Vector3d(0, 0, 4);
		Vector3d v3 = new Vector3d(-10, 2, 2);
		Vector3d v4 = new Vector3d(-10, -2, 2);
		Vector3d v5 = new Vector3d(-7.5, 2, 2);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v3, v4, v5);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * two triangles, one completely in another
	 */
	@Test
	public void testIntersection13() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(3, 0, 0);
		Vector3d v2 = new Vector3d(0, 3, 0);
		Vector3d v3 = new Vector3d(0.5, 0.5, 0);
		Vector3d v4 = new Vector3d(1.5, 0.5, 0);
		Vector3d v5 = new Vector3d(0.5, 1.5, 0);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v3, v4, v5);
		
		assertTrue(t1.doesIntersect(t2));
		assertTrue(t2.doesIntersect(t1));
	}
	
	/**
	 * one triangle intersecting with itself
	 */
	@Test
	public void testIntersection14() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(3, 0, 0);
		Vector3d v2 = new Vector3d(0, 3, 0);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		
		assertTrue(t1.doesIntersect(t1));
	}
	
	/**
	 * two triangles intersecting
	 */
	@Test
	public void testIntersection15() {
		Vector3d v0 = new Vector3d(0, 0, 0);
		Vector3d v1 = new Vector3d(30, 0, 0);
		Vector3d v2 = new Vector3d(0, 30, 0);
		
		Vector3d v3 = new Vector3d(1, 1, 2);
		Vector3d v4 = new Vector3d(1, 2, 2);
		Vector3d v5 = new Vector3d(1, 1, -2);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v3, v4, v5);
		
		assertTrue(t1.doesIntersect(t2));
		assertTrue(t2.doesIntersect(t1));
	}
	
	/**
	 * two triangles not intersecting, forming square in z-y plane
	 */
	@Test
	public void testIntersection16() {
		Vector3d v0 = new Vector3d(0, 0, 10);
		Vector3d v1 = new Vector3d(0, 0, 0);
		Vector3d v2 = new Vector3d(0, 10, 0);
		
		Vector3d v3 = new Vector3d(0, 0, 10);
		Vector3d v4 = new Vector3d(0, 10, 0);
		Vector3d v5 = new Vector3d(0, 10, 10);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v3, v4, v5);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * two triangles not intersecting
	 */
	@Test
	public void testIntersection17() {
		Vector3d v0 = new Vector3d(550200.6852498532, 5804577.182086236, 53.93);
		Vector3d v1 = new Vector3d(550200.7309999987, 5804577.647, 76.3842995075415);
		Vector3d v2 = new Vector3d(550200.7309999987, 5804577.647, 53.93);
		
		Vector3d v3 = new Vector3d(550200.7309999987, 5804577.647, 76.3842995075415);
		Vector3d v4 = new Vector3d(550208.1420000009, 5804576.824, 53.93);
		Vector3d v5 = new Vector3d(550206.5491363483, 5804577.000889325, 53.93);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v3, v4, v5);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * two triangles not intersecting
	 */
	@Test
	public void testIntersection18() {
		Vector3d v0 = new Vector3d(550149.932, 5804610.759, 54.03);
		Vector3d v1 = new Vector3d(550149.9542761615, 5804610.920099844, 54.03);
		Vector3d v2 = new Vector3d(550158.8960000016, 5804609.528, 54.03);
		
		Vector3d v3 = new Vector3d(550149.9542761615, 5804610.920099844, 54.03);
		Vector3d v4 = new Vector3d(550150.4580000006, 5804614.563, 54.03);
		Vector3d v5 = new Vector3d(550158.8960000016, 5804609.528, 54.03);
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v3, v4, v5);
		
		assertFalse(t1.doesIntersect(t2));
		assertFalse(t2.doesIntersect(t1));
	}
	
	/**
	 * two triangles not intersecting
	 */
	@Test
	public void testIntersection19() {
		Vector3d v0 = new Vector3d(16.18180670822862, 4.826301792879059, 9.101142199244872);
		Vector3d v1 = new Vector3d(14.894806708275654, 12.727301792484178, 9.101142199244872);
		Vector3d v2 = new Vector3d(10.549806708245386, 6.8063017923947715, 13.101142199244872);
		
		Vector3d v3 = new Vector3d(17.296806708277515, 5.008301792908862, 9.101142199244872);
		Vector3d v4 = new Vector3d(15.228806708249111, 4.671301792618289, 9.101142199244872);
		Vector3d v5 = new Vector3d(15.228806708249111, 4.671301792618289, 0.6911421992448763);
		
		double xMin = Double.MAX_VALUE;
		double yMin = Double.MAX_VALUE;
		double zMin = Double.MAX_VALUE;
		xMin = Math.min(xMin, v0.getX());
		xMin = Math.min(xMin, v1.getX());
		xMin = Math.min(xMin, v2.getX());
		xMin = Math.min(xMin, v3.getX());
		xMin = Math.min(xMin, v4.getX());
		xMin = Math.min(xMin, v5.getX());
		
		yMin = Math.min(yMin, v0.getY());
		yMin = Math.min(yMin, v1.getY());
		yMin = Math.min(yMin, v2.getY());
		yMin = Math.min(yMin, v3.getY());
		yMin = Math.min(yMin, v4.getY());
		yMin = Math.min(yMin, v5.getY());
		
		zMin = Math.min(zMin, v0.getZ());
		zMin = Math.min(zMin, v1.getZ());
		zMin = Math.min(zMin, v2.getZ());
		zMin = Math.min(zMin, v3.getZ());
		zMin = Math.min(zMin, v4.getZ());
		zMin = Math.min(zMin, v5.getZ());
		
		v0.setX(v0.getX() - xMin);
		v0.setY(v0.getY() - yMin);
		v0.setZ(v0.getZ() - zMin);
		
		v1.setX(v1.getX() - xMin);
		v1.setY(v1.getY() - yMin);
		v1.setZ(v1.getZ() - zMin);
		
		v2.setX(v2.getX() - xMin);
		v2.setY(v2.getY() - yMin);
		v2.setZ(v2.getZ() - zMin);
		
		v3.setX(v3.getX() - xMin);
		v3.setY(v3.getY() - yMin);
		v3.setZ(v3.getZ() - zMin);
		
		v4.setX(v4.getX() - xMin);
		v4.setY(v4.getY() - yMin);
		v4.setZ(v4.getZ() - zMin);
		
		v5.setX(v5.getX() - xMin);
		v5.setY(v5.getY() - yMin);
		v5.setZ(v5.getZ() - zMin);
		
		
		Triangle3d t1 = new Triangle3d(v0, v1, v2);
		Triangle3d t2 = new Triangle3d(v3, v4, v5);
		
//		System.out.println(String.format("dreieck(%.4f|%.4f|%.4f %.4f|%.4f|%.4f %.4f|%.4f|%.4f)", v0.getX(), v0.getY(), v0.getZ(), v1.getX(), v1.getY(), v1.getZ(), v2.getX(), v2.getY(), v2.getZ()));
//		System.out.println(String.format("dreieck(%.4f|%.4f|%.4f %.4f|%.4f|%.4f %.4f|%.4f|%.4f)", v3.getX(), v3.getY(), v3.getZ(), v4.getX(), v4.getY(), v4.getZ(), v5.getX(), v5.getY(), v5.getZ()));
		
		assertFalse(t2.doesIntersect(t1));
		assertFalse(t1.doesIntersect(t2));
	}

}
