diff --git a/CMakeLists.txt b/CMakeLists.txt
index 61c608d5922d975d1a97623526554c8b4efe1398..e6d36f7ec0e0506a19f3ad0219abab2cab9b3c71 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,8 +26,6 @@ add_executable(
 	src/debug_utilities/mostly_equal.cpp
 	tests/unit_tests/simple_triangle_tests.cpp
 	tests/unit_tests/point_in_triangle_tests.cpp
-	tests/unit_tests/triangulation_tests.cpp
-	tests/unit_tests/ear_removal_tests.cpp
 	src/utilities/intersections.cpp
 	tests/union_tests.cpp
 	src/shapes/triangle.cpp
@@ -37,13 +35,10 @@ add_executable(
 	src/utilities/orientation.cpp
 	src/shapes/point.cpp
 	src/union.cpp
-	src/utilities/triangulation.cpp
 	src/utilities/pointList.cpp
 	tests/unit_tests/orientation_tests.cpp
 	tests/unit_tests/intersections_tests.cpp
 	src/data_structures/box.cpp
-	src/data_structures/quad_tree.cpp
-	tests/quad_tree_tests.cpp
 	tests/edge_union_cases.cpp
 	tests/unit_tests/convex_triangulation_tests.cpp
 	src/utilities/convex_triangulation.cpp
@@ -53,6 +48,9 @@ add_executable(
 	src/utilities/split_triangle.cpp
 	tests/unit_tests/orientation_tests.cpp
 	tests/union_neighbours_tests.cpp
+	src/debug_utilities/shift_triangle.cpp
+	src/utilities/interpolate_z.cpp
+	tests/unit_tests/interpolate_z_tests.cpp
 )
 
 include_directories(include)
diff --git a/include/contourize.h b/include/contourize.h
index cb605f7fc51edc95bbcf6d8de759e0892e6bc471..4292e9846ff6ec20846bdea61a7babbe6e33aa76 100644
--- a/include/contourize.h
+++ b/include/contourize.h
@@ -1,9 +1,14 @@
 #pragma once
 #include <vector>
+#include <string>
+#include <exception>
 
 struct Triangle;
 struct Point;
 
+class ContourizeException : public std::exception {
+    virtual const char *what() const throw();
+};
 
 // creates a contour from a list of points 
 std::vector<Point> contourize(const std::vector<Point> &points);
\ No newline at end of file
diff --git a/include/convex_triangulation.h b/include/convex_triangulation.h
index 21c08ef86b4dc3876a9694ee5fe03d734fbcba74..e38679e92ad7c842349ccb5f44a97e56a77e99ac 100644
--- a/include/convex_triangulation.h
+++ b/include/convex_triangulation.h
@@ -5,4 +5,4 @@
 #include <vector>
 
 // Requires a convex shape
-std::vector<Triangle> convexTriangulation(const std::vector<Point> &points, int depth, int triangleId);
+std::vector<Triangle> convexTriangulation(const std::vector<Point> &points, int triangleId);
diff --git a/include/interpolate_z.h b/include/interpolate_z.h
new file mode 100644
index 0000000000000000000000000000000000000000..914b76360a112e65484de01c6f8f8b79057f466c
--- /dev/null
+++ b/include/interpolate_z.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <vector>
+struct Point;
+
+float interpolateZ(const std::vector<Point> &shape, const Point &p);
\ No newline at end of file
diff --git a/include/orientation.h b/include/orientation.h
index cc80bc60830f0b052307f3ab00ae8136f3a6e35c..dfaae1f11af02014e7ec11eba8fb55e7331f92cb 100644
--- a/include/orientation.h
+++ b/include/orientation.h
@@ -1,8 +1,10 @@
 #pragma once
-
+struct Triangle;
 struct Point;
 enum Orientation {
     Clockwise, Collinear, Counterclockwise
 };
 
-Orientation orientation(const Point &p1, const Point &p2, const Point &p3);
\ No newline at end of file
+Orientation orientation(const Point &p1, const Point &p2, const Point &p3);
+
+Orientation orientation(const Triangle &t);
\ No newline at end of file
diff --git a/include/point.h b/include/point.h
index 7b279aa7d3741126e51eb85defcdde4ac8bdf274..8376d863985304ed01983f306da142faa91a9f08 100644
--- a/include/point.h
+++ b/include/point.h
@@ -4,6 +4,9 @@
 struct Point {
 	float x, y, z;
 	bool operator==(const Point &other) const;
+	Point operator-(const Point &other) const;
+	Point operator+(const Point &other) const;
+	Point operator-() const;
 };
 
 std::ostream &operator<<(std::ostream &os, Point const &p);
diff --git a/include/shift_triangle.h b/include/shift_triangle.h
new file mode 100644
index 0000000000000000000000000000000000000000..dabd4cc409e0a11206e3f4fba1def6cab4b53b88
--- /dev/null
+++ b/include/shift_triangle.h
@@ -0,0 +1,4 @@
+#pragma once
+struct Triangle;
+
+void shiftZ(Triangle &t, float z) ;
\ No newline at end of file
diff --git a/include/triangle.h b/include/triangle.h
index 52fd2d8d90ffb0151d659a8a17c4d28f73a5d319..7240edf340d93f395bdfb63f001253ba36e76b2e 100644
--- a/include/triangle.h
+++ b/include/triangle.h
@@ -3,7 +3,7 @@
 #include "print_triangle.h"
 #include <vector>
 
-// points specified counterclockwise
+// points specified in counterclockwise or collinear (line / point) order
 struct Triangle
 {
     Point points[3];
diff --git a/include/triangulation.h b/include/triangulation.h
deleted file mode 100644
index 39153ae06589f144c57b1ef775dc6a9500436d81..0000000000000000000000000000000000000000
--- a/include/triangulation.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-#include <vector>
-#include <optional>
-#include <triangle.h>
-
-class PointList;
-struct Point;
-
-std::optional<Triangle> removeEar(int &index, PointList &pointList, const std::vector<Point> &allPoints, int depth, int id);
-
-std::vector<Triangle> triangulate(std::vector<Point> points, int depth, int id);
-
-std::vector<Triangle> triangulate(Triangle t1, Triangle t2);
\ No newline at end of file
diff --git a/src/data_structures/quad_tree.cpp b/src/data_structures/quad_tree.cpp
deleted file mode 100644
index c192453efd277444c6dc59769d2687bd9cf8556b..0000000000000000000000000000000000000000
--- a/src/data_structures/quad_tree.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-#include <quad_tree.h>
-#include <union.h>
-
-QuadTree::QuadTree(Box b) : b{b} {}
-
-int QuadTree::pointIntersection(Point p) const
-{
-    int triangleId = POINT_NOT_IN_QUADTREE;
-    if (!b.isIn(p))
-    {
-        return POINT_NOT_IN_QUADTREE;
-    }
-    if (triangles.size() > 0)
-    {
-        int depth = INT32_MAX;
-        for (const Triangle &t : triangles)
-        {
-            if (t.depth < depth && t.pointInTriangle(p))
-            {
-                depth = t.depth;
-                triangleId = t.id;
-            }
-        }
-        return triangleId;
-    }
-
-    for (const QuadTree &q : children)
-    {
-        int id = q.pointIntersection(p);
-        if (id != POINT_NOT_IN_QUADTREE)
-        {
-            triangleId = POINT_NOT_IN_QUADTREE;
-        }
-    }
-    return triangleId;
-}
-
-void QuadTree::addTriangle(Triangle triangle)
-{
-
-    if (!b.isIn(triangle))
-    {
-        return;
-    }
-
-    if (triangles.size() >= QUADTREE_NODE_MAX_SHAPE && children.empty())
-    {
-        children = {
-            QuadTree(Box{b.minX, b.maxX / 2, b.minY, b.maxY}),
-            QuadTree(Box{b.maxX / 2, b.maxX, b.minY, b.maxY / 2}),
-            QuadTree(Box{b.maxX / 2, b.maxX, b.maxY / 2, b.maxY}),
-            QuadTree(Box{b.minX, b.maxX / 2, b.minY, b.maxY / 2})};
-
-        for (QuadTree &c : children)
-        {
-
-            c.addTriangle(triangle);
-            for (const Triangle &t : triangles)
-            {
-                c.addTriangle(t);
-            }
-        }
-        triangles.clear();
-        return;
-    }
-
-    if (children.empty())
-    {
-        triangles.push_back(triangle);
-        return;
-    }
-
-    for (QuadTree &c : children)
-    {
-        for (const Triangle &t : triangles)
-        {
-            c.addTriangle(t);
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/debug_utilities/mostly_equal.cpp b/src/debug_utilities/mostly_equal.cpp
index 63f9d127e55bf7678af92aa54f3134d94cf11267..98585a319ad0223132163c32917357eea6c2f25c 100644
--- a/src/debug_utilities/mostly_equal.cpp
+++ b/src/debug_utilities/mostly_equal.cpp
@@ -17,6 +17,5 @@ bool Triangle::operator==(const Triangle &other) const {
 	return mostlyEqual(points[0], other.points[0]) &&
 		mostlyEqual(points[1], other.points[1]) &&
 		mostlyEqual(points[2], other.points[2]) &&
-		depth == other.depth &&
 		id == other.id;
 }
\ No newline at end of file
diff --git a/src/debug_utilities/print_triangles.cpp b/src/debug_utilities/print_triangles.cpp
index 4230de06b4169c1aae7e74d5b9b7b6323ae090dc..679c7f9465eb1f5cf359c4e44a1c04d1f94c278b 100644
--- a/src/debug_utilities/print_triangles.cpp
+++ b/src/debug_utilities/print_triangles.cpp
@@ -4,7 +4,6 @@
 
 std::ostream &operator<<(std::ostream &os, const Triangle &t) {
 	return os << "Triangle: " << t.id
-			<< ", " << "Depth: " << t.depth
 			<< " {" << t.points[0] << ", " << t.points[1] << ", " << t.points[2] << "}";
 }
 /*
diff --git a/src/debug_utilities/shift_triangle.cpp b/src/debug_utilities/shift_triangle.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9f76e8b41c7f994b55cbfd93212f29e1ddd780ea
--- /dev/null
+++ b/src/debug_utilities/shift_triangle.cpp
@@ -0,0 +1,9 @@
+#include <shift_triangle.h>
+#include <triangle.h>
+#include <constants.h>
+
+void shiftZ(Triangle &t, float z) {
+    for (int i =0; i < NB_TRIANGLE_SIDES; i++) {
+        t.points[i].z += z;
+    }
+}
\ No newline at end of file
diff --git a/src/shapes/point.cpp b/src/shapes/point.cpp
index ce13d15f7deeff30f05890bf93ecc0f58572f501..bd2de6a33344da954d471c4e7089f0e1c087ba6c 100644
--- a/src/shapes/point.cpp
+++ b/src/shapes/point.cpp
@@ -9,3 +9,17 @@ bool Point::operator==(const Point &other) const {
 std::ostream &operator<<(std::ostream &os, Point const &p) { 
     return os << "(" << p.x << "," << p.y << "," << p.z << ")";
 }
+
+Point Point::operator-() const {
+    return {-x, -y,-z};
+}
+
+
+Point Point::operator+(const Point &other) const {
+    return {x + other.x, y + other.y, z + other.z};
+}
+
+
+Point Point::operator-(const Point &other) const {
+    return *this + (- other);
+}
\ No newline at end of file
diff --git a/src/shapes/triangle.cpp b/src/shapes/triangle.cpp
index 97aff7afbe8ba2091bf14eac6d7d26e16d6c4f3a..53d346d3747a81af8c645045a48d3fdc55371fe1 100644
--- a/src/shapes/triangle.cpp
+++ b/src/shapes/triangle.cpp
@@ -2,7 +2,7 @@
 #include "triangle_edges.h"
 #include "edge.h"
 
-Triangle::Triangle(const Point &p1, const Point &p2, const Point &p3, int depth, int id, std::vector<int> neighbours) : points{p1,p2,p3}, depth{depth}, id{id}, neighbours{neighbours} {}
+Triangle::Triangle(const Point &p1, const Point &p2, const Point &p3, int id, std::vector<int> neighbours) : points{p1,p2,p3}, id{id}, neighbours{neighbours} {}
 
 bool Triangle::pointInTriangle(const Point &p) const {
     // all tests must be positive
diff --git a/src/union.cpp b/src/union.cpp
index 48d60ba8a40efb0aeff9aad8f967e9721bc6e903..523cf7a2efbc2a7fbd154972b7c31637280bba26 100644
--- a/src/union.cpp
+++ b/src/union.cpp
@@ -6,13 +6,12 @@
 #include <triangle_edges.h>
 #include <intersections.h>
 #include <split_triangle.h>
-#include <triangulation.h>
+#include <convex_triangulation.h>
 
 std::vector<Triangle> unionizeTopAndBottom(const Triangle &top, const Triangle &bot)
 {
     std::vector<Triangle> result;
 
-    std::list<Point> intr;
     TriangleEdges topEdges = TriangleEdges(top);
 
     // keep track of relevant triangles
@@ -21,13 +20,16 @@ std::vector<Triangle> unionizeTopAndBottom(const Triangle &top, const Triangle &
     for (int i = 0; i < NB_TRIANGLE_SIDES; i++)
     {
         const Edge &e = topEdges.edges[i];
+
+        // index 0: On the "outside" of the edge 
+        // index 1: On the "inside" of the edge
         auto shapes = splitShape(relv, e);
         // split triangle if exists
         // currently relevant triangles
         // add these to result
         if (!shapes[0].empty())
         {
-            std::vector<Triangle> relvTriangles = triangulate(shapes[0], bot.depth, bot.id);
+            std::vector<Triangle> relvTriangles = convexTriangulation(shapes[0], bot.id);
             result.insert(result.end(), relvTriangles.begin(), relvTriangles.end());
         }
         // future relevant triangles
@@ -47,7 +49,7 @@ std::vector<Triangle> unionize(const Triangle &t1, const Triangle &t2)
     {
         return {t1, t2};
     }
-    if (t1.depth < t2.depth)
+    if (t1.points[0].z < t2.points[0].z)
     {
         return unionizeTopAndBottom(t1, t2);
     }
diff --git a/src/utilities/contourize.cpp b/src/utilities/contourize.cpp
index 920b62a2ee3fe295bf277fa8b5f2d0c76928815a..9d5cfc3b27085924849f3b54e5adb49f36592096 100644
--- a/src/utilities/contourize.cpp
+++ b/src/utilities/contourize.cpp
@@ -3,12 +3,20 @@
 #include <orientation.h>
 #include <list>
 
+const char* ContourizeException::what() const throw() {
+    return "Infinite Loop detected";
+}
+
 bool isCounterClockwiseForAll(const Point &previous, const Point &candidate, const std::vector<Point> & points) {
     for (const auto &point : points) {
         if (previous == point || candidate == point) {
             continue;
         }
 	auto o = orientation(previous, candidate, point);
+        if (o == Collinear) {
+            continue;
+        }
+
         if (o != Counterclockwise) {
             return false;
         }
@@ -21,19 +29,34 @@ std::vector<Point> contourize(const std::vector<Point> &points) {
         return points;
     }
 
+
+
     std::vector<Point> result;
 
     Point previous = points[0];
     result.push_back(previous);
     std::list<Point> candidates(points.begin() + 1, points.end());
+    const int size = candidates.size();
+
+    // infinite loop detection
+    int seen = 0;
+
     while (!candidates.empty()) {
+        // detect infinite loop
+        if (seen >= candidates.size()) {
+            throw ContourizeException();
+        }
+  
+
         if (isCounterClockwiseForAll(previous, candidates.front(), points)) {
             previous = candidates.front();
             candidates.pop_front();
-	    result.push_back(previous);
+	        result.push_back(previous);
+            seen = 0;
         } else {
             candidates.push_back(candidates.front());
             candidates.pop_front();
+            seen++;
         }
     }
     return result;
diff --git a/src/utilities/convex_triangulation.cpp b/src/utilities/convex_triangulation.cpp
index c9ba15d41947163b4418f8c9955fa7bc084bfb61..5dd8ca969da7cbe75ec85c2cde12d2f9e2bae99e 100644
--- a/src/utilities/convex_triangulation.cpp
+++ b/src/utilities/convex_triangulation.cpp
@@ -1,18 +1,18 @@
 #include <convex_triangulation.h>
 
-std::vector<Triangle> convexTriangulation(const std::vector<Point> &points, int depth, int triangleId) {
+std::vector<Triangle> convexTriangulation(const std::vector<Point> &points, int triangleId) {
 	const int size = points.size();
 	if (size < 3) {
 		return std::vector<Triangle>{};
 	}
 
-	const Triangle t1 = Triangle(points[0], points[1], points[2], depth, triangleId);
+	const Triangle t1 = Triangle(points[0], points[1], points[2], triangleId);
 	std::vector<Triangle> results = {t1};
 
 	
 	Point first = points[0];
 	for (int i = size - 1; i >= 3; i--) {
-		results.push_back(Triangle(first, points[2], points[i], depth, triangleId));
+		results.push_back(Triangle(first, points[2], points[i], triangleId));
 		first = points[i];
 	}
 	return results;
diff --git a/src/utilities/interpolate_z.cpp b/src/utilities/interpolate_z.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b822762bba03688c42a7066b81371d781d26c088
--- /dev/null
+++ b/src/utilities/interpolate_z.cpp
@@ -0,0 +1,26 @@
+#include <interpolate_z.h>
+#include <limits>
+#include <point.h>
+#include <cmath>
+
+
+float interpolateZ(const std::vector<Point> &shape, const Point &p) {
+    // create direction vectors
+    const Point v1 = -(shape[0] - shape[1]);
+    const Point v2 = -(shape[0] - shape[2]);
+
+    const Point &origin = shape[0];
+
+    float t = (p.y * v1.x - origin.y * v1.x - p.x * v1.y + origin.x * v1.y) / (v1.x * v2.y - v1.y * v2.x);
+    float s = (p.y - origin.y - t * v2.y) / v1.y;
+
+    if (s == - std::numeric_limits<float>::infinity() || std::isnan(s)) {
+        s = 0;
+    }
+
+    if (t == - std::numeric_limits<float>::infinity() || std::isnan(t)) {
+       t = 0; 
+    }
+
+    return origin.z + v1.z * s + v2.z * t;
+}
diff --git a/src/utilities/intersections.cpp b/src/utilities/intersections.cpp
index 23551eece5277d679684672e77f93354a20f5c61..660443f5fa74f3d6813c4b203df43a431bbc9844 100644
--- a/src/utilities/intersections.cpp
+++ b/src/utilities/intersections.cpp
@@ -4,6 +4,7 @@
 #include <triangle.h>
 #include <constants.h>
 #include <edge.h>
+#include <interpolate_z.h>
 
 std::optional<float> getB(std::optional<float> slope, Point p)
 {
@@ -138,7 +139,6 @@ std::optional<Point> intersection(const Edge &e1, const Edge &e2)
     {
         return {};
     }
-
     return candPoint;
 }
 void intersections(const Edge &e1, const TriangleEdges &te, std::vector<Point> &results)
@@ -172,6 +172,9 @@ std::vector<Point> intersections(const std::vector<Point> &points, const Edge &l
         auto cand = intersectionFirstSide(line, es[i]);
         if (cand.has_value())
         {
+            // add depth information
+            float z = interpolateZ(points, cand.value());
+            cand.value().z = z;
             result.push_back(cand.value());
         }
     }
diff --git a/src/utilities/orientation.cpp b/src/utilities/orientation.cpp
index fcbfbf990d456ef93a66cd2173c6db9eec2001ee..02bc8f23ad9536aee2afade4a56e5232ce30707e 100644
--- a/src/utilities/orientation.cpp
+++ b/src/utilities/orientation.cpp
@@ -1,5 +1,6 @@
 #include "orientation.h"
 #include <point.h>
+#include <triangle.h>
 
 float edgeValue(const Point &p1, const Point &p2) {
     return (p2.x - p1.x) * (p2.y + p1.y);
@@ -12,4 +13,9 @@ Orientation orientation(const Point &p1, const Point &p2, const Point &p3) {
     }
 
     return (val > 0) ? Clockwise : Counterclockwise; 
+}
+
+
+Orientation orientation(const Triangle &t) {
+    return orientation(t.points[0], t.points[1], t.points[2]);
 }
\ No newline at end of file
diff --git a/src/utilities/triangulation.cpp b/src/utilities/triangulation.cpp
deleted file mode 100644
index 539517b418a57d1e22b424cad4acbd08845e95de..0000000000000000000000000000000000000000
--- a/src/utilities/triangulation.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "triangulation.h"
-#include <list>
-#include <point.h>
-#include <triangle.h>
-#include <optional>
-#include <pointList.h>
-#include <orientation.h>
-
-
-
-
-bool isAnEar(Triangle triangle, const std::vector<Point> &points) {
-    for (const Point &p: points) {
-        if (triangle.pointInTriangle(p)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-
-std::optional<Triangle> removeEar(int &index, PointList &pointList, const std::vector<Point> &allPoints,int depth, int id) {
-    PointNode &p = pointList.points[index];
-    const int next = p.next();
-    const int prev = p.prev();
-
-
-    const PointNode &nextNode = pointList.points[next];
-    const PointNode &prevNode = pointList.points[prev];
-
-    Triangle candidate = Triangle{p.p, nextNode.p, prevNode.p, depth, id}; 
-
-    if (orientation(p.p, nextNode.p, prevNode.p) == Counterclockwise && isAnEar(candidate, allPoints)) {
-
-        pointList.remove(index);
-        index = next;
-        
-        return candidate;
-    }
-    index = next;
-    return {};
-
-}
-
-
-
-std::vector<Triangle> triangulate(std::vector<Point> points, int depth, int id) {
-    std::vector<Triangle> result;
-
-    PointList polygon = PointList(points);
-    int i = 0;
-    while (polygon.getSize() >= 4) {
-
-        std::optional<Triangle> t = removeEar(i, polygon, points, depth, id);
-        if (t.has_value()) {
-            result.push_back(t.value());
-        }
-    }
-
-    const PointNode curr = polygon.points[i];
-    const int next = curr.next();
-    const int prev = curr.prev();
-    
-    Triangle last {curr.p, polygon.points[next].p, polygon.points[prev].p, depth, id};
-    result.push_back(last);
-    
-
-    return result;
-
-}
\ No newline at end of file
diff --git a/tests/edge_union_cases.cpp b/tests/edge_union_cases.cpp
index 30bf2e4797fb7610a29f23066029d7f3f9c1954b..c118d48c7608dc3fb8cafd51a9c75cf03f0e3498 100644
--- a/tests/edge_union_cases.cpp
+++ b/tests/edge_union_cases.cpp
@@ -1,21 +1,77 @@
 #include <gtest/gtest.h>
 #include <union.h>
 #include <triangle.h>
+#include <orientation.h>
 
-TEST(UnionEdgeTests, TriangleVertexOnEdge) {
-	Triangle t1 = Triangle({}, {}, {}, 1, 1);
-	Triangle t2 = Triangle({}, {}, {}, 2, 2);
+struct UnionParams
+{
+	Triangle t1;
+	Triangle t2;
+	std::vector<Triangle> expected;
+};
+
+std::ostream &operator<<(std::ostream &os, const UnionParams &u) {
+	os << u.t1 << "|" << u.t2;
+	return os;
+}
+
+class InstantiateUnionEdgeTests : public testing::TestWithParam<UnionParams>
+{
+};
+
+TEST_P(InstantiateUnionEdgeTests, UnionTest)
+{
+	const Triangle &t1 = GetParam().t1;
+	const Triangle &t2 = GetParam().t2;
+	auto expected = GetParam().expected;
+
+	ASSERT_NE(orientation(t1), Clockwise);
+	ASSERT_NE(orientation(t2), Clockwise);
 
 	auto result = unionize(t1, t2);
 
+	EXPECT_EQ(result, expected);
+}
+
+INSTANTIATE_TEST_SUITE_P(EdgeUnionTests, InstantiateUnionEdgeTests, testing::Values(
+																		// Point on top of triangle
+																		UnionParams{Triangle({0, 0, 4}, {5, 0, 4}, {2, 3, 4}, 1), Triangle({1, 1, 0}, {1, 1, 0}, {1, 1, 0}, 2), {Triangle({0, 0, 4}, {5, 0, 4}, {2, 3, 4}, 1), Triangle({1, 1, 0}, {1, 1, 0}, {1, 1, 0}, 2)}},
+																		// Point on edge of triangle
+																		UnionParams{Triangle({0, 0, 6}, {5, 0, 6}, {2, 3, 6}, 1), Triangle({0, 2, 3}, {0, 2, 3}, {0, 2, 3}, 2), {Triangle({0, 0, 6}, {5, 0, 6}, {2, 3, 6}, 1), Triangle({0, 2, 3}, {0, 2, 3}, {0, 2, 3}, 2)}},
+																		// Point on vertex of triangle
+																		UnionParams{Triangle({0, 0, 2}, {5,0,2}, {2,3,2}, 1), Triangle({0,0,0}, {0,0,0}, {0,0,0}, 2), {Triangle({0, 0, 2}, {5,0,2}, {2,3,2}, 1), Triangle({0,0,0}, {0,0,0}, {0,0,0}, 2)}}
+																		));
+
+TEST(UnionEdgeTests, TriangleVertexOnEdge)
+{
+	Triangle t1 = Triangle({}, {}, {}, 1);
+	Triangle t2 = Triangle({}, {}, {}, 2);
+
+	auto result = unionize(t1, t2);
 }
 
-TEST(UnionEdgeTests, TriangleEdgeLength0) {
-	Triangle t1 = Triangle({1,1}, {2,2}, {3,3}, 1, 1);
-	Triangle t2 = Triangle({}, {}, {}, 2, 2);
+TEST(UnionEdgeTests, TriangleIsPoint)
+{
+	Triangle t1 = Triangle({1, 1, 2}, {2, 2, 2}, {3, 3, 2}, 1);
+	Triangle t2 = Triangle({2, 2, 1}, {2, 2, 1}, {2, 2, 1}, 2);
+
+	auto results = unionize(t1, t2);
+
+	std::vector<Triangle> expected = {
+		Triangle({1, 1, 2}, {2, 2, 2}, {3, 3, 2}, 1),
+		Triangle({2, 2, 1}, {2, 2, 1}, {2, 2, 1}, 2)};
+
+	EXPECT_EQ(results, expected);
 }
 
-TEST(UnionEdgeTests, TriangleOnSide) {
-	Triangle t1 = Triangle({}, {}, {}, 1, 1);
-	Triangle t2 = Triangle({}, {}, {}, 2, 2);
+TEST(UnionEdgeTests, TriangleOnSide)
+{
+	Triangle t1 = Triangle({0, 0, 1}, {0, 5, 1}, {2, 3, 1}, 1);
+	Triangle t2 = Triangle({0, 1}, {1, 1}, {0, 3}, 2);
+
+	auto results = unionize(t1, t2);
+
+	std::vector<Triangle> expected;
+
+	EXPECT_EQ(results, expected);
 }
diff --git a/tests/quad_tree_tests.cpp b/tests/quad_tree_tests.cpp
deleted file mode 100644
index cbe8689d49ba2bb1ef1ad1958da316e0335d98e0..0000000000000000000000000000000000000000
--- a/tests/quad_tree_tests.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-#include <gtest/gtest.h>
-#include <quad_tree.h>
-
-/*
-TEST (QuadTreeTest, OverlappingTriangles) {
-
-    QuadTree q{Box{0, 10, 0, 10}};
-    auto t1 = Triangle(Point{0,0}, Point{5,0},  Point{2,3}, 1);
-    auto t2 = Triangle(Point{0,0}, Point{5,0},  Point{2,3}, 2);
-    auto t3 = Triangle(Point{0,0}, Point{5,0},  Point{2,3}, 3);
-    auto t4 = Triangle(Point{0,0}, Point{5,0},  Point{2,3}, 4);
-    auto t5 = Triangle(Point{0,0}, Point{5,0},  Point{2,3}, 5);
-
-    q.addTriangle(t1);
-    q.addTriangle(t2);
-    q.addTriangle(t3);
-    q.addTriangle(t4);
-    q.addTriangle(t5);
-    EXPECT_EQ(q.pointIntersection(Point{1,1}), 1);
-
-}
-*/
-TEST (QuadTreeTest, SingleInsertion) {
-    QuadTree q{Box{0, 10, 0, 10}};
-
-    auto t1 = Triangle(Point{0,0}, Point{5,0},  Point{2,3}, 0);
-
-    auto t2 = Triangle(Point{3,1}, Point{6,1}, Point{4,3}, 0);
-    
-
-    q.addTriangle(t1);
-    q.addTriangle(t2);
-
-    EXPECT_TRUE(true);
-}
-
-TEST (QuadTreeTest, PointOnTriangleQueryTest) {
-    QuadTree q{Box{0, 10, 0, 10}};
-
-    auto t1 = Triangle(Point{0,0}, Point{5,0},  Point{2,3}, 1, 1);
-
-    auto t2 = Triangle(Point{3,1}, Point{6,1}, Point{4,3}, 2, 2);
-    
-
-    q.addTriangle(t1);
-    q.addTriangle(t2);
-
-    EXPECT_EQ(q.pointIntersection(Point{1,1}), 1);
-    EXPECT_EQ(q.pointIntersection(Point{5,1.5}), 2);
-    EXPECT_EQ(q.pointIntersection(Point{3.5, 1.5}), 1);
-    EXPECT_EQ(q.pointIntersection(Point{-1,-1}), POINT_NOT_IN_QUADTREE);
-
-}
diff --git a/tests/union_neighbours_tests.cpp b/tests/union_neighbours_tests.cpp
index 0896426acafb485fb59d7f8e7c705a1674758721..e595e35579717451bbde9c15e192bfcd32afc26c 100644
--- a/tests/union_neighbours_tests.cpp
+++ b/tests/union_neighbours_tests.cpp
@@ -3,8 +3,8 @@
 #include <union.h>
 
 TEST (UnionNeighbourTests, NeighbourTest) {
-	Triangle t1 = Triangle({}, {}, {}, 1, 1, {2});
-	Triangle t2 = Triangle({}, {}, {}, 2, 2, {1});
+	Triangle t1 = Triangle({}, {}, {}, 1, {2});
+	Triangle t2 = Triangle({}, {}, {}, 2, {1});
 
 	auto results = unionize(t1, t2);
 	std::vector<Triangle> expected ={t1, t2} ;
diff --git a/tests/union_tests.cpp b/tests/union_tests.cpp
index ace3d5bce477a0b2881fc1467bf43a61d9d9debf..900335aa49fbf6a27940db7d9f12c05c5c88ae77 100644
--- a/tests/union_tests.cpp
+++ b/tests/union_tests.cpp
@@ -2,6 +2,7 @@
 #include <union.h>
 #include <triangle.h>
 #include <orientation.h>
+#include <shift_triangle.h>
 
 TEST(UnionTrivialTests, NotTouching)
 {
@@ -16,27 +17,27 @@ TEST(UnionTrivialTests, NotTouching)
 TEST(UnionTests, TwoIntersections)
 {
 
-    auto t1 = Triangle(Point{0, 0}, Point{5, 0}, Point{2, 3}, 0, 1);
-    auto t2 = Triangle(Point{3, 1}, Point{6, 1}, Point{4, 3}, 1, 2);
+    auto t1 = Triangle(Point{0, 0, 0}, Point{5, 0, 0}, Point{2, 3, 0}, 1);
+    auto t2 = Triangle(Point{3, 1, 1}, Point{6, 1, 1}, Point{4, 3, 1}, 2);
 
     auto ts = unionize(t1, t2);
 
     EXPECT_TRUE(ts.size() == 3);
 
     std::vector<Triangle> expected{
-	    Triangle({6,1}, {4,3}, {4,1}, 1, 2),
-		    Triangle({4,3}, {3.33333,1.66667}, {4,1}, 1,2),
-    Triangle({0,0}, {5,0}, {2,3}, 0, 1)};
+	    Triangle({6,1,1}, {4,3,1}, {3.333333,1.66666,1}, 2),
+		    Triangle({4,3,1}, {3.33333,1.66667,1}, {4,1,1},2),
+    Triangle({0,0}, {5,0}, {2,3}, 1)};
     EXPECT_EQ(ts, expected);
 }
 
 // Edge Case
-/*
+
 TEST(UnionTests, SharesPointTest)
 {
     // Case 1 t1 covers t2
-    Triangle bottom = Triangle({0, 5}, {3, 2}, {5, 3}, 1, 1);
-    Triangle top = Triangle({0.1, 7}, {5, 3}, {3.2, 9}, 2, 2);
+    Triangle top = Triangle({0, 5}, {3, 2}, {5, 3}, 1);
+    Triangle bottom = Triangle({0.1, 7, 5}, {5, 3,5}, {3.2, 9,5}, 2);
 
     auto results = unionize(bottom, top);
     std::vector<Triangle> expected_results_1;
@@ -44,41 +45,41 @@ TEST(UnionTests, SharesPointTest)
     EXPECT_EQ(results, expected_results_1);
 
     // Case 2 t2 covers t1
-    bottom.depth = 3;
+    shiftZ(top, 10);
 
     results = unionize(bottom, top);
 
     std::vector<Triangle> expected_results_2;
 
     EXPECT_EQ(results, expected_results_2);
-}*/
+}
 
 TEST(UnionTests, FoldTriangleTest)
 {
 
-    Triangle top = Triangle({0, 5}, {3, 2}, {5, 3}, 1, 1);
-    Triangle bottom = Triangle({0.1, 9}, {2, 4}, {5,5}, 2, 2);
+    Triangle top = Triangle({0, 5}, {3, 2}, {5, 3}, 1);
+    Triangle bottom = Triangle({0.1, 9,1}, {2, 4,1}, {5,5,1}, 2);
 
     auto results = unionize(bottom, top);
 
     std::vector<Triangle> expected_results_1 = {
-        Triangle({0.1, 9}, {1.91038, 4.23585}, {5,5}, 2, 2),
-        Triangle({1.91038,4.23585}, {2.27273,4.09091}, {5, 5}, 2, 2),
-        Triangle({0,5}, {3,2}, {5,3}, 1, 1)
+        Triangle({0.1, 9,1}, {1.91038, 4.23585,1}, {5,5}, 2),
+        Triangle({1.91038,4.23585,1}, {2.27273,4.09091,1}, {5, 5}, 2),
+        Triangle({0,5}, {3,2}, {5,3}, 1)
     };
 
     EXPECT_EQ(results, expected_results_1);
 
-    top.depth = 3;
+    shiftZ(top, 3);
 
     results = unionize(bottom, top);
 
     std::vector<Triangle> expected_results_2 = {
-        Triangle({0,5}, {2.6129, 2.3871}, {1.91038, 4.23585}, 3, 1),
-        Triangle({3,2}, {5, 3}, {2.6129, 2.3871}, 3, 1),
-        Triangle({5,3}, {2.27273, 4.09091}, {2.6129, 2.3871}, 3, 1),
-        Triangle({2.27273,4.09091}, {2, 4}, {2.6129, 2.3871}, 3, 1),
-        Triangle({0.1,9}, {2, 4}, {5, 5}, 2, 2)
+        Triangle({0,5}, {2.6129, 2.3871}, {1.91038, 4.23585}, 1),
+        Triangle({3,2}, {5, 3}, {2.6129, 2.3871}, 1),
+        Triangle({5,3}, {2.27273, 4.09091}, {2.6129, 2.3871}, 1),
+        Triangle({2.27273,4.09091}, {2, 4}, {2.6129, 2.3871}, 1),
+        Triangle({0.1,9}, {2, 4}, {5, 5}, 2)
     };
 
     EXPECT_EQ(results, expected_results_2);
@@ -106,8 +107,8 @@ TEST(UnionTests, IceCreamTest)
 
 TEST(UnionTests, StarTest)
 {
-    Triangle bottom = Triangle({0, 3}, {5, 3}, {2.5, 6}, 1, 1);
-    Triangle top = Triangle({0, 5}, {2, 0}, {5, 5}, 2, 2);
+    Triangle bottom = Triangle({0, 3}, {5, 3}, {2.5, 6}, 1);
+    Triangle top = Triangle({0, 5, 1}, {2, 0, 1}, {5, 5, 1}, 2);
 
     ASSERT_EQ(orientation(bottom.points[0], bottom.points[1], bottom.points[2]), Counterclockwise);
     ASSERT_EQ(orientation(top.points[0], top.points[1], top.points[2]), Counterclockwise);
@@ -115,24 +116,28 @@ TEST(UnionTests, StarTest)
     auto results = unionize(bottom, top);
 
     std::vector<Triangle> expected_results_1 = {
-        Triangle({2,0}, {3.8, 3}, {0.8, 3}, 2, 2),
-        Triangle({5,5}, {3.33333, 5}, {4.30233, 3.83721}, 2, 2),
-        Triangle({0,5}, {0.540541, 3.64865}, {1.66667, 5}, 2, 2),
-        Triangle({0, 3}, {5, 3}, {2.5, 6}, 1, 1)
+        Triangle({2,0}, {3.8, 3}, {0.8, 3}, 2),
+        Triangle({5,5}, {3.33333, 5}, {4.30233, 3.83721}, 2),
+        Triangle({0,5}, {0.540541, 3.64865}, {1.66667, 5}, 2),
+        Triangle({0, 3}, {5, 3}, {2.5, 6}, 1)
     };
 
     EXPECT_EQ(results, expected_results_1);
 
-    bottom.depth = 3;
+    shiftZ(bottom, 3);
 
     results = unionize(bottom, top);
 
     std::vector<Triangle> expected_results_2 = {
-        Triangle({0,3}, {0.8,3}, {0.540541,3.64865}, 3, 1),
-        Triangle({5,3}, {4.30233,3.83721}, {3.8,3}, 3, 1),
-        Triangle({2.5,6}, {1.66667,5}, {3.33333,5}, 3, 1),
-        Triangle({0,5}, {2,0}, {5,5}, 2, 2)
+        Triangle({0,3}, {0.8,3}, {0.540541,3.64865}, 1),
+        Triangle({5,3}, {4.30233,3.83721}, {3.8,3}, 1),
+        Triangle({2.5,6}, {1.66667,5}, {3.33333,5}, 1),
+        Triangle({0,5}, {2,0}, {5,5}, 2)
     };
 
     EXPECT_EQ(results, expected_results_2);
 }
+
+TEST (UnionTests, DepthTest) {
+
+}
\ No newline at end of file
diff --git a/tests/unit_tests/contourize_tests.cpp b/tests/unit_tests/contourize_tests.cpp
index c4c9d10b7a2052a40edaacb996be0b8851c4d74d..80f0f6a3451a4d1fbf8f93f2c931dd3ebedb0930 100644
--- a/tests/unit_tests/contourize_tests.cpp
+++ b/tests/unit_tests/contourize_tests.cpp
@@ -4,11 +4,11 @@
 #include <intersections.h>
 
 TEST(ContourizeTest, Simple) {
-	std::vector<Point> points{{6,1}, {4,3}, {4, 1}, {3.3333, 1.6666}};
+	std::vector<Point> points{{0,0}, {0,5}, {2, 3}};
 
 	auto result = contourize(points);
 
-	std::vector<Point> expected{{4,1}, {3.3333,1.6666}, {4,3}, {6,1}};
+	std::vector<Point> expected{{0,0}, {0,5}, {2, 3}};
 	EXPECT_EQ(result, expected);
 }
 
diff --git a/tests/unit_tests/convex_triangulation_tests.cpp b/tests/unit_tests/convex_triangulation_tests.cpp
index 21c77eb55fb784707ce63a65565e58855ed19223..5fa45e1408ca2fc77b8998f2c95c142c12d639c2 100644
--- a/tests/unit_tests/convex_triangulation_tests.cpp
+++ b/tests/unit_tests/convex_triangulation_tests.cpp
@@ -14,12 +14,12 @@ TEST(ConvexTriangulationTests, Simple) {
 		p1, p2, p3, p4, p5
 	};	
 
-	auto results = convexTriangulation(points, 2, 2);
+	auto results = convexTriangulation(points, 2);
 
 	std::vector<Triangle> expected_result {
-		Triangle(p1, p2, p3, 2, 2),
-			Triangle(p1, p3, p5, 2, 2),
-			Triangle(p5, p3, p4, 2, 2)
+		Triangle(p1, p2, p3, 2),
+			Triangle(p1, p3, p5, 2),
+			Triangle(p5, p3, p4, 2)
 	};
 	
 	EXPECT_EQ(results, expected_result);
@@ -33,8 +33,8 @@ TEST(ConvexTriangulationTests, TrivialTriangle) {
 		{1.5, 2}
 	};
 
-	auto results = convexTriangulation(points, 1, 1);
-	Triangle expected_triangle = Triangle({0,0}, {2,0}, {1.5, 2}, 1, 1);
+	auto results = convexTriangulation(points, 1);
+	Triangle expected_triangle = Triangle({0,0}, {2,0}, {1.5, 2}, 1);
 
 	EXPECT_EQ(results.size(), 1);
 	EXPECT_EQ(results[0], expected_triangle);
diff --git a/tests/unit_tests/ear_removal_tests.cpp b/tests/unit_tests/ear_removal_tests.cpp
deleted file mode 100644
index ed567b806805a0873de178ce6ebb4ffd986dddcf..0000000000000000000000000000000000000000
--- a/tests/unit_tests/ear_removal_tests.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <gtest/gtest.h>
-#include <triangulation.h>
-#include <vector>
-#include <point.h>
-#include <pointList.h>
-
-TEST(EarRemovalTests, SingleEarRemoval) {
-    std::vector<Point> polygon = {
-        {3,48}, {52, 8}, {99,50}, {138,25},
-         {175, 77}, {131, 72}, {111, 113},
-          {72, 43}, {26, 55}, {29, 100}};
-
-    PointList pointList = PointList(polygon);
-    int index = 0;
-    auto t = removeEar(index, pointList, polygon, 0,0);
-    EXPECT_FALSE(t.has_value());
-    EXPECT_TRUE(true);
-
-    auto t2 = removeEar(index, pointList, polygon,0,0);
-
-    auto t3 = removeEar(index, pointList, polygon,0,0);
-}
\ No newline at end of file
diff --git a/tests/unit_tests/interpolate_z_tests.cpp b/tests/unit_tests/interpolate_z_tests.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c1fe2aab53d2ada07b4a156c3190cf21bfd4570d
--- /dev/null
+++ b/tests/unit_tests/interpolate_z_tests.cpp
@@ -0,0 +1,26 @@
+#include <gtest/gtest.h>
+#include <interpolate_z.h>
+#include <point.h>
+
+struct InterpolateParams {
+    std::vector<Point> points;
+    float expected_z;
+};
+
+class InstantiateInterpolateZTests : public testing::TestWithParam<InterpolateParams>{
+};
+
+TEST_P (InstantiateInterpolateZTests, Interpolate) {
+    
+	std::vector<Point> points = GetParam().points;
+
+    float z = interpolateZ(points, {0,1,0});
+
+    EXPECT_EQ(z,GetParam().expected_z);
+}
+
+INSTANTIATE_TEST_SUITE_P(Interpolation, InstantiateInterpolateZTests, testing::Values(
+    InterpolateParams{
+        {{1,1,3}, {2,2,3}, {4,5,3}}
+        , 3},
+    InterpolateParams{{{1,1,5}, {1,1,5}, {1,1,5}}, 5}));
\ No newline at end of file
diff --git a/tests/unit_tests/orientation_tests.cpp b/tests/unit_tests/orientation_tests.cpp
index 8f91929dd9ff534138b152e193780950248cdab7..ec9d68d47baa7d7309cfc1801c6c17b5f8c10a90 100644
--- a/tests/unit_tests/orientation_tests.cpp
+++ b/tests/unit_tests/orientation_tests.cpp
@@ -12,6 +12,10 @@ TEST(OrientationTests, ExampleCounterClockwiseTest)
     EXPECT_EQ(orientation(Point{0, 0}, Point{4, 4}, Point{1, 2}), Counterclockwise);
 }
 
+TEST (OrientationTests, Counterclockwise) {
+    EXPECT_EQ(orientation({0,0}, {5,0}, {2,3}), Counterclockwise);
+}
+
 TEST(OrientationTests, TestAll)
 {
     Point p1 = {3, 1};
diff --git a/tests/unit_tests/split_triangle_tests.cpp b/tests/unit_tests/split_triangle_tests.cpp
index 8185284beb5429381b7f2cf6583a80cbec1f1648..65c1eb373bb9bbfc2af8d95f082bc994245374f1 100644
--- a/tests/unit_tests/split_triangle_tests.cpp
+++ b/tests/unit_tests/split_triangle_tests.cpp
@@ -4,7 +4,7 @@
 #include <edge.h>
 
 TEST(SplitTriangleTests, NormalCase) {
-    Triangle t = Triangle({0,0}, {5,0}, {2,3}, 1, 1);
+    Triangle t = Triangle({0,0}, {5,0}, {2,3}, 1);
     Edge e{{4,3}, {3,1}};
 
     auto results = splitShape(std::vector<Point>{t.points[0], t.points[1], t.points[2]}, e);
@@ -46,4 +46,24 @@ TEST (SplitTriangleTests, Quadric) {
     auto results = splitShape(p,e);
 
     EXPECT_TRUE(results[0].size() > 2);
+}
+
+TEST(SplitTriangleTests, OnEdge1) {
+    std::vector<Point> t {{0,0}, {0,5}, {2,3}};
+    Edge e = Edge{{1,1}, {0,1}};
+
+    auto results = splitShape(t, e);
+
+    EXPECT_EQ(results[0].size(), 4);
+    EXPECT_EQ(results[1].size(), 3);
+}
+
+TEST(SplitTriangleTests, OnEdge2) {
+
+    std::vector<Point> t {{0,0}, {0,5}, {2,3}};
+    Edge e {{1,1}, {0,3}};
+
+    auto results = splitShape(t,e);
+
+    EXPECT_EQ(results[0].size(), 4);
 }
\ No newline at end of file
diff --git a/tests/unit_tests/triangulation_tests.cpp b/tests/unit_tests/triangulation_tests.cpp
deleted file mode 100644
index 65493fb912f756777ea950fe8e0e4d82795c0211..0000000000000000000000000000000000000000
--- a/tests/unit_tests/triangulation_tests.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "gtest/gtest.h"
-#include <point.h>
-#include <triangulation.h>
-
-TEST(TriangulationTests, Page3Example) {
-    std::vector<Point> polygon = {
-        {3,48}, {52, 8}, {99,50}, {138,25},
-         {175, 77}, {131, 72}, {111, 113},
-          {72, 43}, {26, 55}, {29, 100}};
-
-    auto t = triangulate(polygon, 0, 0);    
-    EXPECT_TRUE(t.size() > 0);
-}
\ No newline at end of file