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
Commits on Source (19)
Showing
with 333 additions and 109 deletions
build
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/settings.json
\ No newline at end of file
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(ctest) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${cmake.testProgram}",
"args": ["${cmake.testArgs}"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}
\ No newline at end of file
......@@ -23,13 +23,20 @@ project(intersection)
add_executable(
tests
tests/unit_tests/split_triangle_tests.cpp
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
tests/unit_tests/orientation_tests.cpp
tests/unit_tests/contourize_tests.cpp
tests/unit_tests/intersection_tests/intersections_tests.cpp
tests/edge_union_cases.cpp
tests/unit_tests/orientation_tests.cpp
tests/union_neighbours_tests.cpp
tests/quad_tree_tests.cpp
tests/unit_tests/interpolate_z_tests.cpp
tests/unit_tests/convex_triangulation_tests.cpp
src/debug_utilities/mostly_equal.cpp
src/utilities/intersections.cpp
src/shapes/triangle.cpp
src/shapes/triangle_edges.cpp
src/utilities/contourize.cpp
......@@ -37,22 +44,15 @@ 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
tests/unit_tests/edge_direction_intr_tests.cpp
tests/unit_tests/contourize_tests.cpp
src/debug_utilities/print_triangles.cpp
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
src/data_structures/quad_tree.cpp
src/data_structures/box_edges.cpp
)
include_directories(include)
......
Provides a datastructure for storing triangles based on how far they are from a light source.
Provides a function to perform a union on 2 triangles. Intended for use with cone-tracing algorithms.
Provides a datastructure for storing triangles based on how far they are from a light source. (Not Implemented Yet)
The datastructure is capable of performing the following queries:
1. for a point, get the closest triangle
2. get the visible surface area
## Building
Create a build directory and setup cmake
```
mkdir build
cd build
cmake ..
```
Build code
```
make
```
Run tests
```
./tests
```
\ No newline at end of file
#pragma once
#include <point.h>
#include "box_edges.h"
struct Triangle;
struct Point;
#define BOX_NB_POINTS 4
// left -> +x
// up -> +y
struct Box {
float minX, maxX, minY, maxY;
bool isIn(const Triangle &t) const;
bool isIn(const Point &p) const;
Point topLeft;
Point bottomRight;
Point points [BOX_NB_POINTS];
BoxEdges edges;
Box(Point topLeft, Point bottomRight);
bool intersects(const Triangle &t) const;
bool intersects(const Point &p) const;
Box firstQuadrant() const;
Box secondQuadrant() const;
Box thirdQuadrant() const;
Box fourthQuadrant() const;
};
\ No newline at end of file
#pragma once
#include <edge.h>
struct Box;
struct BoxEdges {
static const int size = 4;
Edge e [4];
BoxEdges(const Point p[size]);
};
\ No newline at end of file
#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
......@@ -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, std::shared_ptr<std::vector<int>> neighbours);
#pragma once
#include <vector>
struct Point;
struct Edge;
float interpolateZ(const Edge &e, const Point &p);
#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
......@@ -2,8 +2,11 @@
#include <ostream>
struct Point {
float x, y;
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);
......@@ -3,22 +3,30 @@
#include <memory>
#include <vector>
#include "box.h"
#include <set>
const int POINT_NOT_IN_QUADTREE = -1;
const int QUADTREE_MAX_DEPTH = 5;
#define QUADTREE_NODE_MAX_SHAPE 4
class QuadTree {
class QuadTree
{
Box b;
std::vector<Triangle> triangles;
int level;
std::vector<Triangle> triangles;
std::vector<QuadTree> children;
QuadTree(Box b, int level);
void collectUniqueTriangleFragments(const Triangle &t, std::set<int> &seen, std::vector<Triangle> &result) const;
void split();
void addNonIntersectingTriangle(const Triangle &t);
public:
QuadTree(Box b);
void addTriangle(Triangle triangle);
std::vector<Triangle> visibleSurface() const;
// returns triangle id
int pointIntersection(Point p) const;
std::vector<Triangle> visibleSurface(std::set<int> &seen) const;
public:
QuadTree(Box b);
void addTriangle(const Triangle &triangle);
std::vector<Triangle> visibleSurface() const;
// returns triangle id
int pointIntersection(const Point &p) const;
};
\ No newline at end of file
#pragma once
struct Triangle;
void shiftZ(Triangle &t, float z) ;
\ No newline at end of file
......@@ -2,17 +2,21 @@
#include "point.h"
#include "print_triangle.h"
#include <vector>
#include <memory>
// points specified counterclockwise
// note : Triangle equality is only specified for tests
// points specified in counterclockwise or collinear (line / point) order
struct Triangle
{
Point points[3];
int depth;
int id;
std::vector<int> neighbours;
int mainTriangleId;
int fragmentId;
std::shared_ptr<std::vector<int>> neighbours;
Triangle(const Point &p1, const Point &p2, const Point &p3, int depth = 0, int id = 0, const std::vector<int> neighbours = {});
Triangle(const Point &p1, const Point &p2, const Point &p3, int mainTriangleId = 0, const std::vector<int> neighbours = {});
Triangle(const Point &p1, const Point &p2, const Point &p3, int mainTriangleId, std::shared_ptr<std::vector<int>> neighbours);
bool pointInTriangle(const Point &p) const;
Point nextPoint(int pointIndex) const;
bool operator==(const Triangle &other) const;
......
#pragma once
class TriangleFragmentIdAssigner {
public:
static TriangleFragmentIdAssigner& getInstance()
{
static TriangleFragmentIdAssigner instance;
return instance;
}
int generateUniqueId() {
currentInd++;
return currentInd;
};
private:
int currentInd = 0;
TriangleFragmentIdAssigner() {}
public:
TriangleFragmentIdAssigner(TriangleFragmentIdAssigner const&) = delete;
void operator=(TriangleFragmentIdAssigner const&) = delete;
};
\ No newline at end of file
#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
......@@ -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
......@@ -2,14 +2,16 @@
#include <point.h>
#include <triangle.h>
#define DEBUG_EPSILON 0.0001
bool mostlyEqual(float a, float b)
{
return abs(a - b) < 0.0001;
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);
return mostlyEqual(a.x, b.x) && mostlyEqual(a.y, b.y) && mostlyEqual(a.z, b.z);
}
// mostly equal
......@@ -17,6 +19,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;
mainTriangleId == other.mainTriangleId;
}
\ No newline at end of file