Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • shlomist/unionized-triangles
  • blaicheo/unionized-triangles-2
2 results
Show changes
Showing
with 500 additions and 220 deletions
......@@ -2,20 +2,47 @@
#include <triangle.h>
#include <constants.h>
bool Box::isIn(const Triangle &t) const {
Box::Box(Point topLeft, Point bottomRight) :
topLeft{topLeft},
bottomRight{bottomRight},
points{bottomRight, {topLeft.x, bottomRight.y}, topLeft, {bottomRight.x, topLeft.y}},
edges{{points}} {}
bool Box::intersects(const Triangle &t) const {
for (int i = 0; i < NB_TRIANGLE_SIDES; i++) {
float x = t.points[i].x;
float y = t.points[i].y;
if (x > minX && x < maxX && y > minY && y < maxY) {
if (intersects({x,y})) {
return true;
}
}
return false;
}
bool Box::isIn(const Point &p) const {
if (p.x > minX && p.x < maxX && p.y > minY && p.y < maxY) {
return true;
for (int i = 0; i < BOX_NB_POINTS; i++) {
if (t.pointInTriangle(points[i])) {
return true;
}
}
return false;
}
bool Box::intersects(const Point &p) const {
return p.x <= topLeft.x && p.y <= topLeft.y && bottomRight.x <= p.x && bottomRight.y <= p.y;
}
Box Box::firstQuadrant() const {
return Box({topLeft.x / 2, topLeft.y}, {bottomRight.x, topLeft.y / 2});
}
Box Box::secondQuadrant() const {
return Box(topLeft, {topLeft.x / 2, topLeft.y / 2});
}
Box Box::thirdQuadrant() const {
return Box({topLeft.x, topLeft.y / 2}, {topLeft.x/2,bottomRight.y});
}
Box Box::fourthQuadrant() const {
return Box({topLeft.x / 2, topLeft.y / 2}, bottomRight);
}
\ No newline at end of file
#include <box_edges.h>
#include <box.h>
BoxEdges::BoxEdges(const Point p [size]) {
for (int i = 0; i < size; i++) {
e[i] = {p[i], p[(i + 1) % size]};
}
}
\ No newline at end of file
#include <quad_tree.h>
#include <union.h>
#include <iterator>
#include <set>
QuadTree::QuadTree(Box b) : b{b} {}
QuadTree::QuadTree(Box b) : b{b}, level{0} {}
int QuadTree::pointIntersection(Point p) const
QuadTree::QuadTree(Box b, int level = 0) : b{b}, level{level} {}
void QuadTree::collectUniqueTriangleFragments(const Triangle &t, std::set<int> &seen, std::vector<Triangle> &result) const {
if (seen.count(t.fragmentId) > 0) {
return;
}
result.push_back(t);
seen.insert(t.fragmentId);
}
std::vector<Triangle> QuadTree::visibleSurface() const {
std::set<int> seen;
return visibleSurface(seen);
}
std::vector<Triangle> QuadTree::visibleSurface(std::set<int> & seen) const
{
int triangleId = POINT_NOT_IN_QUADTREE;
if (!b.isIn(p))
{
return POINT_NOT_IN_QUADTREE;
std::vector<Triangle> result;
for (const Triangle &t : triangles) {
collectUniqueTriangleFragments(t, seen, result);
}
if (triangles.size() > 0)
for (const QuadTree &c : children)
{
int depth = INT32_MAX;
for (const Triangle &t : triangles)
{
if (t.depth < depth && t.pointInTriangle(p))
{
depth = t.depth;
triangleId = t.id;
}
}
return triangleId;
std::vector<Triangle> childResult = c.visibleSurface(seen);
result.insert(result.end(), childResult.begin(), childResult.end());
}
for (const QuadTree &q : children)
{
int id = q.pointIntersection(p);
if (id != POINT_NOT_IN_QUADTREE)
{
triangleId = POINT_NOT_IN_QUADTREE;
}
}
return triangleId;
return result;
}
void QuadTree::addTriangle(Triangle triangle)
void QuadTree::split()
{
const int nextLevel = level + 1;
children = {
QuadTree(b.firstQuadrant(), nextLevel),
QuadTree(b.secondQuadrant(), nextLevel),
QuadTree(b.thirdQuadrant(), nextLevel),
QuadTree(b.fourthQuadrant(), nextLevel)};
}
if (!b.isIn(triangle))
void QuadTree::addTriangle(const Triangle &triangle)
{
if (!b.intersects(triangle))
{
return;
}
if (triangles.size() >= QUADTREE_NODE_MAX_SHAPE && children.empty())
if (triangles.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})};
triangles.push_back(triangle);
return;
}
for (QuadTree &c : children)
if (!children.empty())
{
for (QuadTree &child : children)
{
child.addTriangle(triangle);
}
return;
}
c.addTriangle(triangle);
for (const Triangle &t : triangles)
std::vector<Triangle> currentTriangleFragments{triangle};
// triangles that are not the current triangle and that have already been unioned
std::vector<Triangle> otherTriangleFragments;
for (const auto &otherTriangle : triangles)
{
for (const auto &fragment : currentTriangleFragments)
{
auto newTriangles = unionize(triangle, otherTriangle);
std::vector<Triangle> bottoms;
if (newTriangles.size() > 1)
{
c.addTriangle(t);
bottoms = {newTriangles.begin(), newTriangles.begin() + newTriangles.size() - 2};
}
const Triangle &top = newTriangles.back();
otherTriangleFragments.push_back(top);
if (top.mainTriangleId == triangle.mainTriangleId)
{
otherTriangleFragments.insert(otherTriangleFragments.begin(), bottoms.begin(), bottoms.end());
}
else
{
currentTriangleFragments.insert(currentTriangleFragments.begin(), bottoms.begin(), bottoms.end());
}
}
triangles.clear();
return;
}
if (children.empty())
{
triangles.push_back(triangle);
// split
if (level >= QUADTREE_MAX_DEPTH) {
triangles.insert(triangles.end(), currentTriangleFragments.begin(), currentTriangleFragments.end());
triangles.insert(triangles.end(), otherTriangleFragments.begin(), otherTriangleFragments.end());
return;
}
split();
for (QuadTree &q : children)
{
for (const Triangle &t : otherTriangleFragments)
{
q.addNonIntersectingTriangle(t);
}
for (const Triangle &t : currentTriangleFragments)
{
q.addNonIntersectingTriangle(t);
}
}
triangles.clear();
}
for (QuadTree &c : children)
int QuadTree::pointIntersection(const Point &p) const
{
if (b.intersects(p))
{
for (const Triangle &t : triangles)
for (const Triangle &triangle : triangles)
{
c.addTriangle(t);
if (triangle.pointInTriangle(p))
{
return triangle.mainTriangleId;
}
}
for (const QuadTree &child : children)
{
int id = child.pointIntersection(p);
if (id != POINT_NOT_IN_QUADTREE)
{
return id;
}
}
}
return POINT_NOT_IN_QUADTREE;
}
void QuadTree::addNonIntersectingTriangle(const Triangle &t)
{
if (b.intersects(t))
{
triangles.push_back(t);
}
}
\ No newline at end of file
#include <stdlib.h>
#include <point.h>
#include <triangle.h>
#define DEBUG_EPSILON 0.0001
bool mostlyEqual(float a, float b)
{
return abs(a - b) < DEBUG_EPSILON;
}
bool mostlyEqual(const Point &a, const Point &b)
{
return mostlyEqual(a.x, b.x) && mostlyEqual(a.y, b.y) && mostlyEqual(a.z, b.z);
}
// mostly equal
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]) &&
mainTriangleId == other.mainTriangleId;
}
\ No newline at end of file
......@@ -3,9 +3,7 @@
#include <point.h>
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] << "}";
return os << "Triangle" << "(" << t.points[0] << ", " << t.points[1] << ", " << t.points[2] << ", " << t.mainTriangleId << ")";
}
/*
std::ostream &operator<<(std::ostream &os, const std::vector<Triangle> &triangles) {
......
#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
......@@ -4,4 +4,19 @@ bool Edge::positiveSide(const Point &p) const {
float h = p1.y - p2.y;
float g = p1.x - p2.x;
return -h * (p.x - p1.x) + g * (p.y - p1.y) >= 0;
}
// Output: produces a list of edges in counterclockwise order
// Requirements: points are provided in counterclockwise order
std::vector<Edge> makeEdges(const std::vector<Point> &points) {
std::vector<Edge> result;
if (points.size() < 2) {
return result;
}
for (int i = 0; i < points.size() - 1; i++) {
result.push_back(Edge{points[i], points[i + 1]});
}
result.push_back(Edge{points[points.size() - 1], points[0]});
return result;
}
\ No newline at end of file
#include "point.h"
bool mostlyEqual(float a, float b) {
return abs(a - b) < 0.0001;
}
bool Point::operator==(const Point &other) const {
return (x == other.x) && (y == other.y);
return (x == other.x) && (y == other.y) && (z == other.z);
}
std::ostream &operator<<(std::ostream &os, Point const &p) {
return os << "(" << p.x << "," << p.y << ")";
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);
}
#include "triangle.h"
#include "triangle_edges.h"
#include "edge.h"
#include <triangle_fragment_id_assigner.h>
bool Triangle::operator==(const Triangle &other) const {
return points[0] == other.points[0] &&
points[1] == other.points[1] &&
points[2] == other.points[2] &&
depth == other.depth &&
id == other.id;
}
bool Triangle::neighbours(const Triangle &other) const {
return false;
}
Triangle::Triangle(const Point &p1, const Point &p2, const Point &p3, int mainTriangleId, std::shared_ptr<std::vector<int>> neighbours = {}) :
points{p1, p2, p3}, mainTriangleId{mainTriangleId}, fragmentId{TriangleFragmentIdAssigner::getInstance().generateUniqueId()}, neighbours{neighbours} {}
Triangle::Triangle(const Point &p1, const Point &p2, const Point &p3, int depth, int id) : points{p1,p2,p3}, depth{depth}, id{id} {}
Triangle::Triangle(const Point &p1, const Point &p2, const Point &p3, int mainTriangleId, const std::vector<int> neighbours) :
points{p1, p2, p3}, mainTriangleId{mainTriangleId}, fragmentId{TriangleFragmentIdAssigner::getInstance().generateUniqueId()}, neighbours{make_shared<std::vector<int>>(neighbours)} {}
bool Triangle::pointInTriangle(const Point &p) const {
bool Triangle::pointInTriangle(const Point &p) const
{
// all tests must be positive
auto edges = TriangleEdges(*this);
for (int i = 0; i < NB_TRIANGLE_SIDES; i++) {
if (edges.edges[i].positiveSide(p)) {
for (int i = 0; i < NB_TRIANGLE_SIDES; i++)
{
if (edges.edges[i].positiveSide(p))
{
return false;
}
}
return true;
}
int nextPoint(int pointIndex) {
int nextPoint(int pointIndex)
{
return (pointIndex + 1) % 3;
}
......@@ -6,13 +6,18 @@
#include <triangle_edges.h>
#include <intersections.h>
#include <split_triangle.h>
#include <triangulation.h>
#include <convex_triangulation.h>
#include <orientation.h>
std::vector<Triangle> unionizeTopAndBottom(const Triangle &top, const Triangle &bot)
{
// degenerate line / point case
if (orientation(top) == Collinear) {
return {bot, top};
}
std::vector<Triangle> result;
std::list<Point> intr;
TriangleEdges topEdges = TriangleEdges(top);
// keep track of relevant triangles
......@@ -21,13 +26,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];
auto shapes = splitTriangle(Triangle(relv[0], relv[1], relv[2], bot.depth, bot.id), e);
// 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]);
std::vector<Triangle> relvTriangles = convexTriangulation(shapes[0], bot.mainTriangleId, bot.neighbours);
result.insert(result.end(), relvTriangles.begin(), relvTriangles.end());
}
// future relevant triangles
......@@ -40,13 +48,16 @@ std::vector<Triangle> unionizeTopAndBottom(const Triangle &top, const Triangle &
std::vector<Triangle> unionize(const Triangle &t1, const Triangle &t2)
{
if (intersections(t1, t2).empty())
{
if (t1.mainTriangleId == t2.mainTriangleId) {
return {t1, t2};
}
if (t1.depth < t2.depth)
if (std::find(t1.neighbours->begin(), t1.neighbours->end(), t2.mainTriangleId) != t1.neighbours->end()) {
return {t1, t2};
}
if (t1.points[0].z < t2.points[0].z)
{
return unionizeTopAndBottom(t1, t2);
}
return unionizeTopAndBottom(t2, t1);
}
\ No newline at end of file
}
#include <vector>
#include <triangle.h>
std::vector<Triangle> completeTriangles() {
//
}
\ No newline at end of file
......@@ -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,20 +29,37 @@ 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());
while (!candidates.empty()) {
const int size = candidates.size();
// infinite loop detection
int seen = 0;
while (candidates.size() > 1) {
// 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++;
}
}
result.push_back(candidates.front());
return result;
}
#include <convex_triangulation.h>
#include <orientation.h>
std::vector<Triangle> convexTriangulation(const std::vector<Point> &points, int depth, int triangleId) {
// filters out degenerate triangles
void appendResults(std::vector<Triangle> &results, const Triangle &t) {
if (orientation(t) == Collinear) {
return;
}
results.push_back(t);
}
std::vector<Triangle> convexTriangulation(const std::vector<Point> &points, int triangleId, std::shared_ptr<std::vector<int>> neighbours) {
const int size = points.size();
if (size < 3) {
return std::vector<Triangle>{};
}
const Triangle t1 = Triangle(points[0], points[1], points[2], depth, triangleId);
std::vector<Triangle> results = {t1};
const Triangle t1 = Triangle(points[0], points[1], points[2], triangleId, neighbours);
std::vector<Triangle> results;
appendResults(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));
const Triangle t = Triangle(first, points[2], points[i], triangleId, neighbours);
appendResults(results, t);
first = points[i];
}
return results;
}
#include <interpolate_z.h>
#include <limits>
#include <point.h>
#include <edge.h>
#include <cmath>
float dist2D(const Point &p) {
return sqrt(p.x * p.x + p.y * p.y);
}
float interpolateZ(const Edge &e, const Point &p) {
const float p1Dist = dist2D(e.p1);
const float p2Dist = dist2D(e.p2);
const float pDist = dist2D(p);
if (p1Dist - p2Dist == 0) {
return e.p1.z;
}
const float m = (e.p1.z - e.p2.z) / (p1Dist - p2Dist);
const float b = e.p1.z - p1Dist * m;
return m * pDist + b;
}
......@@ -2,8 +2,9 @@
#include <optional>
#include <triangle_edges.h>
#include <triangle.h>
#include <constants.h>
#include <edge.h>
#include <interpolate_z.h>
std::optional<float> getB(std::optional<float> slope, Point p)
{
......@@ -26,11 +27,11 @@ std::optional<float> getSlope(Edge e)
bool withinEdge(Edge e, Point p)
{
float minX = std::min(e.p1.x, e.p2.x);
float maxX = std::max(e.p1.x, e.p2.x);
float minX = std::min(e.p1.x, e.p2.x) - EPSILON;
float maxX = std::max(e.p1.x, e.p2.x) + EPSILON;
float minY = std::min(e.p1.y, e.p2.y);
float maxY = std::max(e.p1.y, e.p2.y);
float minY = std::min(e.p1.y, e.p2.y) - EPSILON;
float maxY = std::max(e.p1.y, e.p2.y) + EPSILON;
return minX <= p.x && p.x <= maxX && minY <= p.y && p.y <= maxY;
}
......@@ -56,6 +57,18 @@ std::optional<Point> intersectionWithinEdge(const Edge &e1, const Edge &e2)
return {};
}
// intersection with first edge being considered infinite in length
std::optional<Point> intersectionFirstSide(const Edge &e1, const Edge &e2) {
auto candPoint = intersection(e1, e2);
if (candPoint.has_value() && withinEdge(e2, candPoint.value())) {
float z = interpolateZ(e2, candPoint.value());
candPoint.value().z = z;
return candPoint;
}
return {};
}
// returns intersection with e1 being extended infinitly in its direction
std::optional<Point> intersectionWithinEdgeDirection(const Edge &e1, const Edge &e2)
{
......@@ -128,7 +141,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)
......@@ -153,17 +165,17 @@ std::vector<Point> intersections(const Triangle &t1, const Triangle &t2)
return results;
}
std::vector<Point> intersections(const Triangle t, const Edge &line)
std::vector<Point> intersections(const std::vector<Point> &points, const Edge &line)
{
std::vector<Point> result;
TriangleEdges es{t};
for (int i = 0; i < NB_TRIANGLE_SIDES; i++)
std::vector<Edge> es = makeEdges(points);
for (int i = 0; i < es.size(); i++)
{
auto cand = intersectionWithinEdge(es.edges[i], line);
auto cand = intersectionFirstSide(line, es[i]);
if (cand.has_value())
{
result.push_back(cand.value());
}
}
return result;
}
\ No newline at end of file
}
#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
......@@ -5,22 +5,22 @@
#include <edge.h>
#include <intersections.h>
std::vector<std::vector<Point>> splitTriangle(const Triangle &t, const Edge &line)
std::vector<std::vector<Point>> splitShape(const std::vector<Point> &points, const Edge &line)
{
// get intersections
auto intr = intersections(t, line);
auto intr = intersections(points, line);
std::vector<Point> pos;
std::vector<Point> neg;
for (int i = 0; i < NB_TRIANGLE_SIDES; i++)
for (int i = 0; i < points.size(); i++)
{
if (line.positiveSide(t.points[i]))
if (line.positiveSide(points[i]))
{
pos.push_back(t.points[i]);
pos.push_back(points[i]);
}
else
{
neg.push_back(t.points[i]);
neg.push_back(points[i]);
}
}
......
#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) {
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, 0};
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) {
std::vector<Triangle> result;
PointList polygon = PointList(points);
int i = 0;
while (polygon.getSize() >= 4) {
std::optional<Triangle> t = removeEar(i, polygon, points);
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, 0};
result.push_back(last);
return result;
}
\ No newline at end of file
#include <gtest/gtest.h>
#include <union.h>
#include <triangle.h>
#include <orientation.h>
#include "union_tests.h"
TEST(UnionEdgeTests, TriangleVertexOnEdge) {
Triangle t1 = Triangle({}, {}, {}, 1, 1);
Triangle t2 = Triangle({}, {}, {}, 2, 2);
INSTANTIATE_TEST_SUITE_P(EdgeUnionTests, InstantiateUnionTests, 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)}},
// Line and Triangle
UnionParams{Triangle({0,0,5}, {5,0,5}, {2,3,5}, 1), Triangle({0,0,0}, {1,1,0}, {6,6,0}, 2), {Triangle({0,0,5}, {5,0,5}, {2,3,5}, 1), Triangle({0,0,0}, {1,1,0}, {6,6,0}, 2)}}
));
TEST(UnionEdgeTests, TriangleVertexOnEdge)
{
Triangle t1 = Triangle({}, {}, {}, 1);
Triangle t2 = Triangle({}, {}, {}, 2);
auto result = unionize(t1, t2);
}
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, TriangleEdgeLength0) {
Triangle t1 = Triangle({1,1}, {2,2}, {3,3}, 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 = {
Triangle({0,0,1}, {0.666667,1,1}, {0,1,1}, 1),
Triangle({0,5,1}, {0,3,1}, {0.857143,1.28571,1}, 1),
Triangle({0,5,1}, {0.857143,1.28571,1}, {2,3,1}, 1),
Triangle({0,1,0}, {1,1,0}, {0,3,0}, 2)
};
EXPECT_EQ(results, expected);
}
#include <gtest/gtest.h>
#include <quad_tree.h>
/*
TEST (QuadTreeTest, OverlappingTriangles) {
TEST(QuadTreeTests, SimplePointQueryTest) {
QuadTree q({{10,10}, {0,0}});
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);
Triangle t({0,0}, {5,0}, {2,3}, 42);
q.addTriangle(t);
Point p{1,1};
int id = q.pointIntersection(p);
EXPECT_EQ(id, t.mainTriangleId);
}
TEST (QuadTreeTests, NotInQueryPointTest) {
QuadTree q({{10,10}, {0,0}});
Triangle t({0,0}, {5,0}, {2,3});
Point p{9,9};
int id = q.pointIntersection(p);
EXPECT_EQ(id, POINT_NOT_IN_QUADTREE);
}
TEST (QuadTreeTests, OverlapTest) {
QuadTree q({{10,10}, {0,0}});
Triangle t1({0, 0, 0}, {5, 0, 0}, {2, 3, 0}, 1);
Triangle t2({3, 1, 1}, {6, 1, 1}, {4, 3, 1}, 2);
q.addTriangle(t1);
q.addTriangle(t2);
q.addTriangle(t3);
q.addTriangle(t4);
q.addTriangle(t5);
EXPECT_EQ(q.pointIntersection(Point{1,1}), 1);
Point p{1,1};
int id = q.pointIntersection(p);
EXPECT_EQ(id, t1.mainTriangleId);
}
TEST (QuadTreeTests, NonOverlappingTest) {
QuadTree q({{10,10}, {0,0}});
Triangle t1({0, 0, 0}, {5, 0, 0}, {2, 3, 0}, 1);
Triangle t2{{6, 0, 1}, {10, 0, 1}, {7, 5, 1}, 2};
q.addTriangle(t1);
q.addTriangle(t2);
}
*/
TEST (QuadTreeTest, SingleInsertion) {
QuadTree q{Box{0, 10, 0, 10}};
auto t1 = Triangle(Point{0,0}, Point{5,0}, Point{2,3}, 0);
TEST (QuadTreeTests, VisibleSurfaceTestNonOverlapping) {
auto t2 = Triangle(Point{3,1}, Point{6,1}, Point{4,3}, 0);
QuadTree q({{10,10}, {0,0}});
Triangle t1({0, 0, 0}, {5, 0, 0}, {2, 3, 0}, 1);
Triangle t2{{6, 0, 1}, {10, 0, 1}, {7, 5, 1}, 2};
q.addTriangle(t1);
q.addTriangle(t2);
EXPECT_TRUE(true);
auto surface = q.visibleSurface();
std::vector<Triangle> expected_surface {{{6,0,1}, {10, 0, 1}, {7, 5, 1}, 2}, {{0,0,0}, {5,0,0},{2,3,0}, 1}};
EXPECT_EQ(surface, expected_surface);
}
TEST (QuadTreeTest, PointOnTriangleQueryTest) {
QuadTree q{Box{0, 10, 0, 10}};
TEST (QuadTreeTests, NoVisibleSurfaceTest) {
QuadTree q({{1,1}, {0,0}});
auto surface = q.visibleSurface();
EXPECT_EQ(surface, std::vector<Triangle>{});
}
auto t1 = Triangle(Point{0,0}, Point{5,0}, Point{2,3}, 1, 1);
TEST (QuadTreeTests, FiveTrianglesTest) {
auto t2 = Triangle(Point{3,1}, Point{6,1}, Point{4,3}, 2, 2);
QuadTree q({{1,1}, {0,0}});
q.addTriangle(t1);
Triangle t1{{0,0,0}, {0,0,0}, {0,0,0}, 1};
Triangle t2{{0,0,0}, {0,0,0}, {0,0,0}, 1};
Triangle t3{{0,0,0}, {0,0,0}, {0,0,0}, 1};
Triangle t4{{0,0,0}, {0,0,0}, {0,0,0}, 1};
}
TEST (QuadTreeTests, DegenerateTrianglesTest) {
QuadTree q({{1,1}, {0,0}});
Triangle t1{{0,0,0}, {0,0,0}, {0,0,0}, 1};
Triangle t2{{0.2, 0.1, 1}, {0.2, 0.1, 1}, {0.3, 0.2, 1}, 2};
Triangle t3{{1,0,0}, {1,0,0}, {0,0,0}, 3};
q.addTriangle(t3);
q.addTriangle(t2);
q.addTriangle(t1);
auto surface = q.visibleSurface();
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);
std::vector<Triangle> expected_surface {t1, t3, t2};
EXPECT_EQ(surface, expected_surface);
}
TEST (QuadTreeTests, StackingPointsTest) {
}
TEST (QuadTreeTests, TriangleCompletelyCoversOthers) {
QuadTree q({{1,1}, {0,0}});
Triangle t1{{0,0.1,10}, {0.234, 0.3, 9}, {0.1, 0.5, 9}, 1};
Triangle cover{{-100,-100,0}, {100,-1000,0}, {25, 50,0}, 100};
q.addTriangle(t1);
q.addTriangle(cover);
std::vector<Triangle> expected {cover};
EXPECT_EQ(q.visibleSurface(), expected);
}
\ No newline at end of file