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 690 additions and 213 deletions
#pragma once #pragma once
#include "point.h" #include "point.h"
#include "print_triangle.h"
#include <vector>
#include <memory>
// points specified counterclockwise // note : Triangle equality is only specified for tests
struct Triangle {
Point points[3]; // points specified in counterclockwise or collinear (line / point) order
int depth; struct Triangle
bool neighbours(const Triangle &other) const; {
Triangle(Point p1, Point p2, Point p3, int depth); Point points[3];
int mainTriangleId;
int fragmentId;
std::shared_ptr<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;
}; };
#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);
std::vector<Triangle> triangulate(std::vector<Point> points);
#include <box.h>
#include <triangle.h>
#include <constants.h>
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 (intersects({x,y})) {
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}, level{0} {}
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
{
std::vector<Triangle> result;
for (const Triangle &t : triangles) {
collectUniqueTriangleFragments(t, seen, result);
}
for (const QuadTree &c : children)
{
std::vector<Triangle> childResult = c.visibleSurface(seen);
result.insert(result.end(), childResult.begin(), childResult.end());
}
return result;
}
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)};
}
void QuadTree::addTriangle(const Triangle &triangle)
{
if (!b.intersects(triangle))
{
return;
}
if (triangles.empty())
{
triangles.push_back(triangle);
return;
}
if (!children.empty())
{
for (QuadTree &child : children)
{
child.addTriangle(triangle);
}
return;
}
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)
{
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());
}
}
}
// 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();
}
int QuadTree::pointIntersection(const Point &p) const
{
if (b.intersects(p))
{
for (const Triangle &triangle : triangles)
{
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
#include <print_triangle.h>
#include <triangle.h>
#include <point.h>
std::ostream &operator<<(std::ostream &os, const Triangle &t) {
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) {
for (const auto &t : triangles) {
}
return os;
}*/
#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 { ...@@ -4,4 +4,19 @@ bool Edge::positiveSide(const Point &p) const {
float h = p1.y - p2.y; float h = p1.y - p2.y;
float g = p1.x - p2.x; float g = p1.x - p2.x;
return -h * (p.x - p1.x) + g * (p.y - p1.y) >= 0; 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" #include "point.h"
bool Point::operator==(const Point &other) const { bool Point::operator==(const Point &other) const {
return x == other.x && y == other.y; return (x == other.x) && (y == other.y) && (z == other.z);
} }
\ No newline at end of file
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);
}
#include "triangle.h" #include "triangle.h"
#include "triangle_edges.h" #include "triangle_edges.h"
#include "edge.h" #include "edge.h"
#include <triangle_fragment_id_assigner.h>
bool Triangle::neighbours(const Triangle &other) const { Triangle::Triangle(const Point &p1, const Point &p2, const Point &p3, int mainTriangleId, std::shared_ptr<std::vector<int>> neighbours = {}) :
return false; points{p1, p2, p3}, mainTriangleId{mainTriangleId}, fragmentId{TriangleFragmentIdAssigner::getInstance().generateUniqueId()}, neighbours{neighbours} {}
}
Triangle::Triangle(Point p1, Point p2, Point p3, int depth) : points{p1,p2,p3}, depth{depth} {} 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 // all tests must be positive
auto edges = TriangleEdges(*this); auto edges = TriangleEdges(*this);
for (int i = 0; i < NB_TRIANGLE_SIDES; i++) { for (int i = 0; i < NB_TRIANGLE_SIDES; i++)
if (edges.edges[i].positiveSide(p)) { {
if (edges.edges[i].positiveSide(p))
{
return false; return false;
} }
} }
return true; return true;
} }
int nextPoint(int pointIndex)
int nextPoint(int pointIndex) { {
return (pointIndex + 1) % 3; return (pointIndex + 1) % 3;
} }
\ No newline at end of file
#include <vector>
#include <optional>
#include <union.h> #include <union.h>
#include <contourize.h> #include <contourize.h>
#include <triangle.h>
#include <constants.h>
#include <list>
#include <triangle_edges.h>
#include <intersections.h> #include <intersections.h>
#include <triangulation.h> #include <split_triangle.h>
#include <convex_triangulation.h>
#include <orientation.h>
std::vector<Triangle> unionize(const Triangle &t1, const Triangle &t2) { std::vector<Triangle> unionizeTopAndBottom(const Triangle &top, const Triangle &bot)
{
// degenerate line / point case
if (orientation(top) == Collinear) {
return {bot, top};
}
// if neighbours, do nothing std::vector<Triangle> result;
if (t1.neighbours(t2)) {
return std::vector{t1, t2};
}
// at most 3? if infinite -> consider no intersections TriangleEdges topEdges = TriangleEdges(top);
std::vector<Point> newIntersections = intersections(t1, t2);
if (newIntersections.empty()) { // keep track of relevant triangles
// either completely envelops each other, no intersections std::vector<Point> relv = {bot.points[0], bot.points[1], bot.points[2]};
return std::vector{t1, t2};
}
std::vector<Point> contour = contourize(t1, t2); for (int i = 0; i < NB_TRIANGLE_SIDES; i++)
{
const Edge &e = topEdges.edges[i];
return triangulate(contour); // 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 = convexTriangulation(shapes[0], bot.mainTriangleId, bot.neighbours);
result.insert(result.end(), relvTriangles.begin(), relvTriangles.end());
}
// future relevant triangles
relv = shapes[1];
}
result.push_back(top);
return result;
}
std::vector<Triangle> unionize(const Triangle &t1, const Triangle &t2)
{
if (t1.mainTriangleId == t2.mainTriangleId) {
return {t1, t2};
}
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);
} }
#include <vector> #include <contourize.h>
#include <triangle.h> #include <point.h>
#include <orientation.h> #include <orientation.h>
#include <triangle_edges.h> #include <list>
#include <stack_vector.h>
#include <intersections.h>
Triangle clockwiseToCounterclockwise(const Triangle &t) const char* ContourizeException::what() const throw() {
{ return "Infinite Loop detected";
return Triangle(t.points[0], t.points[2], t.points[1], t.depth);
} }
/*
bool isCounterClockwiseForAll(const Point &previous, const Point &candidate, const std::vector<Point> & points) { bool isCounterClockwiseForAll(const Point &previous, const Point &candidate, const std::vector<Point> & points) {
for (const auto &point : points) { for (const auto &point : points) {
if (previous == point || candidate == point) { if (previous == point || candidate == point) {
continue; continue;
} }
if (orientation(previous, point, candidate) != Counterclockwise) { auto o = orientation(previous, candidate, point);
if (o == Collinear) {
continue;
}
if (o != Counterclockwise) {
return false; return false;
} }
} }
return true; return true;
} }
*/
/* std::vector<Point> contourize(const std::vector<Point> &points) {
Point nextPoint(const Point &previous, std::vector<Point> &candidates, const std::vector<Point> &points) { if (points.size() < 3) {
Point mostCounterclockwise = candidates[0]; return points;
for (int i = 1; i < candidates.size(); i++) {
if (isCounterClockwiseForAll(previous, candidates[i], points)) {
Point p = candidates[i];
candidates.erase(i + candidates.begin());
return p;
}
} }
return Point{-69,-69};
}
*/
float distanceSquared(const Point &p1, const Point &p2)
{
return (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y);
}
bool closestPoint(const Point &refPoint, const Point &p1, const Point &p2)
{
float d1 = distanceSquared(refPoint, p1);
float d2 = distanceSquared(refPoint, p2);
return d1 < d2;
}
struct IntersectionAndEdge
{
Point p;
int edge;
};
std::vector<Point> contourize(const Triangle &leftmostTriangle, const Triangle &otherTriangle, int leftmostInd)
{
std::vector<Point> result; std::vector<Point> result;
const TriangleEdges lftEdges(leftmostTriangle); Point previous = points[0];
const TriangleEdges otherEdges(otherTriangle); result.push_back(previous);
StaticVector<IntersectionAndEdge> intersectionVector; std::list<Point> candidates(points.begin() + 1, points.end());
const int size = candidates.size();
bool usingLeftTriangle = true;
int prev = leftmostInd;
do
{
const Triangle &currentTriangle = usingLeftTriangle ? leftmostTriangle : otherTriangle;
const TriangleEdges &other = !usingLeftTriangle ? lftEdges : otherEdges;
result.push_back(currentTriangle.points[prev]);
int nextPointInTriangle = (prev + 1) % NB_TRIANGLE_SIDES;
const Edge &e1 = Edge{currentTriangle.points[prev], currentTriangle.points[nextPointInTriangle]};
for (int i = 0; i < NB_TRIANGLE_SIDES; i++)
{
auto intrs = intersection(e1, other.edges[i]);
if (intrs.has_value())
{
intersectionVector.push_back(IntersectionAndEdge{intrs.value(), i});
}
}
if (intersectionVector.getSize() == 0)
{
prev = nextPointInTriangle;
continue;
}
else
{
// intersections found. Must switch to next triangle
usingLeftTriangle = !usingLeftTriangle;
int edgeIndex = intersectionVector.items[0].edge;
if (intersectionVector.getSize() == 2 && !closestPoint(currentTriangle.points[prev], intersectionVector.items->p, intersectionVector.items[1].p))
{
// closest edge
edgeIndex = intersectionVector.items[1].edge;
result.push_back(intersectionVector.items[1].p);
}
else
{
result.push_back(intersectionVector.items[0].p);
}
const Edge &e2 = other.edges[edgeIndex];
const std::pair<int, int> edgePointIndexes = TriangleEdges::otherPoint(edgeIndex);
if (!e1.positiveSide(e2.p1))
{
prev = edgePointIndexes.first;
}
else
{
prev = edgePointIndexes.second;
}
}
} while (!(prev == leftmostInd));
return result;
}
/* // infinite loop detection
int seen = 0;
std::vector<Point> contourize(const Triangle &t1, const Triangle &t2, const std::vector<Point> &newIntersections) { while (candidates.size() > 1) {
std::vector<Point> result; // detect infinite loop
// Go through each event point. if (seen >= candidates.size()) {
// If an even point is in a triangle, it is not part of the final Triangle throw ContourizeException();
for (auto &p : t1.points) {
if (!t2.pointInTriangle(p)) {
result.push_back(p);
} }
}
for (auto &p : t2.points) { if (isCounterClockwiseForAll(previous, candidates.front(), points)) {
if (!t2.pointInTriangle(p)) { previous = candidates.front();
result.push_back(p); candidates.pop_front();
result.push_back(previous);
seen = 0;
} else {
candidates.push_back(candidates.front());
candidates.pop_front();
seen++;
} }
} }
result.insert(result.end(), newIntersections.begin(), newIntersections.end()); result.push_back(candidates.front());
return result; return result;
} }
*/
\ No newline at end of file
#include <convex_triangulation.h>
#include <orientation.h>
// 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], triangleId, neighbours);
std::vector<Triangle> results;
appendResults(results, t1);
Point first = points[0];
for (int i = size - 1; i >= 3; i--) {
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;
}
#include "intersections.h" #include "intersections.h"
#include <optional> #include <optional>
#include <edge.h>
#include <triangle_edges.h> #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)
{
std::optional<float> getB(std::optional<float> slope, Point p) { if (!slope.has_value())
if (!slope.has_value()) { {
return {}; return {};
} }
return p.y - slope.value() * p.x; return p.y - slope.value() * p.x;
} }
std::optional<float> getSlope(Edge e) { std::optional<float> getSlope(Edge e)
if (e.p2.x - e.p1.x == 0) { {
if (e.p2.x - e.p1.x == 0)
{
return {}; return {};
} }
return (e.p2.y - e.p1.y) / (e.p2.x - e.p1.x) ; return (e.p2.y - e.p1.y) / (e.p2.x - e.p1.x);
}
bool withinEdge(Edge e, Point p)
{
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) - 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;
}
void intersection(Edge e1, Edge e2, std::vector<Point> &results)
{
auto point = intersectionWithinEdge(e1, e2);
if (point.has_value())
{
results.push_back(point.value());
}
} }
bool withinEdge(Edge e, Point p) { std::optional<Point> intersectionWithinEdge(const Edge &e1, const Edge &e2)
float minX = std::min(e.p1.x, e.p2.x); {
float maxX = std::max(e.p1.x, e.p2.x); std::optional<Point> candPoint = intersection(e1, e2);
if (candPoint.has_value() &&
withinEdge(e1, candPoint.value()) &&
withinEdge(e2, candPoint.value()))
{
return candPoint;
}
return {};
}
float minY = std::min(e.p1.y, e.p2.y); // intersection with first edge being considered infinite in length
float maxY = std::max(e.p1.y, e.p2.y); std::optional<Point> intersectionFirstSide(const Edge &e1, const Edge &e2) {
auto candPoint = intersection(e1, e2);
return minX <= p.x && p.x <= maxX && minY <= p.y && p.y <= maxY; 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)
{
auto candPoint = intersection(e1, e2);
if (candPoint.has_value() && withinEdge(e2, candPoint.value()))
{
// check if in direction e1 is pointing at
// case where e1 is pointing towards positive x axis
if (e1.p1.x < e1.p2.x && candPoint.value().x >= e1.p1.x)
{
return candPoint;
}
// case where e1 is pointing towards negative x axis
if (e1.p1.x > e1.p2.x && candPoint.value().x <= e1.p1.x)
{
return candPoint;
}
// edge case where p1 and p2 form a vertical line
// case where e1 is pointing towards positive y axis
if (e1.p1.y < e1.p2.y && candPoint.value().y >= e1.p1.y)
{
return candPoint;
}
// case where e1 is pointing towards negative axis
if (e1.p1.y > e1.p2.y && candPoint.value().y <= e1.p1.y)
{
return candPoint;
}
}
return {};
}
void intersection(Edge e1, Edge e2, std::vector<Point> &results) { std::optional<Point> intersection(const Edge &e1, const Edge &e2)
{
auto slope1 = getSlope(e1); auto slope1 = getSlope(e1);
auto slope2 = getSlope(e2); auto slope2 = getSlope(e2);
auto b1 = getB(slope1, e1.p1); auto b1 = getB(slope1, e1.p1);
auto b2 = getB(slope2, e2.p1); auto b2 = getB(slope2, e2.p1);
// ignore overlapping case // ignore overlapping case
if (!slope1.has_value() && !slope2.has_value()) { if (!slope1.has_value() && !slope2.has_value())
return; {
return {};
} }
if (!slope1.has_value()) { if (!slope1.has_value())
results.push_back(Point{e1.p1.x, slope2.value() * e1.p1.x + b2.value()}); {
return; return Point{e1.p1.x, slope2.value() * e1.p1.x + b2.value()};
} }
if (!slope2.has_value()) { if (!slope2.has_value())
results.push_back(Point{e2.p1.x, slope1.value() * e2.p1.x + b1.value()}); {
return; return Point{e2.p1.x, slope1.value() * e2.p1.x + b1.value()};
} }
float candX = (b2.value() - b1.value()) / (slope1.value() - slope2.value()); float candX = (b2.value() - b1.value()) / (slope1.value() - slope2.value());
auto candPoint = Point{ candX, slope1.value() * candX + b1.value()}; auto candPoint = Point{candX, slope1.value() * candX + b1.value()};
if (withinEdge(e1, candPoint) && withinEdge(e2, candPoint)) { // ignore case where intersection is at an endpoint
results.push_back(candPoint); /*
if (candPoint == e1.p1 || candPoint == e1.p2 || candPoint == e2.p1 || candPoint == e2.p2) {
return {};
}*/
if (candPoint.x > MAX_POINT || candPoint.y > MAX_POINT || candPoint.x < MIN_POINT || candPoint.y < MIN_POINT)
{
return {};
} }
return candPoint;
} }
void intersections(Edge e1, TriangleEdges te, std::vector<Point> &results) { void intersections(const Edge &e1, const TriangleEdges &te, std::vector<Point> &results)
intersection(e1, te.e1, results); {
intersection(e1, te.e2, results); for (int i = 0; i < NB_TRIANGLE_SIDES; i++)
intersection(e1, te.e3, results); {
intersection(e1, te.edges[i], results);
}
} }
std::vector<Point> intersections(Triangle t1, Triangle t2) { std::vector<Point> intersections(const Triangle &t1, const Triangle &t2)
{
TriangleEdges t1Edges = TriangleEdges(t1); TriangleEdges t1Edges = TriangleEdges(t1);
TriangleEdges t2Edges = TriangleEdges(t2); TriangleEdges t2Edges = TriangleEdges(t2);
std::vector<Point> results; std::vector<Point> results;
intersections(t1Edges.e1, t2Edges, results); for (int i = 0; i < NB_TRIANGLE_SIDES; i++)
intersections(t1Edges.e2, t2Edges, results); {
intersections(t1Edges.e3, t2Edges, results); intersections(t1Edges.edges[i], t2Edges, results);
}
return results; return results;
} }
std::vector<Point> intersections(const std::vector<Point> &points, const Edge &line)
{
std::vector<Point> result;
std::vector<Edge> es = makeEdges(points);
for (int i = 0; i < es.size(); i++)
{
auto cand = intersectionFirstSide(line, es[i]);
if (cand.has_value())
{
result.push_back(cand.value());
}
}
return result;
}
#include "orientation.h" #include "orientation.h"
#include <point.h> #include <point.h>
#include <triangle.h>
float edgeValue(const Point &p1, const Point &p2) {
return (p2.x - p1.x) * (p2.y + p1.y);
}
Orientation orientation(const Point &p1, const Point &p2, const Point &p3) { Orientation orientation(const Point &p1, const Point &p2, const Point &p3) {
int val = (p2.y - p1.y) * (p3.x - p2.x) - (p2.x - p1.x) * (p3.y - p2.y); float val = edgeValue(p1, p2) + edgeValue(p2, p3) + edgeValue(p3, p1);
if (val == 0) { if (val == 0) {
return Collinear; return Collinear;
} }
return (val > 0) ? Clockwise : Counterclockwise; 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
...@@ -4,14 +4,16 @@ bool PointList::empty() const { ...@@ -4,14 +4,16 @@ bool PointList::empty() const {
return size == 0; return size == 0;
} }
int PointList::prev(const PointNode &p) const { int PointList::getSize() const {
const int index = (p.prevIndex + size) % size; return size;
return index;
} }
int PointList::next(const PointNode &p) const { int PointNode::next() const {
const int index = p.nextIndex % size; return nextIndex;
return index; }
int PointNode::prev() const {
return prevIndex;
} }
void PointList::remove(int i) { void PointList::remove(int i) {
...@@ -20,10 +22,8 @@ void PointList::remove(int i) { ...@@ -20,10 +22,8 @@ void PointList::remove(int i) {
return; return;
} }
const PointNode &p = points[i]; const PointNode &p = points[i];
int prevInd = prev(p); points[p.prev()].nextIndex = p.next();
int nextInd = next(p); points[p.next()].prevIndex = p.prev();
points[prevInd].nextIndex = nextInd;
points[nextInd].prevIndex = prevInd;
size--; size--;
} }
...@@ -31,4 +31,6 @@ PointList::PointList(const std::vector<Point> &points) : size {points.size()} { ...@@ -31,4 +31,6 @@ PointList::PointList(const std::vector<Point> &points) : size {points.size()} {
for (int i = 0; i < points.size(); i++) { for (int i = 0; i < points.size(); i++) {
this->points[i] = PointNode{i - 1, i + 1, points[i]}; this->points[i] = PointNode{i - 1, i + 1, points[i]};
} }
this->points[0].prevIndex = size - 1;
this->points[size - 1].nextIndex = 0;
} }
\ No newline at end of file
#include <split_triangle.h>
#include <constants.h>
#include <contourize.h>
#include <triangle.h>
#include <edge.h>
#include <intersections.h>
std::vector<std::vector<Point>> splitShape(const std::vector<Point> &points, const Edge &line)
{
// get intersections
auto intr = intersections(points, line);
std::vector<Point> pos;
std::vector<Point> neg;
for (int i = 0; i < points.size(); i++)
{
if (line.positiveSide(points[i]))
{
pos.push_back(points[i]);
}
else
{
neg.push_back(points[i]);
}
}
if (!(pos.empty() || neg.empty()))
{
pos.insert(pos.end(), intr.begin(), intr.end());
neg.insert(neg.end(), intr.begin(), intr.end());
} // Build Positive side
pos = contourize(pos);
// Build Negative side
neg = contourize(neg);
return std::vector<std::vector<Point>>{pos, neg};
}
\ No newline at end of file