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 build
.vscode/c_cpp_properties.json .vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/settings.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) ...@@ -23,13 +23,20 @@ project(intersection)
add_executable( add_executable(
tests tests
tests/unit_tests/split_triangle_tests.cpp tests/unit_tests/split_triangle_tests.cpp
src/debug_utilities/mostly_equal.cpp
tests/unit_tests/simple_triangle_tests.cpp tests/unit_tests/simple_triangle_tests.cpp
tests/unit_tests/point_in_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/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.cpp
src/shapes/triangle_edges.cpp src/shapes/triangle_edges.cpp
src/utilities/contourize.cpp src/utilities/contourize.cpp
...@@ -37,22 +44,15 @@ add_executable( ...@@ -37,22 +44,15 @@ add_executable(
src/utilities/orientation.cpp src/utilities/orientation.cpp
src/shapes/point.cpp src/shapes/point.cpp
src/union.cpp src/union.cpp
src/utilities/triangulation.cpp
src/utilities/pointList.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/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 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/debug_utilities/print_triangles.cpp
src/utilities/split_triangle.cpp src/utilities/split_triangle.cpp
tests/unit_tests/orientation_tests.cpp src/debug_utilities/shift_triangle.cpp
tests/union_neighbours_tests.cpp src/utilities/interpolate_z.cpp
src/data_structures/quad_tree.cpp
src/data_structures/box_edges.cpp
) )
include_directories(include) 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: The datastructure is capable of performing the following queries:
1. for a point, get the closest triangle 1. for a point, get the closest triangle
2. get the visible surface area 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 #pragma once
#include <point.h>
#include "box_edges.h"
struct Triangle; struct Triangle;
struct Point; #define BOX_NB_POINTS 4
// left -> +x
// up -> +y
struct Box { struct Box {
float minX, maxX, minY, maxY; Point topLeft;
bool isIn(const Triangle &t) const; Point bottomRight;
bool isIn(const Point &p) const; 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 #pragma once
#include <vector> #include <vector>
#include <string>
#include <exception>
struct Triangle; struct Triangle;
struct Point; struct Point;
class ContourizeException : public std::exception {
virtual const char *what() const throw();
};
// creates a contour from a list of points // creates a contour from a list of points
std::vector<Point> contourize(const std::vector<Point> &points); std::vector<Point> contourize(const std::vector<Point> &points);
\ No newline at end of file
...@@ -5,4 +5,4 @@ ...@@ -5,4 +5,4 @@
#include <vector> #include <vector>
// Requires a convex shape // 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 #pragma once
struct Triangle;
struct Point; struct Point;
enum Orientation { enum Orientation {
Clockwise, Collinear, Counterclockwise Clockwise, Collinear, Counterclockwise
}; };
Orientation orientation(const Point &p1, const Point &p2, const Point &p3); Orientation orientation(const Point &p1, const Point &p2, const Point &p3);
\ No newline at end of file
Orientation orientation(const Triangle &t);
\ No newline at end of file
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
#include <ostream> #include <ostream>
struct Point { struct Point {
float x, y; float x, y, z;
bool operator==(const Point &other) const; 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); std::ostream &operator<<(std::ostream &os, Point const &p);
...@@ -3,22 +3,30 @@ ...@@ -3,22 +3,30 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "box.h" #include "box.h"
#include <set>
const int POINT_NOT_IN_QUADTREE = -1; const int POINT_NOT_IN_QUADTREE = -1;
const int QUADTREE_MAX_DEPTH = 5;
#define QUADTREE_NODE_MAX_SHAPE 4 #define QUADTREE_NODE_MAX_SHAPE 4
class QuadTree { class QuadTree
{
Box b; Box b;
std::vector<Triangle> triangles; int level;
std::vector<Triangle> triangles;
std::vector<QuadTree> children; 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: std::vector<Triangle> visibleSurface(std::set<int> &seen) const;
QuadTree(Box b); public:
void addTriangle(Triangle triangle); QuadTree(Box b);
std::vector<Triangle> visibleSurface() const; void addTriangle(const Triangle &triangle);
// returns triangle id std::vector<Triangle> visibleSurface() const;
int pointIntersection(Point p) 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 @@ ...@@ -2,17 +2,21 @@
#include "point.h" #include "point.h"
#include "print_triangle.h" #include "print_triangle.h"
#include <vector> #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 struct Triangle
{ {
Point points[3]; Point points[3];
int depth; int mainTriangleId;
int id; int fragmentId;
std::vector<int> neighbours; 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; bool pointInTriangle(const Point &p) const;
Point nextPoint(int pointIndex) const; Point nextPoint(int pointIndex) const;
bool operator==(const Triangle &other) 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 @@ ...@@ -2,20 +2,47 @@
#include <triangle.h> #include <triangle.h>
#include <constants.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++) { for (int i = 0; i < NB_TRIANGLE_SIDES; i++) {
float x = t.points[i].x; float x = t.points[i].x;
float y = t.points[i].y; float y = t.points[i].y;
if (x > minX && x < maxX && y > minY && y < maxY) { if (intersects({x,y})) {
return true; return true;
} }
} }
return false;
}
bool Box::isIn(const Point &p) const { for (int i = 0; i < BOX_NB_POINTS; i++) {
if (p.x > minX && p.x < maxX && p.y > minY && p.y < maxY) { if (t.pointInTriangle(points[i])) {
return true; return true;
}
} }
return false; 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 <quad_tree.h>
#include <union.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; std::vector<Triangle> result;
if (!b.isIn(p))
{ for (const Triangle &t : triangles) {
return POINT_NOT_IN_QUADTREE; collectUniqueTriangleFragments(t, seen, result);
} }
if (triangles.size() > 0) for (const QuadTree &c : children)
{ {
int depth = INT32_MAX; std::vector<Triangle> childResult = c.visibleSurface(seen);
for (const Triangle &t : triangles) result.insert(result.end(), childResult.begin(), childResult.end());
{
if (t.depth < depth && t.pointInTriangle(p))
{
depth = t.depth;
triangleId = t.id;
}
}
return triangleId;
} }
for (const QuadTree &q : children) return result;
{
int id = q.pointIntersection(p);
if (id != POINT_NOT_IN_QUADTREE)
{
triangleId = POINT_NOT_IN_QUADTREE;
}
}
return triangleId;
} }
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; return;
} }
if (triangles.size() >= QUADTREE_NODE_MAX_SHAPE && children.empty()) if (triangles.empty())
{ {
children = { triangles.push_back(triangle);
QuadTree(Box{b.minX, b.maxX / 2, b.minY, b.maxY}), return;
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) if (!children.empty())
{
for (QuadTree &child : children)
{ {
child.addTriangle(triangle);
}
return;
}
c.addTriangle(triangle); std::vector<Triangle> currentTriangleFragments{triangle};
for (const Triangle &t : triangles)
// 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()) // split
{ if (level >= QUADTREE_MAX_DEPTH) {
triangles.push_back(triangle); triangles.insert(triangles.end(), currentTriangleFragments.begin(), currentTriangleFragments.end());
triangles.insert(triangles.end(), otherTriangleFragments.begin(), otherTriangleFragments.end());
return; 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 @@ ...@@ -2,14 +2,16 @@
#include <point.h> #include <point.h>
#include <triangle.h> #include <triangle.h>
#define DEBUG_EPSILON 0.0001
bool mostlyEqual(float a, float b) 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) 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 // mostly equal
...@@ -17,6 +19,5 @@ bool Triangle::operator==(const Triangle &other) const { ...@@ -17,6 +19,5 @@ bool Triangle::operator==(const Triangle &other) const {
return mostlyEqual(points[0], other.points[0]) && return mostlyEqual(points[0], other.points[0]) &&
mostlyEqual(points[1], other.points[1]) && mostlyEqual(points[1], other.points[1]) &&
mostlyEqual(points[2], other.points[2]) && mostlyEqual(points[2], other.points[2]) &&
depth == other.depth && mainTriangleId == other.mainTriangleId;
id == other.id;
} }
\ No newline at end of file