Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,141 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief A class to hold a bounding box
*/
#include <algorithm>
#include <stdlib.h>
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
template<class Point> class BBox {
public:
inline BBox()
{
_empty = true;
}
template<class T> inline BBox(const T &min_in, const T &max_in) : _min(min_in), _max(max_in)
{
_empty = false;
}
template<class T> inline BBox(const BBox<T> &b) : _min(b.getMin()), _max(b.getMax())
{
_empty = false;
}
template<class T> inline void extendToContain(const T &p)
{
if (_empty) {
_min = p;
_max = p;
_empty = false;
return;
}
for (uint i = 0; i < Point::dim(); i++) {
if (p[i] < _min[i]) {
_min[i] = p[i];
}
else if (p[i] > _max[i]) {
_max[i] = p[i];
}
}
_empty = false;
}
inline void clear()
{
_empty = true;
}
inline bool empty() const
{
return _empty;
}
inline const Point &getMin() const
{
return _min;
}
inline const Point &getMax() const
{
return _max;
}
inline BBox<Point> &operator=(const BBox<Point> &b)
{
BLI_assert(!b.empty());
_min = b.getMin();
_max = b.getMax();
_empty = false;
return *this;
}
inline BBox<Point> &operator+=(const BBox<Point> &b)
{
BLI_assert(!b.empty());
if (_empty) {
_min = b.getMin();
_max = b.getMax();
_empty = false;
}
else {
for (uint i = 0; i < Point::dim(); i++) {
if (b.getMin()[i] < _min[i]) {
_min[i] = b.getMin()[i];
}
if (b.getMax()[i] > _max[i]) {
_max[i] = b.getMax()[i];
}
}
}
return *this;
}
inline bool inside(const Point &p)
{
if (empty()) {
return false;
}
for (uint i = 0; i < Point::dim(); i++) {
if ((_min[i] > p[i]) || (_max[i] < p[i])) {
return false;
}
}
return true;
}
private:
Point _min;
Point _max;
bool _empty;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BBox")
};
template<class Point> BBox<Point> &operator+(const BBox<Point> &b1, const BBox<Point> &b2)
{
Point new_min;
Point new_max;
for (uint i = 0; i < Point::dim(); i++) {
new_min[i] = b1.getMin()[i] < b2.getMin()[i] ? b1.getMin()[i] : b2.getMin()[i];
new_max[i] = b1.getMax()[i] > b2.getMax()[i] ? b1.getMax()[i] : b2.getMax()[i];
}
return BBox<Point>(new_min, new_max);
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,108 @@
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define a Bezier curve of order 4.
*/
#include "Bezier.h"
#include "FitCurve.h"
using namespace std;
namespace Freestyle {
BezierCurveSegment::~BezierCurveSegment() = default;
void BezierCurveSegment::AddControlPoint(const Vec2d &iPoint)
{
_ControlPolygon.push_back(iPoint);
if (_ControlPolygon.size() == 4) {
Build();
}
}
void BezierCurveSegment::Build()
{
if (_ControlPolygon.size() != 4) {
return;
}
// Compute the rightmost part of the matrix:
vector<Vec2d>::const_iterator p0, p1, p2, p3;
p0 = _ControlPolygon.begin();
p1 = p0;
++p1;
p2 = p1;
++p2;
p3 = p2;
++p3;
float x[4], y[4];
x[0] = -p0->x() + 3 * p1->x() - 3 * p2->x() + p3->x();
x[1] = 3 * p0->x() - 6 * p1->x() + 3 * p2->x();
x[2] = -3 * p0->x() + 3 * p1->x();
x[3] = p0->x();
y[0] = -p0->y() + 3 * p1->y() - 3 * p2->y() + p3->y();
y[1] = 3 * p0->y() - 6 * p1->y() + 3 * p2->y();
y[2] = -3 * p0->y() + 3 * p1->y();
y[3] = p0->y();
int nvertices = 12;
float increment = 1.0 / float(nvertices);
float t = 0.0f;
for (int i = 0; i <= nvertices; ++i) {
_Vertices.emplace_back((x[3] + t * (x[2] + t * (x[1] + t * x[0]))),
(y[3] + t * (y[2] + t * (y[1] + t * y[0]))));
t += increment;
}
}
BezierCurve::BezierCurve()
{
_currentSegment = new BezierCurveSegment;
}
BezierCurve::BezierCurve(vector<Vec2d> &iPoints, double error)
{
FitCurveWrapper fitcurve;
_currentSegment = new BezierCurveSegment;
vector<Vec2d> curve;
fitcurve.FitCurve(iPoints, curve, error);
int i = 0;
vector<Vec2d>::iterator v, vend;
for (v = curve.begin(), vend = curve.end(); v != vend; ++v) {
if ((i == 0) || (i % 4 != 0)) {
AddControlPoint(*v);
}
++i;
}
}
BezierCurve::~BezierCurve()
{
if (!_Segments.empty()) {
vector<BezierCurveSegment *>::iterator v, vend;
for (v = _Segments.begin(), vend = _Segments.end(); v != vend; ++v) {
delete *v;
}
}
delete _currentSegment;
}
void BezierCurve::AddControlPoint(const Vec2d &iPoint)
{
_ControlPolygon.push_back(iPoint);
_currentSegment->AddControlPoint(iPoint);
if (_currentSegment->size() == 4) {
_Segments.push_back(_currentSegment);
_currentSegment = new BezierCurveSegment;
_currentSegment->AddControlPoint(iPoint);
}
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,74 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a Bezier curve of order 4.
*/
#include <vector>
#include "Geom.h"
#include "../system/FreestyleConfig.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
using namespace Geometry;
class BezierCurveSegment {
private:
std::vector<Vec2d> _ControlPolygon;
std::vector<Vec2d> _Vertices;
public:
virtual ~BezierCurveSegment();
void AddControlPoint(const Vec2d &iPoint);
void Build();
inline int size() const
{
return _ControlPolygon.size();
}
inline std::vector<Vec2d> &vertices()
{
return _Vertices;
}
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BezierCurveSegment")
};
class BezierCurve {
private:
std::vector<Vec2d> _ControlPolygon;
std::vector<BezierCurveSegment *> _Segments;
BezierCurveSegment *_currentSegment;
public:
BezierCurve();
BezierCurve(std::vector<Vec2d> &iPoints, double error = 4.0);
virtual ~BezierCurve();
void AddControlPoint(const Vec2d &iPoint);
std::vector<Vec2d> &controlPolygon()
{
return _ControlPolygon;
}
std::vector<BezierCurveSegment *> &segments()
{
return _Segments;
}
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BezierCurve")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,71 @@
/* SPDX-FileCopyrightText: 2009-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the bounding box of the scene
*/
#include <cstdlib>
#include "FastGrid.h"
#include "BKE_global.hh"
#include "BLI_utildefines.h"
namespace Freestyle {
void FastGrid::clear()
{
if (!_cells) {
return;
}
for (uint i = 0; i < _cells_size; i++) {
if (_cells[i]) {
delete _cells[i];
}
}
delete[] _cells;
_cells = nullptr;
_cells_size = 0;
Grid::clear();
}
void FastGrid::configure(const Vec3r &orig, const Vec3r &size, uint nb)
{
Grid::configure(orig, size, nb);
_cells_size = _cells_nb[0] * _cells_nb[1] * _cells_nb[2];
_cells = new Cell *[_cells_size];
memset(_cells, 0, _cells_size * sizeof(*_cells));
}
Cell *FastGrid::getCell(const Vec3u &p)
{
#if 0
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
cout << _cells << " " << p << " " << _cells_nb[0] << "-" << _cells_nb[1] << "-" << _cells_nb[2]
<< " " << _cells_size << endl;
}
#endif
BLI_assert_msg(_cells, "_cells is a null pointer");
BLI_assert((_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]) < _cells_size);
BLI_assert(p[0] < _cells_nb[0]);
BLI_assert(p[1] < _cells_nb[1]);
BLI_assert(p[2] < _cells_nb[2]);
return _cells[_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]];
}
void FastGrid::fillCell(const Vec3u &p, Cell &cell)
{
BLI_assert_msg(_cells, "_cells is a null pointer");
BLI_assert((_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]) < _cells_size);
BLI_assert(p[0] < _cells_nb[0]);
BLI_assert(p[1] < _cells_nb[1]);
BLI_assert(p[2] < _cells_nb[2]);
_cells[_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]] = &cell;
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,63 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the bounding box of the scene
*/
#include "Grid.h"
namespace Freestyle {
/** Class to define a regular grid used for ray casting computations
* We don't use a hash-table here. The grid is explicitly stored for faster computations.
* However, this might result in significant increase in memory usage
* (compared to the regular grid).
*/
class FastGrid : public Grid {
public:
FastGrid() : Grid()
{
_cells = nullptr;
_cells_size = 0;
}
virtual ~FastGrid()
{
clear();
}
/**
* clears the grid
* Deletes all the cells, clears the hash-table, resets size, size of cell, number of cells.
*/
virtual void clear();
/** Sets the different parameters of the grid
* orig
* The grid origin
* size
* The grid's dimensions
* nb
* The number of cells of the grid
*/
virtual void configure(const Vec3r &orig, const Vec3r &size, uint nb);
/** returns the cell whose coordinates are passed as argument */
Cell *getCell(const Vec3u &p);
/** Fills the case p with the cell iCell */
virtual void fillCell(const Vec3u &p, Cell &cell);
protected:
Cell **_cells;
uint _cells_size;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:FastGrid")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,569 @@
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief An Algorithm for Automatically Fitting Digitized Curves by Philip J. Schneider,
* \brief from "Graphics Gems", Academic Press, 1990
*/
#include <cmath>
#include <cstdio>
#include <cstdlib> // for malloc and free
#include "FitCurve.h"
#include "BLI_sys_types.h"
using namespace std;
namespace Freestyle {
using BezierCurve = Vector2 *;
/* Forward declarations */
static double *Reparameterize(Vector2 *d, int first, int last, double *u, BezierCurve bezCurve);
static double NewtonRaphsonRootFind(BezierCurve Q, Vector2 P, double u);
static Vector2 BezierII(int degree, Vector2 *V, double t);
static double B0(double u);
static double B1(double u);
static double B2(double u);
static double B3(double u);
static Vector2 ComputeLeftTangent(Vector2 *d, int end);
static double ComputeMaxError(
Vector2 *d, int first, int last, BezierCurve bezCurve, double *u, int *splitPoint);
static double *ChordLengthParameterize(Vector2 *d, int first, int last);
static BezierCurve GenerateBezier(
Vector2 *d, int first, int last, double *uPrime, Vector2 tHat1, Vector2 tHat2);
static Vector2 V2AddII(Vector2 a, Vector2 b);
static Vector2 V2ScaleIII(Vector2 v, double s);
static Vector2 V2SubII(Vector2 a, Vector2 b);
/* returns squared length of input vector */
static double V2SquaredLength(Vector2 *a)
{
return (((*a)[0] * (*a)[0]) + ((*a)[1] * (*a)[1]));
}
/* returns length of input vector */
static double V2Length(Vector2 *a)
{
return sqrt(V2SquaredLength(a));
}
static Vector2 *V2Scale(Vector2 *v, double newlen)
{
double len = V2Length(v);
if (len != 0.0) {
(*v)[0] *= newlen / len;
(*v)[1] *= newlen / len;
}
return v;
}
/* return the dot product of vectors a and b */
static double V2Dot(Vector2 *a, Vector2 *b)
{
return (((*a)[0] * (*b)[0]) + ((*a)[1] * (*b)[1]));
}
/* return the distance between two points */
static double V2DistanceBetween2Points(Vector2 *a, Vector2 *b)
{
double dx = (*a)[0] - (*b)[0];
double dy = (*a)[1] - (*b)[1];
return sqrt((dx * dx) + (dy * dy));
}
/* return vector sum c = a+b */
static Vector2 *V2Add(Vector2 *a, Vector2 *b, Vector2 *c)
{
(*c)[0] = (*a)[0] + (*b)[0];
(*c)[1] = (*a)[1] + (*b)[1];
return c;
}
/* normalizes the input vector and returns it */
static Vector2 *V2Normalize(Vector2 *v)
{
double len = V2Length(v);
if (len != 0.0) {
(*v)[0] /= len;
(*v)[1] /= len;
}
return v;
}
/* negates the input vector and returns it */
static Vector2 *V2Negate(Vector2 *v)
{
(*v)[0] = -(*v)[0];
(*v)[1] = -(*v)[1];
return v;
}
/* GenerateBezier:
* Use least-squares method to find Bezier control points for region.
* Vector2 *d; Array of digitized points
* int first, last; Indices defining region
* double *uPrime; Parameter values for region
* Vector2 tHat1, tHat2; Unit tangents at endpoints
*/
static BezierCurve GenerateBezier(
Vector2 *d, int first, int last, double *uPrime, Vector2 tHat1, Vector2 tHat2)
{
int i;
Vector2 A[2]; /* rhs for eqn */
int nPts; /* Number of pts in sub-curve */
double C[2][2]; /* Matrix C */
double X[2]; /* Matrix X */
double det_C0_C1; /* Determinants of matrices */
double det_C0_X;
double det_X_C1;
double alpha_l; /* Alpha values, left and right */
double alpha_r;
Vector2 tmp; /* Utility variable */
BezierCurve bezCurve; /* RETURN bezier curve control points. */
bezCurve = (Vector2 *)malloc(4 * sizeof(Vector2));
nPts = last - first + 1;
/* Create the C and X matrices */
C[0][0] = 0.0;
C[0][1] = 0.0;
C[1][0] = 0.0;
C[1][1] = 0.0;
X[0] = 0.0;
X[1] = 0.0;
for (i = 0; i < nPts; i++) {
/* Compute the A's */
A[0] = tHat1;
A[1] = tHat2;
V2Scale(&A[0], B1(uPrime[i]));
V2Scale(&A[1], B2(uPrime[i]));
C[0][0] += V2Dot(&A[0], &A[0]);
C[0][1] += V2Dot(&A[0], &A[1]);
// C[1][0] += V2Dot(&A[0], &A[1]);
C[1][0] = C[0][1];
C[1][1] += V2Dot(&A[1], &A[1]);
tmp = V2SubII(d[first + i],
V2AddII(V2ScaleIII(d[first], B0(uPrime[i])),
V2AddII(V2ScaleIII(d[first], B1(uPrime[i])),
V2AddII(V2ScaleIII(d[last], B2(uPrime[i])),
V2ScaleIII(d[last], B3(uPrime[i]))))));
X[0] += V2Dot(&A[0], &tmp);
X[1] += V2Dot(&A[1], &tmp);
}
/* Compute the determinants of C and X */
det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
det_C0_X = C[0][0] * X[1] - C[0][1] * X[0];
det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
/* Finally, derive alpha values */
if (det_C0_C1 == 0.0) {
det_C0_C1 = (C[0][0] * C[1][1]) * 10.0e-12;
}
alpha_l = det_X_C1 / det_C0_C1;
alpha_r = det_C0_X / det_C0_C1;
/* If alpha negative, use the Wu/Barsky heuristic (see text) (if alpha is 0, you get coincident
* control points that lead to divide by zero in any subsequent NewtonRaphsonRootFind() call).
*/
if (alpha_l < 1.0e-6 || alpha_r < 1.0e-6) {
double dist = V2DistanceBetween2Points(&d[last], &d[first]) / 3.0;
bezCurve[0] = d[first];
bezCurve[3] = d[last];
V2Add(&(bezCurve[0]), V2Scale(&(tHat1), dist), &(bezCurve[1]));
V2Add(&(bezCurve[3]), V2Scale(&(tHat2), dist), &(bezCurve[2]));
return bezCurve;
}
/* First and last control points of the Bezier curve are positioned exactly at the first and last
* data points Control points 1 and 2 are positioned an alpha distance out on the tangent
* vectors, left and right, respectively
*/
bezCurve[0] = d[first];
bezCurve[3] = d[last];
V2Add(&bezCurve[0], V2Scale(&tHat1, alpha_l), &bezCurve[1]);
V2Add(&bezCurve[3], V2Scale(&tHat2, alpha_r), &bezCurve[2]);
return bezCurve;
}
/*
* Reparameterize:
* Given set of points and their parameterization, try to find a better parameterization.
* Vector2 *d; Array of digitized points
* int first, last; Indices defining region
* double *u; Current parameter values
* BezierCurve bezCurve; Current fitted curve
*/
static double *Reparameterize(Vector2 *d, int first, int last, double *u, BezierCurve bezCurve)
{
int nPts = last - first + 1;
int i;
double *uPrime; /* New parameter values */
uPrime = (double *)malloc(nPts * sizeof(double));
for (i = first; i <= last; i++) {
uPrime[i - first] = NewtonRaphsonRootFind(bezCurve, d[i], u[i - first]);
}
return uPrime;
}
/*
* NewtonRaphsonRootFind:
* Use Newton-Raphson iteration to find better root.
* BezierCurve Q; Current fitted curve
* Vector2 P; Digitized point
* double u; Parameter value for "P"
*/
static double NewtonRaphsonRootFind(BezierCurve Q, Vector2 P, double u)
{
double numerator, denominator;
Vector2 Q1[3], Q2[2]; /* Q' and Q'' */
Vector2 Q_u, Q1_u, Q2_u; /* u evaluated at Q, Q', & Q'' */
double uPrime; /* Improved u */
int i;
/* Compute Q(u) */
Q_u = BezierII(3, Q, u);
/* Generate control vertices for Q' */
for (i = 0; i <= 2; i++) {
Q1[i][0] = (Q[i + 1][0] - Q[i][0]) * 3.0;
Q1[i][1] = (Q[i + 1][1] - Q[i][1]) * 3.0;
}
/* Generate control vertices for Q'' */
for (i = 0; i <= 1; i++) {
Q2[i][0] = (Q1[i + 1][0] - Q1[i][0]) * 2.0;
Q2[i][1] = (Q1[i + 1][1] - Q1[i][1]) * 2.0;
}
/* Compute Q'(u) and Q''(u) */
Q1_u = BezierII(2, Q1, u);
Q2_u = BezierII(1, Q2, u);
/* Compute f(u)/f'(u) */
numerator = (Q_u[0] - P[0]) * (Q1_u[0]) + (Q_u[1] - P[1]) * (Q1_u[1]);
denominator = (Q1_u[0]) * (Q1_u[0]) + (Q1_u[1]) * (Q1_u[1]) + (Q_u[0] - P[0]) * (Q2_u[0]) +
(Q_u[1] - P[1]) * (Q2_u[1]);
/* u = u - f(u)/f'(u) */
if (denominator == 0) { // FIXME
return u;
}
uPrime = u - (numerator / denominator);
return uPrime;
}
/*
* Bezier:
* Evaluate a Bezier curve at a particular parameter value
* int degree; The degree of the bezier curve
* Vector2 *V; Array of control points
* double t; Parametric value to find point for
*/
static Vector2 BezierII(int degree, Vector2 *V, double t)
{
int i, j;
Vector2 Q; /* Point on curve at parameter t */
Vector2 *Vtemp; /* Local copy of control points */
/* Copy array */
Vtemp = (Vector2 *)malloc(uint((degree + 1) * sizeof(Vector2)));
for (i = 0; i <= degree; i++) {
Vtemp[i] = V[i];
}
/* Triangle computation */
for (i = 1; i <= degree; i++) {
for (j = 0; j <= degree - i; j++) {
Vtemp[j][0] = (1.0 - t) * Vtemp[j][0] + t * Vtemp[j + 1][0];
Vtemp[j][1] = (1.0 - t) * Vtemp[j][1] + t * Vtemp[j + 1][1];
}
}
Q = Vtemp[0];
free((void *)Vtemp);
return Q;
}
/*
* B0, B1, B2, B3:
* Bezier multipliers
*/
static double B0(double u)
{
double tmp = 1.0 - u;
return (tmp * tmp * tmp);
}
static double B1(double u)
{
double tmp = 1.0 - u;
return (3 * u * (tmp * tmp));
}
static double B2(double u)
{
double tmp = 1.0 - u;
return (3 * u * u * tmp);
}
static double B3(double u)
{
return (u * u * u);
}
/*
* ComputeLeftTangent, ComputeRightTangent, ComputeCenterTangent:
* Approximate unit tangents at endpoints and "center" of digitized curve
*/
/* Vector2 *d; Digitized points
* int end; Index to "left" end of region
*/
static Vector2 ComputeLeftTangent(Vector2 *d, int end)
{
Vector2 tHat1;
tHat1 = V2SubII(d[end + 1], d[end]);
tHat1 = *V2Normalize(&tHat1);
return tHat1;
}
/* Vector2 *d; Digitized points
* int end; Index to "right" end of region
*/
static Vector2 ComputeRightTangent(Vector2 *d, int end)
{
Vector2 tHat2;
tHat2 = V2SubII(d[end - 1], d[end]);
tHat2 = *V2Normalize(&tHat2);
return tHat2;
}
/* Vector2 *d; Digitized points
* int end; Index to point inside region
*/
static Vector2 ComputeCenterTangent(Vector2 *d, int center)
{
Vector2 V1, V2, tHatCenter;
V1 = V2SubII(d[center - 1], d[center]);
V2 = V2SubII(d[center], d[center + 1]);
tHatCenter[0] = (V1[0] + V2[0]) / 2.0;
tHatCenter[1] = (V1[1] + V2[1]) / 2.0;
tHatCenter = *V2Normalize(&tHatCenter);
/* avoid numerical singularity in the special case when V1 == -V2 */
if (V2Length(&tHatCenter) < M_EPSILON) {
tHatCenter = *V2Normalize(&V1);
}
return tHatCenter;
}
/*
* ChordLengthParameterize:
* Assign parameter values to digitized points using relative distances between points.
* Vector2 *d; Array of digitized points
* int first, last; Indices defining region
*/
static double *ChordLengthParameterize(Vector2 *d, int first, int last)
{
int i;
double *u; /* Parameterization */
u = (double *)malloc(uint(last - first + 1) * sizeof(double));
u[0] = 0.0;
for (i = first + 1; i <= last; i++) {
u[i - first] = u[i - first - 1] + V2DistanceBetween2Points(&d[i], &d[i - 1]);
}
for (i = first + 1; i <= last; i++) {
u[i - first] = u[i - first] / u[last - first];
}
return u;
}
/*
* ComputeMaxError :
* Find the maximum squared distance of digitized points to fitted curve.
* Vector2 *d; Array of digitized points
* int first, last; Indices defining region
* BezierCurve bezCurve; Fitted Bezier curve
* double *u; Parameterization of points
* int *splitPoint; Point of maximum error
*/
static double ComputeMaxError(
Vector2 *d, int first, int last, BezierCurve bezCurve, double *u, int *splitPoint)
{
int i;
double maxDist; /* Maximum error */
double dist; /* Current error */
Vector2 P; /* Point on curve */
Vector2 v; /* Vector from point to curve */
*splitPoint = (last - first + 1) / 2;
maxDist = 0.0;
for (i = first + 1; i < last; i++) {
P = BezierII(3, bezCurve, u[i - first]);
v = V2SubII(P, d[i]);
dist = V2SquaredLength(&v);
if (dist >= maxDist) {
maxDist = dist;
*splitPoint = i;
}
}
return maxDist;
}
static Vector2 V2AddII(Vector2 a, Vector2 b)
{
Vector2 c;
c[0] = a[0] + b[0];
c[1] = a[1] + b[1];
return c;
}
static Vector2 V2ScaleIII(Vector2 v, double s)
{
Vector2 result;
result[0] = v[0] * s;
result[1] = v[1] * s;
return result;
}
static Vector2 V2SubII(Vector2 a, Vector2 b)
{
Vector2 c;
c[0] = a[0] - b[0];
c[1] = a[1] - b[1];
return c;
}
//------------------------- WRAPPER -----------------------------//
FitCurveWrapper::~FitCurveWrapper()
{
_vertices.clear();
}
void FitCurveWrapper::DrawBezierCurve(int n, Vector2 *curve)
{
for (int i = 0; i <= n; ++i) {
_vertices.push_back(curve[i]);
}
}
void FitCurveWrapper::FitCurve(vector<Vec2d> &data, vector<Vec2d> &oCurve, double error)
{
int size = data.size();
Vector2 *d = new Vector2[size];
for (int i = 0; i < size; ++i) {
d[i][0] = data[i][0];
d[i][1] = data[i][1];
}
FitCurve(d, size, error);
delete[] d;
// copy results
for (vector<Vector2>::iterator v = _vertices.begin(), vend = _vertices.end(); v != vend; ++v) {
oCurve.emplace_back(v->x(), v->y());
}
}
void FitCurveWrapper::FitCurve(Vector2 *d, int nPts, double error)
{
Vector2 tHat1, tHat2; /* Unit tangent vectors at endpoints */
tHat1 = ComputeLeftTangent(d, 0);
tHat2 = ComputeRightTangent(d, nPts - 1);
FitCubic(d, 0, nPts - 1, tHat1, tHat2, error);
}
void FitCurveWrapper::FitCubic(
Vector2 *d, int first, int last, Vector2 tHat1, Vector2 tHat2, double error)
{
BezierCurve bezCurve; /* Control points of fitted Bezier curve */
double *u; /* Parameter values for point */
double *uPrime; /* Improved parameter values */
double maxError; /* Maximum fitting error */
int splitPoint; /* Point to split point set at */
int nPts; /* Number of points in subset */
double iterationError; /* Error below which you try iterating */
int maxIterations = 4; /* Max times to try iterating */
Vector2 tHatCenter; /* Unit tangent vector at splitPoint */
int i;
iterationError = error * error;
nPts = last - first + 1;
/* Use heuristic if region only has two points in it */
if (nPts == 2) {
double dist = V2DistanceBetween2Points(&d[last], &d[first]) / 3.0;
bezCurve = (Vector2 *)malloc(4 * sizeof(Vector2));
bezCurve[0] = d[first];
bezCurve[3] = d[last];
V2Add(&bezCurve[0], V2Scale(&tHat1, dist), &bezCurve[1]);
V2Add(&bezCurve[3], V2Scale(&tHat2, dist), &bezCurve[2]);
DrawBezierCurve(3, bezCurve);
free((void *)bezCurve);
return;
}
/* Parameterize points, and attempt to fit curve */
u = ChordLengthParameterize(d, first, last);
bezCurve = GenerateBezier(d, first, last, u, tHat1, tHat2);
/* Find max deviation of points to fitted curve */
maxError = ComputeMaxError(d, first, last, bezCurve, u, &splitPoint);
if (maxError < error) {
DrawBezierCurve(3, bezCurve);
free((void *)u);
free((void *)bezCurve);
return;
}
/* If error not too large, try some reparameterization and iteration */
if (maxError < iterationError) {
for (i = 0; i < maxIterations; i++) {
uPrime = Reparameterize(d, first, last, u, bezCurve);
free((void *)u);
free((void *)bezCurve);
u = uPrime;
bezCurve = GenerateBezier(d, first, last, u, tHat1, tHat2);
maxError = ComputeMaxError(d, first, last, bezCurve, u, &splitPoint);
if (maxError < error) {
DrawBezierCurve(3, bezCurve);
free((void *)u);
free((void *)bezCurve);
return;
}
}
}
/* Fitting failed -- split at max error point and fit recursively */
free((void *)u);
free((void *)bezCurve);
tHatCenter = ComputeCenterTangent(d, splitPoint);
FitCubic(d, first, splitPoint, tHat1, tHatCenter, error);
V2Negate(&tHatCenter);
FitCubic(d, splitPoint, last, tHatCenter, tHat2, error);
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,97 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief An Algorithm for Automatically Fitting Digitized Curves by Philip J. Schneider,
* \brief from "Graphics Gems", Academic Press, 1990
*/
#include <vector>
#include "Geom.h"
#include "../system/FreestyleConfig.h"
namespace Freestyle {
using namespace Geometry;
/* 2d point */
struct Point2 {
double coordinates[2];
Point2()
{
coordinates[0] = 0;
coordinates[1] = 0;
}
inline double operator[](const int i) const
{
return coordinates[i];
}
inline double &operator[](const int i)
{
return coordinates[i];
}
inline double x() const
{
return coordinates[0];
}
inline double y() const
{
return coordinates[1];
}
};
using Vector2 = Point2;
class FitCurveWrapper {
private:
std::vector<Vector2> _vertices;
public:
~FitCurveWrapper();
/** Fits a set of 2D data points to a set of Bezier Curve segments
* data
* Input data points
* oCurve
* Control points of the sets of bezier curve segments.
* Each segment is made of 4 points (polynomial degree of curve = 3)
* error
* max error tolerance between resulting curve and input data
*/
void FitCurve(std::vector<Vec2d> &data, std::vector<Vec2d> &oCurve, double error);
protected:
/* Vec2d *d; Array of digitized points
* int nPts; Number of digitized points
* double error; User-defined error squared
*/
void FitCurve(Vector2 *d, int nPts, double error);
/** Draws a Bezier curve segment
* n
* degree of curve (=3)
* curve
* bezier segments control points
*/
void DrawBezierCurve(int n, Vector2 *curve);
/* Vec2d *d; Array of digitized points
* int first, last; Indices of first and last pts in region
* Vec2d tHat1, tHat2; Unit tangent vectors at endpoints
* double error; User-defined error squared
*/
void FitCubic(Vector2 *d, int first, int last, Vector2 tHat1, Vector2 tHat2, double error);
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,58 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Vectors and Matrices (useful type definitions)
*/
#include "VecMat.h"
#include "../system/Precision.h"
namespace Freestyle {
namespace Geometry {
typedef VecMat::Vec2<uint> Vec2u;
typedef VecMat::Vec2<int> Vec2i;
typedef VecMat::Vec2<float> Vec2f;
typedef VecMat::Vec2<double> Vec2d;
typedef VecMat::Vec2<real> Vec2r;
typedef VecMat::Vec3<uint> Vec3u;
typedef VecMat::Vec3<int> Vec3i;
typedef VecMat::Vec3<float> Vec3f;
typedef VecMat::Vec3<double> Vec3d;
typedef VecMat::Vec3<real> Vec3r;
typedef VecMat::HVec3<uint> HVec3u;
typedef VecMat::HVec3<int> HVec3i;
typedef VecMat::HVec3<float> HVec3f;
typedef VecMat::HVec3<double> HVec3d;
typedef VecMat::HVec3<real> HVec3r;
typedef VecMat::SquareMatrix<uint, 2> Matrix22u;
typedef VecMat::SquareMatrix<int, 2> Matrix22i;
typedef VecMat::SquareMatrix<float, 2> Matrix22f;
typedef VecMat::SquareMatrix<double, 2> Matrix22d;
typedef VecMat::SquareMatrix<real, 2> Matrix22r;
typedef VecMat::SquareMatrix<uint, 3> Matrix33u;
typedef VecMat::SquareMatrix<int, 3> Matrix33i;
typedef VecMat::SquareMatrix<float, 3> Matrix33f;
typedef VecMat::SquareMatrix<double, 3> Matrix33d;
typedef VecMat::SquareMatrix<real, 3> Matrix33r;
typedef VecMat::SquareMatrix<uint, 4> Matrix44u;
typedef VecMat::SquareMatrix<int, 4> Matrix44i;
typedef VecMat::SquareMatrix<float, 4> Matrix44f;
typedef VecMat::SquareMatrix<double, 4> Matrix44d;
typedef VecMat::SquareMatrix<real, 4> Matrix44r;
} // end of namespace Geometry
} /* namespace Freestyle */

View File

@@ -0,0 +1,243 @@
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define a cleaner of geometry providing a set of useful tools
*/
#if 0
# if defined(__GNUC__) && (__GNUC__ >= 3)
// hash_map is not part of the C++ standard anymore;
// hash_map.h has been kept though for backward compatibility
# include <hash_map.h>
# else
# include <hash_map>
# endif
#endif
#include <cstdio>
#include <list>
#include <map>
#include "GeomCleaner.h"
#include "../system/TimeUtils.h"
#include "BKE_global.hh"
#include "BLI_sys_types.h"
using namespace std;
namespace Freestyle {
void GeomCleaner::SortIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint **oIndices)
{
// First, we build a list of IndexVertex:
list<IndexedVertex> indexedVertices;
uint i;
for (i = 0; i < iVSize; i += 3) {
indexedVertices.emplace_back(Vec3f(iVertices[i], iVertices[i + 1], iVertices[i + 2]), i / 3);
}
// q-sort
indexedVertices.sort();
// build the indices mapping array:
uint *mapIndices = new uint[iVSize / 3];
*oVertices = new float[iVSize];
list<IndexedVertex>::iterator iv;
uint newIndex = 0;
uint vIndex = 0;
for (iv = indexedVertices.begin(); iv != indexedVertices.end(); iv++) {
// Build the final results:
(*oVertices)[vIndex] = iv->x();
(*oVertices)[vIndex + 1] = iv->y();
(*oVertices)[vIndex + 2] = iv->z();
mapIndices[iv->index()] = newIndex;
newIndex++;
vIndex += 3;
}
// Build the final index array:
*oIndices = new uint[iISize];
for (i = 0; i < iISize; i++) {
(*oIndices)[i] = 3 * mapIndices[iIndices[i] / 3];
}
delete[] mapIndices;
}
void GeomCleaner::CompressIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices)
{
// First, we build a list of IndexVertex:
vector<Vec3f> vertices;
uint i;
for (i = 0; i < iVSize; i += 3) {
vertices.emplace_back(iVertices[i], iVertices[i + 1], iVertices[i + 2]);
}
uint *mapVertex = new uint[iVSize];
vector<Vec3f>::iterator v = vertices.begin();
vector<Vec3f> compressedVertices;
Vec3f previous = *v;
mapVertex[0] = 0;
compressedVertices.push_back(vertices.front());
v++;
Vec3f current;
i = 1;
for (; v != vertices.end(); v++) {
current = *v;
if (current == previous) {
mapVertex[i] = compressedVertices.size() - 1;
}
else {
compressedVertices.push_back(current);
mapVertex[i] = compressedVertices.size() - 1;
}
previous = current;
i++;
}
// Builds the resulting vertex array:
*oVSize = 3 * compressedVertices.size();
*oVertices = new float[*oVSize];
i = 0;
for (v = compressedVertices.begin(); v != compressedVertices.end(); v++) {
(*oVertices)[i] = (*v)[0];
(*oVertices)[i + 1] = (*v)[1];
(*oVertices)[i + 2] = (*v)[2];
i += 3;
}
// Map the index array:
*oIndices = new uint[iISize];
for (i = 0; i < iISize; i++) {
(*oIndices)[i] = 3 * mapVertex[iIndices[i] / 3];
}
delete[] mapVertex;
}
void GeomCleaner::SortAndCompressIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices)
{
// tmp arrays used to store the sorted data:
float *tmpVertices;
uint *tmpIndices;
Chronometer chrono;
// Sort data
chrono.start();
GeomCleaner::SortIndexedVertexArray(
iVertices, iVSize, iIndices, iISize, &tmpVertices, &tmpIndices);
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
printf("Sorting: %lf sec.\n", chrono.stop());
}
// compress data
chrono.start();
GeomCleaner::CompressIndexedVertexArray(
tmpVertices, iVSize, tmpIndices, iISize, oVertices, oVSize, oIndices);
real duration = chrono.stop();
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
printf("Merging: %lf sec.\n", duration);
}
// deallocates memory:
delete[] tmpVertices;
delete[] tmpIndices;
}
/** Defines a hash table used for searching the Cells */
struct GeomCleanerHasher {
#define _MUL 950706376UL
#define _MOD 2147483647UL
inline size_t operator()(const Vec3r &p) const
{
size_t res = ulong(p[0] * _MUL) % _MOD;
res = (res + ulong(p[1]) * _MUL) % _MOD;
return (res + ulong(p[2]) * _MUL) % _MOD;
}
#undef _MUL
#undef _MOD
};
void GeomCleaner::CleanIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices)
{
using cleanHashTable = map<Vec3f, uint>;
vector<Vec3f> vertices;
uint i;
for (i = 0; i < iVSize; i += 3) {
vertices.emplace_back(iVertices[i], iVertices[i + 1], iVertices[i + 2]);
}
cleanHashTable ht;
vector<uint> newIndices;
vector<Vec3f> newVertices;
// elimination of needless points
uint currentIndex = 0;
vector<Vec3f>::const_iterator v = vertices.begin();
vector<Vec3f>::const_iterator end = vertices.end();
cleanHashTable::const_iterator found;
for (; v != end; v++) {
found = ht.find(*v);
if (found != ht.end()) {
// The vertex is already in the new array.
newIndices.push_back((*found).second);
}
else {
newVertices.push_back(*v);
newIndices.push_back(currentIndex);
ht[*v] = currentIndex;
currentIndex++;
}
}
// creation of oVertices array:
*oVSize = 3 * newVertices.size();
*oVertices = new float[*oVSize];
currentIndex = 0;
end = newVertices.end();
for (v = newVertices.begin(); v != end; v++) {
(*oVertices)[currentIndex++] = (*v)[0];
(*oVertices)[currentIndex++] = (*v)[1];
(*oVertices)[currentIndex++] = (*v)[2];
}
// map new indices:
*oIndices = new uint[iISize];
for (i = 0; i < iISize; i++) {
(*oIndices)[i] = 3 * newIndices[iIndices[i] / 3];
}
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,234 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a cleaner of geometry providing a set of useful tools
*/
#include "Geom.h"
#include "../system/FreestyleConfig.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
using namespace Geometry;
class GeomCleaner {
public:
inline GeomCleaner() {}
inline ~GeomCleaner() {}
/** Sorts an array of Indexed vertices
* iVertices
* Array of vertices to sort.
* It is organized as a float series of vertex coordinates: XYZXYZXYZ...
* iVSize
* The size of iVertices array.
* iIndices
* The array containing the vertex indices
* (used to refer to the vertex coordinates in an indexed face).
* Each element is a uint multiple of 3.
* iISize
* The size of iIndices array
* oVertices
* Output of sorted vertices.
* A vertex v1 precedes another one v2 in this array
* if v1.x<v2.x, or v1.x=v2.x && v1.y < v2.y or v1.x=v2.y && v1.y=v2.y && v1.z < v2.z.
* The array is organized as a 3-float series giving the vertices coordinates: XYZXYZXYZ...
* oIndices
* Output corresponding to the iIndices array but reorganized in
* order to match the sorted vertex array.
*/
static void SortIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint **oIndices);
/** Compress a SORTED indexed vertex array by eliminating multiple
* appearing occurrences of a single vertex.
* iVertices
* The SORTED vertex array to compress.
* It is organized as a float series of vertex coordinates: XYZXYZXYZ...
* iVSize
* The size of iVertices array.
* iIndices
* The array containing the vertex indices
* (used to refer to the vertex coordinates in an indexed face).
* Each element is a uint multiple of 3.
* iISize
* The size of iIndices array
* oVertices
* The vertex array, result of the compression.
* The array is organized as a 3-float series giving the vertices coordinates: XYZXYZXYZ...
* oVSize
* The size of oVertices.
* oIndices
* The indices array, reorganized to match the compressed oVertices array.
*/
static void CompressIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices);
/** Sorts and compress an array of indexed vertices.
* iVertices
* The vertex array to sort then compress. It is organized as a float series of
* vertex coordinates: XYZXYZXYZ...
* iVSize
* The size of iVertices array.
* iIndices
* The array containing the vertex indices
* (used to refer to the vertex coordinates in an indexed face).
* Each element is a uint multiple of 3.
* iISize
* The size of iIndices array
* oVertices
* The vertex array, result of the sorting-compression.
* The array is organized as a 3-float series giving the vertices coordinates: XYZXYZXYZ...
* oVSize
* The size of oVertices.
* oIndices
* The indices array, reorganized to match the sorted and compressed oVertices array.
*/
static void SortAndCompressIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices);
/** Cleans an indexed vertex array.
* (Identical to SortAndCompress except that we use here a hash table to create the new array.)
* iVertices
* The vertex array to sort then compress. It is organized as a float series of
* vertex coordinates: XYZXYZXYZ...
* iVSize
* The size of iVertices array.
* iIndices
* The array containing the vertex indices
* (used to refer to the vertex coordinates in an indexed face).
* Each element is a uint multiple of 3.
* iISize
* The size of iIndices array
* oVertices
* The vertex array, result of the sorting-compression.
* The array is organized as a 3-float series giving the vertices coordinates: XYZXYZXYZ...
* oVSize
* The size of oVertices.
* oIndices
* The indices array, reorganized to match the sorted and compressed oVertices array.
*/
static void CleanIndexedVertexArray(const float *iVertices,
uint iVSize,
const uint *iIndices,
uint iISize,
float **oVertices,
uint *oVSize,
uint **oIndices);
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:GeomCleaner")
};
/** Binary operators */
// inline bool operator<(const IndexedVertex& iv1, const IndexedVertex& iv2);
/** Class Indexed Vertex. Used to represent an indexed vertex by storing the vertex coordinates as
* well as its index */
class IndexedVertex {
private:
Vec3f _Vector;
uint _index;
public:
inline IndexedVertex() {}
inline IndexedVertex(Vec3f iVector, uint iIndex)
{
_Vector = iVector;
_index = iIndex;
}
/** accessors */
inline const Vec3f &vector() const
{
return _Vector;
}
inline uint index()
{
return _index;
}
inline float x()
{
return _Vector[0];
}
inline float y()
{
return _Vector[1];
}
inline float z()
{
return _Vector[2];
}
/** modifiers */
inline void setVector(const Vec3f &iVector)
{
_Vector = iVector;
}
inline void setIndex(uint iIndex)
{
_index = iIndex;
}
/** operators */
IndexedVertex &operator=(const IndexedVertex &iv)
{
_Vector = iv._Vector;
_index = iv._index;
return *this;
}
inline float operator[](const uint i)
{
return _Vector[i];
}
// friend inline bool operator<(const IndexedVertex& iv1, const IndexedVertex& iv2);
inline bool operator<(const IndexedVertex &v) const
{
return (_Vector < v._Vector);
}
inline bool operator==(const IndexedVertex &v)
{
return (_Vector == v._Vector);
}
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:IndexedVertex")
};
#if 0
bool operator<(const IndexedVertex &iv1, const IndexedVertex &iv2)
{
return iv1.operator<(iv2);
}
#endif
} /* namespace Freestyle */

View File

@@ -0,0 +1,840 @@
/* SPDX-FileCopyrightText: 2009-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Various tools for geometry
*/
#include "GeomUtils.h"
#include "BLI_sys_types.h"
namespace Freestyle::GeomUtils {
// This internal procedure is defined below.
bool intersect2dSegPoly(Vec2r *seg, Vec2r *poly, uint n);
bool intersect2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, const Vec2r &B)
{
Vec2r seg[2];
seg[0] = A;
seg[1] = B;
Vec2r poly[5];
poly[0][0] = min[0];
poly[0][1] = min[1];
poly[1][0] = max[0];
poly[1][1] = min[1];
poly[2][0] = max[0];
poly[2][1] = max[1];
poly[3][0] = min[0];
poly[3][1] = max[1];
poly[4][0] = min[0];
poly[4][1] = min[1];
return intersect2dSegPoly(seg, poly, 4);
}
bool include2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, const Vec2r &B)
{
if ((((max[0] > A[0]) && (A[0] > min[0])) && ((max[0] > B[0]) && (B[0] > min[0]))) &&
(((max[1] > A[1]) && (A[1] > min[1])) && ((max[1] > B[1]) && (B[1] > min[1]))))
{
return true;
}
return false;
}
intersection_test intersect2dSeg2dSeg(
const Vec2r &p1, const Vec2r &p2, const Vec2r &p3, const Vec2r &p4, Vec2r &res)
{
real a1, a2, b1, b2, c1, c2; // Coefficients of line eqns
real r1, r2, r3, r4; // 'Sign' values
real denom, num; // Intermediate values
// Compute a1, b1, c1, where line joining points p1 and p2 is "a1 x + b1 y + c1 = 0".
a1 = p2[1] - p1[1];
b1 = p1[0] - p2[0];
c1 = p2[0] * p1[1] - p1[0] * p2[1];
// Compute r3 and r4.
r3 = a1 * p3[0] + b1 * p3[1] + c1;
r4 = a1 * p4[0] + b1 * p4[1] + c1;
// Check signs of r3 and r4. If both point 3 and point 4 lie on same side of line 1,
// the line segments do not intersect.
if (r3 != 0 && r4 != 0 && r3 * r4 > 0.0) {
return DONT_INTERSECT;
}
// Compute a2, b2, c2
a2 = p4[1] - p3[1];
b2 = p3[0] - p4[0];
c2 = p4[0] * p3[1] - p3[0] * p4[1];
// Compute r1 and r2
r1 = a2 * p1[0] + b2 * p1[1] + c2;
r2 = a2 * p2[0] + b2 * p2[1] + c2;
// Check signs of r1 and r2. If both point 1 and point 2 lie on same side of second line
// segment, the line segments do not intersect.
if (r1 != 0 && r2 != 0 && r1 * r2 > 0.0) {
return DONT_INTERSECT;
}
// Line segments intersect: compute intersection point.
denom = a1 * b2 - a2 * b1;
if (fabs(denom) < M_EPSILON) {
return COLINEAR;
}
num = b1 * c2 - b2 * c1;
res[0] = num / denom;
num = a2 * c1 - a1 * c2;
res[1] = num / denom;
return DO_INTERSECT;
}
intersection_test intersect2dLine2dLine(
const Vec2r &p1, const Vec2r &p2, const Vec2r &p3, const Vec2r &p4, Vec2r &res)
{
real a1, a2, b1, b2, c1, c2; // Coefficients of line eqns
real denom, num; // Intermediate values
// Compute a1, b1, c1, where line joining points p1 and p2 is "a1 x + b1 y + c1 = 0".
a1 = p2[1] - p1[1];
b1 = p1[0] - p2[0];
c1 = p2[0] * p1[1] - p1[0] * p2[1];
// Compute a2, b2, c2
a2 = p4[1] - p3[1];
b2 = p3[0] - p4[0];
c2 = p4[0] * p3[1] - p3[0] * p4[1];
// Line segments intersect: compute intersection point.
denom = a1 * b2 - a2 * b1;
if (fabs(denom) < M_EPSILON) {
return COLINEAR;
}
num = b1 * c2 - b2 * c1;
res[0] = num / denom;
num = a2 * c1 - a1 * c2;
res[1] = num / denom;
return DO_INTERSECT;
}
intersection_test intersect2dSeg2dSegParametric(const Vec2r &p1,
const Vec2r &p2,
const Vec2r &p3,
const Vec2r &p4,
real &t,
real &u,
real epsilon)
{
real a1, a2, b1, b2, c1, c2; // Coefficients of line eqns
real r1, r2, r3, r4; // 'Sign' values
real denom, num; // Intermediate values
// Compute a1, b1, c1, where line joining points p1 and p2 is "a1 x + b1 y + c1 = 0".
a1 = p2[1] - p1[1];
b1 = p1[0] - p2[0];
c1 = p2[0] * p1[1] - p1[0] * p2[1];
// Compute r3 and r4.
r3 = a1 * p3[0] + b1 * p3[1] + c1;
r4 = a1 * p4[0] + b1 * p4[1] + c1;
// Check signs of r3 and r4. If both point 3 and point 4 lie on same side of line 1,
// the line segments do not intersect.
if (r3 != 0 && r4 != 0 && r3 * r4 > 0.0) {
return DONT_INTERSECT;
}
// Compute a2, b2, c2
a2 = p4[1] - p3[1];
b2 = p3[0] - p4[0];
c2 = p4[0] * p3[1] - p3[0] * p4[1];
// Compute r1 and r2
r1 = a2 * p1[0] + b2 * p1[1] + c2;
r2 = a2 * p2[0] + b2 * p2[1] + c2;
// Check signs of r1 and r2. If both point 1 and point 2 lie on same side of second line
// segment, the line segments do not intersect.
if (r1 != 0 && r2 != 0 && r1 * r2 > 0.0) {
return DONT_INTERSECT;
}
// Line segments intersect: compute intersection point.
denom = a1 * b2 - a2 * b1;
if (fabs(denom) < epsilon) {
return COLINEAR;
}
real d1, e1;
d1 = p1[1] - p3[1];
e1 = p1[0] - p3[0];
num = -b2 * d1 - a2 * e1;
t = num / denom;
num = -b1 * d1 - a1 * e1;
u = num / denom;
return DO_INTERSECT;
}
// AABB-triangle overlap test code by Tomas Akenine-Möller
// Function: int triBoxOverlap(real boxcenter[3], real boxhalfsize[3],real triverts[3][3]);
// History:
// 2001-03-05: released the code in its first version
// 2001-06-18: changed the order of the tests, faster
//
// Acknowledgement: Many thanks to Pierre Terdiman for suggestions and discussions on how to
// optimize code. Thanks to David Hunt for finding a ">="-bug!
#define X 0
#define Y 1
#define Z 2
#define FINDMINMAX(x0, x1, x2, min, max) \
{ \
min = max = x0; \
if (x1 < min) { \
min = x1; \
} \
if (x1 > max) { \
max = x1; \
} \
if (x2 < min) { \
min = x2; \
} \
if (x2 > max) { \
max = x2; \
} \
} \
(void)0
//======================== X-tests ========================//
#define AXISTEST_X01(a, b, fa, fb) \
{ \
p0 = a * v0[Y] - b * v0[Z]; \
p2 = a * v2[Y] - b * v2[Z]; \
if (p0 < p2) { \
min = p0; \
max = p2; \
} \
else { \
min = p2; \
max = p0; \
} \
rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
#define AXISTEST_X2(a, b, fa, fb) \
{ \
p0 = a * v0[Y] - b * v0[Z]; \
p1 = a * v1[Y] - b * v1[Z]; \
if (p0 < p1) { \
min = p0; \
max = p1; \
} \
else { \
min = p1; \
max = p0; \
} \
rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
//======================== Y-tests ========================//
#define AXISTEST_Y02(a, b, fa, fb) \
{ \
p0 = -a * v0[X] + b * v0[Z]; \
p2 = -a * v2[X] + b * v2[Z]; \
if (p0 < p2) { \
min = p0; \
max = p2; \
} \
else { \
min = p2; \
max = p0; \
} \
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
#define AXISTEST_Y1(a, b, fa, fb) \
{ \
p0 = -a * v0[X] + b * v0[Z]; \
p1 = -a * v1[X] + b * v1[Z]; \
if (p0 < p1) { \
min = p0; \
max = p1; \
} \
else { \
min = p1; \
max = p0; \
} \
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
//======================== Z-tests ========================//
#define AXISTEST_Z12(a, b, fa, fb) \
{ \
p1 = a * v1[X] - b * v1[Y]; \
p2 = a * v2[X] - b * v2[Y]; \
if (p2 < p1) { \
min = p2; \
max = p1; \
} \
else { \
min = p1; \
max = p2; \
} \
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
#define AXISTEST_Z0(a, b, fa, fb) \
{ \
p0 = a * v0[X] - b * v0[Y]; \
p1 = a * v1[X] - b * v1[Y]; \
if (p0 < p1) { \
min = p0; \
max = p1; \
} \
else { \
min = p1; \
max = p0; \
} \
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \
if (min > rad || max < -rad) { \
return 0; \
} \
} \
(void)0
// This internal procedure is defined below.
bool overlapPlaneBox(const Vec3r &normal, const real d, const Vec3r &maxbox);
bool overlapTriangleBox(const Vec3r &boxcenter, const Vec3r &boxhalfsize, const Vec3r triverts[3])
{
/* Use separating axis theorem to test overlap between triangle and box need to test for overlap
* in these directions:
*
* 1) The {x,y,z}-directions
* (actually, since we use the AABB of the triangle we do not even need to test these).
* 2) Normal of the triangle.
* 3) `crossproduct(edge from tri, {x,y,z}-directin)` this gives 3x3=9 more tests.
*
* Adapted from Tomas Akenine-Möller code. */
Vec3r v0, v1, v2, normal, e0, e1, e2;
real min, max, d, p0, p1, p2, rad, fex, fey, fez;
// This is the fastest branch on Sun
// move everything so that the boxcenter is in (0, 0, 0)
v0 = triverts[0] - boxcenter;
v1 = triverts[1] - boxcenter;
v2 = triverts[2] - boxcenter;
// compute triangle edges
e0 = v1 - v0;
e1 = v2 - v1;
e2 = v0 - v2;
// Bullet 3:
// Do the 9 tests first (this was faster)
fex = fabs(e0[X]);
fey = fabs(e0[Y]);
fez = fabs(e0[Z]);
AXISTEST_X01(e0[Z], e0[Y], fez, fey);
AXISTEST_Y02(e0[Z], e0[X], fez, fex);
AXISTEST_Z12(e0[Y], e0[X], fey, fex);
fex = fabs(e1[X]);
fey = fabs(e1[Y]);
fez = fabs(e1[Z]);
AXISTEST_X01(e1[Z], e1[Y], fez, fey);
AXISTEST_Y02(e1[Z], e1[X], fez, fex);
AXISTEST_Z0(e1[Y], e1[X], fey, fex);
fex = fabs(e2[X]);
fey = fabs(e2[Y]);
fez = fabs(e2[Z]);
AXISTEST_X2(e2[Z], e2[Y], fez, fey);
AXISTEST_Y1(e2[Z], e2[X], fez, fex);
AXISTEST_Z12(e2[Y], e2[X], fey, fex);
// Bullet 1:
// first test overlap in the {x,y,z}-directions
// find min, max of the triangle each direction, and test for overlap in that direction -- this
// is equivalent to testing a minimal AABB around the triangle against the AABB
// test in X-direction
FINDMINMAX(v0[X], v1[X], v2[X], min, max);
if (min > boxhalfsize[X] || max < -boxhalfsize[X]) {
return false;
}
// test in Y-direction
FINDMINMAX(v0[Y], v1[Y], v2[Y], min, max);
if (min > boxhalfsize[Y] || max < -boxhalfsize[Y]) {
return false;
}
// test in Z-direction
FINDMINMAX(v0[Z], v1[Z], v2[Z], min, max);
if (min > boxhalfsize[Z] || max < -boxhalfsize[Z]) {
return false;
}
// Bullet 2:
// test if the box intersects the plane of the triangle
// compute plane equation of triangle: normal * x + d = 0
normal = e0 ^ e1;
d = -(normal * v0); // plane eq: normal.x + d = 0
if (!overlapPlaneBox(normal, d, boxhalfsize)) {
return false;
}
return true; // box and triangle overlaps
}
bool intersectRayTriangle(const Vec3r &orig,
const Vec3r &dir,
const Vec3r &v0,
const Vec3r &v1,
const Vec3r &v2,
real &t,
real &u,
real &v,
const real epsilon)
{
/* Fast, Minimum Storage Ray-Triangle Intersection.
* Adapted from Tomas Möller and Ben Trumbore code.
*
* Tomas Möller, Prosolvia Clarus AB, Sweden, <tompa@clarus.se>.
* Ben Trumbore, Cornell University, Ithaca, New York <wbt@graphics.cornell.edu>. */
Vec3r edge1, edge2, tvec, pvec, qvec;
real det, inv_det;
// find vectors for two edges sharing v0
edge1 = v1 - v0;
edge2 = v2 - v0;
// begin calculating determinant - also used to calculate U parameter
pvec = dir ^ edge2;
// if determinant is near zero, ray lies in plane of triangle
det = edge1 * pvec;
// calculate distance from v0 to ray origin
tvec = orig - v0;
inv_det = 1.0 / det;
qvec = tvec ^ edge1;
if (det > epsilon) {
u = tvec * pvec;
if (u < 0.0 || u > det) {
return false;
}
// calculate V parameter and test bounds
v = dir * qvec;
if (v < 0.0 || u + v > det) {
return false;
}
}
else if (det < -epsilon) {
// calculate U parameter and test bounds
u = tvec * pvec;
if (u > 0.0 || u < det) {
return false;
}
// calculate V parameter and test bounds
v = dir * qvec;
if (v > 0.0 || u + v < det) {
return false;
}
}
else {
return false; // ray is parallel to the plane of the triangle
}
u *= inv_det;
v *= inv_det;
t = (edge2 * qvec) * inv_det;
return true;
}
intersection_test intersectRayPlane(const Vec3r &orig,
const Vec3r &dir,
const Vec3r &norm,
const real d,
real &t,
const real epsilon)
{
/* Intersection between plane and ray, adapted from Graphics Gems, Didier Badouel
* The plane is represented by a set of points P implicitly defined as `dot(norm, P) + d = 0`.
* The ray is represented as `r(t) = orig + dir * t`. */
real denom = norm * dir;
if (fabs(denom) <= epsilon) { // plane and ray are parallel
if (fabs((norm * orig) + d) <= epsilon) {
return COINCIDENT; // plane and ray are coincident
}
return COLINEAR;
}
t = -(d + (norm * orig)) / denom;
if (t < 0.0f) {
return DONT_INTERSECT;
}
return DO_INTERSECT;
}
bool intersectRayBBox(const Vec3r &orig,
const Vec3r &dir, // ray origin and direction
const Vec3r &boxMin,
const Vec3r &boxMax, // the bbox
real t0,
real t1,
real &tmin, // I0 = orig + tmin * dir is the first intersection
real &tmax, // I1 = orig + tmax * dir is the second intersection
real /*epsilon*/)
{
float tymin, tymax, tzmin, tzmax;
Vec3r inv_direction(1.0 / dir[0], 1.0 / dir[1], 1.0 / dir[2]);
int sign[3];
sign[0] = (inv_direction.x() < 0);
sign[1] = (inv_direction.y() < 0);
sign[2] = (inv_direction.z() < 0);
Vec3r bounds[2];
bounds[0] = boxMin;
bounds[1] = boxMax;
tmin = (bounds[sign[0]].x() - orig.x()) * inv_direction.x();
tmax = (bounds[1 - sign[0]].x() - orig.x()) * inv_direction.x();
tymin = (bounds[sign[1]].y() - orig.y()) * inv_direction.y();
tymax = (bounds[1 - sign[1]].y() - orig.y()) * inv_direction.y();
if ((tmin > tymax) || (tymin > tmax)) {
return false;
}
if (tymin > tmin) {
tmin = tymin;
}
if (tymax < tmax) {
tmax = tymax;
}
tzmin = (bounds[sign[2]].z() - orig.z()) * inv_direction.z();
tzmax = (bounds[1 - sign[2]].z() - orig.z()) * inv_direction.z();
if ((tmin > tzmax) || (tzmin > tmax)) {
return false;
}
if (tzmin > tmin) {
tmin = tzmin;
}
if (tzmax < tmax) {
tmax = tzmax;
}
return ((tmin < t1) && (tmax > t0));
}
// Checks whether 3D points p lies inside or outside of the triangle ABC
bool includePointTriangle(const Vec3r &P, const Vec3r &A, const Vec3r &B, const Vec3r &C)
{
Vec3r AB(B - A);
Vec3r BC(C - B);
Vec3r CA(A - C);
Vec3r AP(P - A);
Vec3r BP(P - B);
Vec3r CP(P - C);
Vec3r N(AB ^ BC); // triangle's normal
N.normalize();
Vec3r J(AB ^ AP), K(BC ^ BP), L(CA ^ CP);
J.normalize();
K.normalize();
L.normalize();
if (J * N < 0) {
return false; // on the right of AB
}
if (K * N < 0) {
return false; // on the right of BC
}
if (L * N < 0) {
return false; // on the right of CA
}
return true;
}
void transformVertex(const Vec3r &vert, const Matrix44r &matrix, Vec3r &res)
{
HVec3r hvert(vert), res_tmp;
real scale;
for (uint j = 0; j < 4; j++) {
scale = hvert[j];
for (uint i = 0; i < 4; i++) {
res_tmp[i] += matrix(i, j) * scale;
}
}
res[0] = res_tmp.x();
res[1] = res_tmp.y();
res[2] = res_tmp.z();
}
void transformVertices(const vector<Vec3r> &vertices, const Matrix44r &trans, vector<Vec3r> &res)
{
size_t i;
res.resize(vertices.size());
for (i = 0; i < vertices.size(); i++) {
transformVertex(vertices[i], trans, res[i]);
}
}
Vec3r rotateVector(const Matrix44r &mat, const Vec3r &v)
{
Vec3r res;
for (uint i = 0; i < 3; i++) {
res[i] = 0;
for (uint j = 0; j < 3; j++) {
res[i] += mat(i, j) * v[j];
}
}
res.normalize();
return res;
}
// This internal procedure is defined below.
void fromCoordAToCoordB(const Vec3r &p, Vec3r &q, const real transform[4][4]);
void fromWorldToCamera(const Vec3r &p, Vec3r &q, const real model_view_matrix[4][4])
{
fromCoordAToCoordB(p, q, model_view_matrix);
}
void fromCameraToRetina(const Vec3r &p, Vec3r &q, const real projection_matrix[4][4])
{
fromCoordAToCoordB(p, q, projection_matrix);
}
void fromRetinaToImage(const Vec3r &p, Vec3r &q, const int viewport[4])
{
// winX:
q[0] = viewport[0] + viewport[2] * (p[0] + 1.0) / 2.0;
// winY:
q[1] = viewport[1] + viewport[3] * (p[1] + 1.0) / 2.0;
// winZ:
q[2] = (p[2] + 1.0) / 2.0;
}
void fromWorldToImage(const Vec3r &p,
Vec3r &q,
const real model_view_matrix[4][4],
const real projection_matrix[4][4],
const int viewport[4])
{
Vec3r p1, p2;
fromWorldToCamera(p, p1, model_view_matrix);
fromCameraToRetina(p1, p2, projection_matrix);
fromRetinaToImage(p2, q, viewport);
q[2] = p1[2];
}
void fromWorldToImage(const Vec3r &p, Vec3r &q, const real transform[4][4], const int viewport[4])
{
fromCoordAToCoordB(p, q, transform);
// winX:
q[0] = viewport[0] + viewport[2] * (q[0] + 1.0) / 2.0;
// winY:
q[1] = viewport[1] + viewport[3] * (q[1] + 1.0) / 2.0;
}
void fromImageToRetina(const Vec3r &p, Vec3r &q, const int viewport[4])
{
q = p;
q[0] = 2.0 * (q[0] - viewport[0]) / viewport[2] - 1.0;
q[1] = 2.0 * (q[1] - viewport[1]) / viewport[3] - 1.0;
}
void fromRetinaToCamera(const Vec3r &p, Vec3r &q, real focal, const real projection_matrix[4][4])
{
if (projection_matrix[3][3] == 0.0) { // perspective
q[0] = (-p[0] * focal) / projection_matrix[0][0];
q[1] = (-p[1] * focal) / projection_matrix[1][1];
q[2] = focal;
}
else { // orthogonal
q[0] = p[0] / projection_matrix[0][0];
q[1] = p[1] / projection_matrix[1][1];
q[2] = focal;
}
}
void fromCameraToWorld(const Vec3r &p, Vec3r &q, const real model_view_matrix[4][4])
{
const real translation[3] = {
model_view_matrix[0][3],
model_view_matrix[1][3],
model_view_matrix[2][3],
};
for (ushort i = 0; i < 3; i++) {
q[i] = 0.0;
for (ushort j = 0; j < 3; j++) {
q[i] += model_view_matrix[j][i] * (p[j] - translation[j]);
}
}
}
//
// Internal code
//
/////////////////////////////////////////////////////////////////////////////
// Copyright 2001, softSurfer (www.softsurfer.com)
// This code may be freely used and modified for any purpose providing that this copyright notice
// is included with it. SoftSurfer makes no warranty for this code, and cannot be held liable for
// any real or imagined damage resulting from its use. Users of this code must verify correctness
// for their application.
#define PERP(u, v) ((u)[0] * (v)[1] - (u)[1] * (v)[0]) // 2D perp product
inline bool intersect2dSegPoly(Vec2r *seg, Vec2r *poly, uint n)
{
if (seg[0] == seg[1]) {
return false;
}
real tE = 0; // the maximum entering segment parameter
real tL = 1; // the minimum leaving segment parameter
real t, N, D; // intersect parameter t = N / D
Vec2r dseg = seg[1] - seg[0]; // the segment direction vector
Vec2r e; // edge vector
for (uint i = 0; i < n; i++) { // process polygon edge poly[i]poly[i+1]
e = poly[i + 1] - poly[i];
N = PERP(e, seg[0] - poly[i]);
D = -PERP(e, dseg);
if (fabs(D) < M_EPSILON) {
if (N < 0) {
return false;
}
continue;
}
t = N / D;
if (D < 0) { // segment seg is entering across this edge
if (t > tE) { // new max tE
tE = t;
if (tE > tL) { // seg enters after leaving polygon
return false;
}
}
}
else { // segment seg is leaving across this edge
if (t < tL) { // new min tL
tL = t;
if (tL < tE) { // seg leaves before entering polygon
return false;
}
}
}
}
// tE <= tL implies that there is a valid intersection subsegment
return true;
}
inline bool overlapPlaneBox(const Vec3r &normal, const real d, const Vec3r &maxbox)
{
Vec3r vmin, vmax;
for (uint q = X; q <= Z; q++) {
if (normal[q] > 0.0f) {
vmin[q] = -maxbox[q];
vmax[q] = maxbox[q];
}
else {
vmin[q] = maxbox[q];
vmax[q] = -maxbox[q];
}
}
if ((normal * vmin) + d > 0.0f) {
return false;
}
if ((normal * vmax) + d >= 0.0f) {
return true;
}
return false;
}
inline void fromCoordAToCoordB(const Vec3r &p, Vec3r &q, const real transform[4][4])
{
HVec3r hp(p);
HVec3r hq(0, 0, 0, 0);
for (uint i = 0; i < 4; i++) {
for (uint j = 0; j < 4; j++) {
hq[i] += transform[i][j] * hp[j];
}
}
if (hq[3] == 0) {
q = p;
return;
}
for (uint k = 0; k < 3; k++) {
q[k] = hq[k] / hq[3];
}
}
} // namespace Freestyle::GeomUtils

View File

@@ -0,0 +1,254 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Various tools for geometry
*/
#include <vector>
#include "Geom.h"
#include "../system/FreestyleConfig.h"
using namespace std;
namespace Freestyle {
using namespace Geometry;
namespace GeomUtils {
//
// Templated procedures
//
/////////////////////////////////////////////////////////////////////////////
/** Computes the distance from a point P to a segment AB */
template<class T> real distPointSegment(const T &P, const T &A, const T &B)
{
T AB, AP, BP;
AB = B - A;
AP = P - A;
BP = P - B;
real c1(AB * AP);
if (c1 <= 0) {
return AP.norm();
}
real c2(AB * AB);
if (c2 <= c1) {
return BP.norm();
}
real b = c1 / c2;
T Pb, PPb;
Pb = A + b * AB;
PPb = P - Pb;
return PPb.norm();
}
//
// Non-templated procedures
//
/////////////////////////////////////////////////////////////////////////////
enum intersection_test {
DONT_INTERSECT,
DO_INTERSECT,
COLINEAR,
COINCIDENT,
};
intersection_test intersect2dSeg2dSeg(const Vec2r &p1,
const Vec2r &p2, // first segment
const Vec2r &p3,
const Vec2r &p4, // second segment
Vec2r &res); // found intersection point
intersection_test intersect2dLine2dLine(const Vec2r &p1,
const Vec2r &p2, // first segment
const Vec2r &p3,
const Vec2r &p4, // second segment
Vec2r &res); // found intersection point
intersection_test intersect2dSeg2dSegParametric(const Vec2r &p1,
const Vec2r &p2, // first segment
const Vec2r &p3,
const Vec2r &p4, // second segment
real &t, // I = P1 + t * P1P2)
real &u, // I = P3 + u * P3P4
real epsilon = M_EPSILON);
/** check whether a 2D segment intersect a 2D region or not */
bool intersect2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, const Vec2r &B);
/** check whether a 2D segment is included in a 2D region or not */
bool include2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, const Vec2r &B);
/** Box-triangle overlap test. */
bool overlapTriangleBox(const Vec3r &boxcenter, const Vec3r &boxhalfsize, const Vec3r triverts[3]);
/** Fast, Minimum Storage Ray-Triangle Intersection. */
bool intersectRayTriangle(const Vec3r &orig,
const Vec3r &dir,
const Vec3r &v0,
const Vec3r &v1,
const Vec3r &v2,
real &t, // I = orig + t * dir
real &u,
real &v, // I = (1 - u - v) * v0 + u * v1 + v * v2
const real epsilon = M_EPSILON); // the epsilon to use
/** Intersection between plane and ray. */
intersection_test intersectRayPlane(const Vec3r &orig,
const Vec3r &dir, // ray origin and direction
// plane's normal and offset (plane = { P / P.N + d = 0 })
const Vec3r &norm,
const real d,
real &t, // I = orig + t * dir
const real epsilon = M_EPSILON); // the epsilon to use
/** Intersection Ray-Bounding box (axis aligned).
* Adapted from Williams et al, "An Efficient Robust Ray-Box Intersection Algorithm", JGT 10:1
* (2005), pp. 49-54.
*/
bool intersectRayBBox(const Vec3r &orig,
const Vec3r &dir, // ray origin and direction
const Vec3r &boxMin,
const Vec3r &boxMax, // the bbox
// the interval in which at least on of the intersections must happen
real t0,
real t1,
real &tmin, // Imin = orig + tmin * dir is the first intersection
real &tmax, // Imax = orig + tmax * dir is the second intersection
real epsilon = M_EPSILON); // the epsilon to use
/** Checks whether 3D point P lies inside or outside of the triangle ABC */
bool includePointTriangle(const Vec3r &P, const Vec3r &A, const Vec3r &B, const Vec3r &C);
void transformVertex(const Vec3r &vert, const Matrix44r &matrix, Vec3r &res);
void transformVertices(const vector<Vec3r> &vertices, const Matrix44r &trans, vector<Vec3r> &res);
Vec3r rotateVector(const Matrix44r &mat, const Vec3r &v);
//
// Coordinates systems changing procedures
//
/////////////////////////////////////////////////////////////////////////////
/** From world to image
* p
* point's coordinates expressed in world coordinates system
* q
* vector in which the result will be stored
* model_view_matrix
* The model view matrix expressed in line major order (OpenGL
* matrices are column major ordered)
* projection_matrix
* The projection matrix expressed in line major order (OpenGL
* matrices are column major ordered)
* viewport
* The viewport: x,y coordinates followed by width and height (OpenGL like viewport)
*/
void fromWorldToImage(const Vec3r &p,
Vec3r &q,
const real model_view_matrix[4][4],
const real projection_matrix[4][4],
const int viewport[4]);
/** From world to image
* p
* point's coordinates expressed in world coordinates system
* q
* vector in which the result will be stored
* transform
* The transformation matrix (gathering model view and projection),
* expressed in line major order (OpenGL matrices are column major ordered)
* viewport
* The viewport: x,y coordinates followed by width and height (OpenGL like viewport)
*/
void fromWorldToImage(const Vec3r &p, Vec3r &q, const real transform[4][4], const int viewport[4]);
/** Projects from world coordinates to camera coordinates
* Returns the point's coordinates expressed in the camera's
* coordinates system.
* p
* point's coordinates expressed in world coordinates system
* q
* vector in which the result will be stored
* model_view_matrix
* The model view matrix expressed in line major order (OpenGL
* matrices are column major ordered)
*/
void fromWorldToCamera(const Vec3r &p, Vec3r &q, const real model_view_matrix[4][4]);
/** Projects from World Coordinates to retina coordinates
* Returns the point's coordinates expressed in Retina system.
* p
* point's coordinates expressed in camera system
* q
* vector in which the result will be stored
* projection_matrix
* The projection matrix expressed in line major order (OpenGL
* matrices are column major ordered)
*/
void fromCameraToRetina(const Vec3r &p, Vec3r &q, const real projection_matrix[4][4]);
/** From retina to image.
* Returns the coordinates expressed in Image coordinates system.
* p
* point's coordinates expressed in retina system
* q
* vector in which the result will be stored
* viewport
* The viewport: x,y coordinates followed by width and height (OpenGL like viewport).
*/
void fromRetinaToImage(const Vec3r &p, Vec3r &q, const int viewport[4]);
/** From image to retina
* p
* point's coordinates expressed in image system
* q
* vector in which the result will be stored
* viewport
* The viewport: x,y coordinates followed by width and height (OpenGL like viewport).
*/
void fromImageToRetina(const Vec3r &p, Vec3r &q, const int viewport[4]);
/** computes the coordinates of q in the camera coordinates system,
* using the known z coordinates of the 3D point.
* That means that this method does not inverse any matrices,
* it only computes X and Y from x,y and Z
* p
* point's coordinates expressed in retina system
* q
* vector in which the result will be stored
* projection_matrix
* The projection matrix expressed in line major order (OpenGL
* matrices are column major ordered)
*/
void fromRetinaToCamera(const Vec3r &p, Vec3r &q, real focal, const real projection_matrix[4][4]);
/** Projects from camera coordinates to world coordinates
* Returns the point's coordinates expressed in the world's
* coordinates system.
* p
* point's coordinates expressed in the camera coordinates system
* q
* vector in which the result will be stored
* model_view_matrix
* The model view matrix expressed in line major order (OpenGL
* matrices are column major ordered)
*/
void fromCameraToWorld(const Vec3r &p, Vec3r &q, const real model_view_matrix[4][4]);
} // end of namespace GeomUtils
} /* namespace Freestyle */

View File

@@ -0,0 +1,389 @@
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Base class to define a cell grid surrounding the bounding box of the scene
*/
#include <stdexcept>
#include "BBox.h"
#include "Grid.h"
#include "BLI_utildefines.h"
namespace Freestyle {
// Grid Visitors
/////////////////
void allOccludersGridVisitor::examineOccluder(Polygon3r *occ)
{
occluders_.push_back(occ);
}
static bool inBox(const Vec3r &inter, const Vec3r &box_min, const Vec3r &box_max)
{
if (((inter.x() >= box_min.x()) && (inter.x() < box_max.x())) &&
((inter.y() >= box_min.y()) && (inter.y() < box_max.y())) &&
((inter.z() >= box_min.z()) && (inter.z() < box_max.z())))
{
return true;
}
return false;
}
void firstIntersectionGridVisitor::examineOccluder(Polygon3r *occ)
{
// check whether the edge and the polygon plane are coincident:
//-------------------------------------------------------------
// first let us compute the plane equation.
Vec3r v1((occ)->getVertices()[0]);
Vec3d normal((occ)->getNormal());
// soc unused - double d = -(v1 * normal);
double tmp_u, tmp_v, tmp_t;
if ((occ)->rayIntersect(ray_org_, ray_dir_, tmp_t, tmp_u, tmp_v)) {
if (fabs(ray_dir_ * normal) > 0.0001) {
// Check whether the intersection is in the cell:
if (inBox(ray_org_ + tmp_t * ray_dir_ / ray_dir_.norm(),
current_cell_->getOrigin(),
current_cell_->getOrigin() + cell_size_))
{
#if 0
Vec3d bboxdiag(_scene3d->bbox().getMax() - _scene3d->bbox().getMin());
if ((t > 1.0e-06 * (min(min(bboxdiag.x(), bboxdiag.y()), bboxdiag.z()))) &&
(t < raylength))
#else
if (tmp_t < t_)
#endif
{
occluder_ = occ;
u_ = tmp_u;
v_ = tmp_v;
t_ = tmp_t;
}
}
else {
occ->userdata2 = nullptr;
}
}
}
} // namespace Freestyle
bool firstIntersectionGridVisitor::stop()
{
if (occluder_) {
return true;
}
return false;
}
// Grid
/////////////////
void Grid::clear()
{
if (!_occluders.empty()) {
for (OccludersSet::iterator it = _occluders.begin(); it != _occluders.end(); it++) {
delete (*it);
}
_occluders.clear();
}
_size = Vec3r(0, 0, 0);
_cell_size = Vec3r(0, 0, 0);
_orig = Vec3r(0, 0, 0);
_cells_nb = Vec3u(0, 0, 0);
//_ray_occluders.clear();
}
void Grid::configure(const Vec3r &orig, const Vec3r &size, uint nb)
{
_orig = orig;
Vec3r tmpSize = size;
// Compute the volume of the desired grid
real grid_vol = size[0] * size[1] * size[2];
if (grid_vol == 0) {
double min = DBL_MAX;
int index = 0;
int nzeros = 0;
for (int i = 0; i < 3; ++i) {
if (size[i] == 0) {
++nzeros;
index = i;
}
if ((size[i] != 0) && (min > size[i])) {
min = size[i];
}
}
if (nzeros > 1) {
throw std::runtime_error("Warning: the 3D grid has more than one null dimension");
}
tmpSize[index] = min;
_orig[index] = _orig[index] - min / 2;
}
// Compute the desired volume of a single cell
real cell_vol = grid_vol / nb;
// The edge of such a cubic cell is cubic root of cellVolume
real edge = pow(cell_vol, 1.0 / 3.0);
// We compute the number of cells par edge such as we cover at least the whole box.
uint i;
for (i = 0; i < 3; i++) {
_cells_nb[i] = uint(floor(tmpSize[i] / edge)) + 1;
}
_size = tmpSize;
for (i = 0; i < 3; i++) {
_cell_size[i] = _size[i] / _cells_nb[i];
}
}
void Grid::insertOccluder(Polygon3r *occluder)
{
const vector<Vec3r> vertices = occluder->getVertices();
if (vertices.empty()) {
return;
}
// add this occluder to the grid's occluders list
addOccluder(occluder);
// find the bbox associated to this polygon
Vec3r min, max;
occluder->getBBox(min, max);
// Retrieve the cell x, y, z coordinates associated with these min and max
Vec3u imax, imin;
getCellCoordinates(max, imax);
getCellCoordinates(min, imin);
// We are now going to fill in the cells overlapping with the polygon bbox.
// If the polygon is a triangle (most of cases), we also check for each of these cells if it is
// overlapping with the triangle in order to only fill in the ones really overlapping the
// triangle.
uint i, x, y, z;
vector<Vec3r>::const_iterator it;
Vec3u coord;
if (vertices.size() == 3) { // Triangle case
Vec3r triverts[3];
i = 0;
for (it = vertices.begin(); it != vertices.end(); it++) {
triverts[i] = Vec3r(*it);
i++;
}
Vec3r boxmin, boxmax;
for (z = imin[2]; z <= imax[2]; z++) {
for (y = imin[1]; y <= imax[1]; y++) {
for (x = imin[0]; x <= imax[0]; x++) {
coord[0] = x;
coord[1] = y;
coord[2] = z;
// We retrieve the box coordinates of the current cell
getCellBox(coord, boxmin, boxmax);
// We check whether the triangle and the box ovewrlap:
Vec3r boxcenter((boxmin + boxmax) / 2.0);
Vec3r boxhalfsize(_cell_size / 2.0);
if (GeomUtils::overlapTriangleBox(boxcenter, boxhalfsize, triverts)) {
// We must then create the Cell and add it to the cells list if it does not exist yet.
// We must then add the occluder to the occluders list of this cell.
Cell *cell = getCell(coord);
if (!cell) {
cell = new Cell(boxmin);
fillCell(coord, *cell);
}
cell->addOccluder(occluder);
}
}
}
}
}
else { // The polygon is not a triangle, we add all the cells overlapping the polygon bbox.
for (z = imin[2]; z <= imax[2]; z++) {
for (y = imin[1]; y <= imax[1]; y++) {
for (x = imin[0]; x <= imax[0]; x++) {
coord[0] = x;
coord[1] = y;
coord[2] = z;
Cell *cell = getCell(coord);
if (!cell) {
Vec3r orig;
getCellOrigin(coord, orig);
cell = new Cell(orig);
fillCell(coord, *cell);
}
cell->addOccluder(occluder);
}
}
}
}
}
bool Grid::nextRayCell(Vec3u &current_cell, Vec3u &next_cell)
{
next_cell = current_cell;
real t_min, t;
uint i;
t_min = FLT_MAX; // init tmin with handle of the case where one or 2 _u[i] = 0.
uint coord = 0; // predominant coord(0=x, 1=y, 2=z)
// using a parametric equation of a line : B = A + t u, we find the tx, ty and tz respectively
// corresponding to the intersections with the plans:
// x = _cell_size[0], y = _cell_size[1], z = _cell_size[2]
for (i = 0; i < 3; i++) {
if (_ray_dir[i] == 0) {
continue;
}
if (_ray_dir[i] > 0) {
t = (_cell_size[i] - _pt[i]) / _ray_dir[i];
}
else {
t = -_pt[i] / _ray_dir[i];
}
if (t < t_min) {
t_min = t;
coord = i;
}
}
// We use the parametric line equation and the found t (tamx) to compute the B coordinates:
Vec3r pt_tmp(_pt);
_pt = pt_tmp + t_min * _ray_dir;
// We express B coordinates in the next cell coordinates system. We just have to
// set the coordinate coord of B to 0 of _CellSize[coord] depending on the sign of _u[coord]
if (_ray_dir[coord] > 0) {
next_cell[coord]++;
_pt[coord] -= _cell_size[coord];
// if we are out of the grid, we must stop
if (next_cell[coord] >= _cells_nb[coord]) {
return false;
}
}
else {
int tmp = next_cell[coord] - 1;
_pt[coord] = _cell_size[coord];
if (tmp < 0) {
return false;
}
next_cell[coord]--;
}
_t += t_min;
if (_t >= _t_end) {
return false;
}
return true;
}
void Grid::castRay(const Vec3r &orig, const Vec3r &end, OccludersSet &occluders, uint timestamp)
{
initRay(orig, end, timestamp);
allOccludersGridVisitor visitor(occluders);
castRayInternal(visitor);
}
void Grid::castInfiniteRay(const Vec3r &orig,
const Vec3r &dir,
OccludersSet &occluders,
uint timestamp)
{
Vec3r end = Vec3r(orig + FLT_MAX * dir / dir.norm());
bool inter = initInfiniteRay(orig, dir, timestamp);
if (!inter) {
return;
}
allOccludersGridVisitor visitor(occluders);
castRayInternal(visitor);
}
Polygon3r *Grid::castRayToFindFirstIntersection(
const Vec3r &orig, const Vec3r &dir, double &t, double &u, double &v, uint timestamp)
{
Polygon3r *occluder = nullptr;
Vec3r end = Vec3r(orig + FLT_MAX * dir / dir.norm());
bool inter = initInfiniteRay(orig, dir, timestamp);
if (!inter) {
return nullptr;
}
firstIntersectionGridVisitor visitor(orig, dir, _cell_size);
castRayInternal(visitor);
// ARB: This doesn't work, because occluders are unordered within any cell
// visitor.occluder() will be an occluder, but we have no guarantee it will be the *first*
// occluder. I assume that is the reason this code is not actually used for FindOccludee.
occluder = visitor.occluder();
t = visitor.t_;
u = visitor.u_;
v = visitor.v_;
return occluder;
}
void Grid::initRay(const Vec3r &orig, const Vec3r &end, uint timestamp)
{
_ray_dir = end - orig;
_t_end = _ray_dir.norm();
_t = 0;
_ray_dir.normalize();
_timestamp = timestamp;
for (uint i = 0; i < 3; i++) {
_current_cell[i] = uint(floor((orig[i] - _orig[i]) / _cell_size[i]));
// soc unused - uint u = _current_cell[i];
_pt[i] = orig[i] - _orig[i] - _current_cell[i] * _cell_size[i];
}
//_ray_occluders.clear();
}
bool Grid::initInfiniteRay(const Vec3r &orig, const Vec3r &dir, uint timestamp)
{
_ray_dir = dir;
_t_end = FLT_MAX;
_t = 0;
_ray_dir.normalize();
_timestamp = timestamp;
// check whether the origin is in or out the box:
Vec3r boxMin(_orig);
Vec3r boxMax(_orig + _size);
BBox<Vec3r> box(boxMin, boxMax);
if (box.inside(orig)) {
for (uint i = 0; i < 3; i++) {
_current_cell[i] = uint(floor((orig[i] - _orig[i]) / _cell_size[i]));
// soc unused - uint u = _current_cell[i];
_pt[i] = orig[i] - _orig[i] - _current_cell[i] * _cell_size[i];
}
}
else {
// is the ray intersecting the box?
real tmin(-1.0), tmax(-1.0);
if (GeomUtils::intersectRayBBox(orig, _ray_dir, boxMin, boxMax, 0, _t_end, tmin, tmax)) {
BLI_assert(tmin != -1.0);
Vec3r newOrig = orig + tmin * _ray_dir;
for (uint i = 0; i < 3; i++) {
_current_cell[i] = uint(floor((newOrig[i] - _orig[i]) / _cell_size[i]));
if (_current_cell[i] == _cells_nb[i]) {
_current_cell[i] = _cells_nb[i] - 1;
}
// soc unused - uint u = _current_cell[i];
_pt[i] = newOrig[i] - _orig[i] - _current_cell[i] * _cell_size[i];
}
}
else {
return false;
}
}
//_ray_occluders.clear();
return true;
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,398 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Base class to define a cell grid surrounding the bounding box of the scene
*/
#include <cstring> // for memset
#include <float.h>
#include <stdint.h> // For POINTER_FROM_UINT, i.e. uintptr_t.
#include <vector>
#include "Geom.h"
#include "GeomUtils.h"
#include "Polygon.h"
#include "../system/FreestyleConfig.h"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
using namespace std;
namespace Freestyle {
using namespace Geometry;
typedef vector<Polygon3r *> OccludersSet;
//
// Class to define cells used by the regular grid
//
///////////////////////////////////////////////////////////////////////////////
class Cell {
public:
Cell(Vec3r &orig)
{
_orig = orig;
}
virtual ~Cell() {}
inline void addOccluder(Polygon3r *o)
{
if (o) {
_occluders.push_back(o);
}
}
inline const Vec3r &getOrigin()
{
return _orig;
}
inline OccludersSet &getOccluders()
{
return _occluders;
}
private:
Vec3r _orig;
OccludersSet _occluders;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Cell")
};
class GridVisitor {
public:
virtual ~GridVisitor() {}; // soc
virtual void discoverCell(Cell * /*cell*/) {}
virtual void examineOccluder(Polygon3r * /*occ*/) {}
virtual void finishCell(Cell * /*cell*/) {}
virtual bool stop()
{
return false;
}
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:GridVisitor")
};
/** Gathers all the occluders belonging to the cells traversed by the ray */
class allOccludersGridVisitor : public GridVisitor {
public:
allOccludersGridVisitor(OccludersSet &occluders) : GridVisitor(), occluders_(occluders) {}
virtual void examineOccluder(Polygon3r *occ);
OccludersSet &occluders()
{
return occluders_;
}
void clear()
{
occluders_.clear();
}
private:
OccludersSet &occluders_;
};
/** Finds the first intersection and breaks.
* The occluder and the intersection information are stored and accessible.
*/
class firstIntersectionGridVisitor : public GridVisitor {
// soc - changed order to remove warnings
public:
double u_, v_, t_;
private:
Polygon3r *occluder_;
Vec3r ray_org_, ray_dir_, cell_size_;
Cell *current_cell_;
public:
firstIntersectionGridVisitor(const Vec3r &ray_org, const Vec3r &ray_dir, const Vec3r &cell_size)
: GridVisitor(),
u_(0),
v_(0),
t_(DBL_MAX),
occluder_(0),
ray_org_(ray_org),
ray_dir_(ray_dir),
cell_size_(cell_size),
current_cell_(0)
{
}
virtual ~firstIntersectionGridVisitor() {}
virtual void discoverCell(Cell *cell)
{
current_cell_ = cell;
}
virtual void examineOccluder(Polygon3r *occ);
virtual bool stop();
Polygon3r *occluder()
{
return occluder_;
}
};
//
// Class to define a regular grid used for ray casting computations
//
///////////////////////////////////////////////////////////////////////////////
class Grid {
public:
/** Builds a Grid. Must be followed by a call to configure() */
Grid() {}
virtual ~Grid()
{
clear();
}
/** clears the grid
* Deletes all the cells, clears the hash-table, resets size, size of cell, number of cells.
*/
virtual void clear();
/** Sets the different parameters of the grid
* orig
* The grid origin
* size
* The grid's dimensions
* nb
* The number of cells of the grid
*/
virtual void configure(const Vec3r &orig, const Vec3r &size, uint nb);
/** returns a vector of integer containing the coordinates of the cell containing the point
* passed as argument
* p
* The point for which we're looking the cell
*/
inline void getCellCoordinates(const Vec3r &p, Vec3u &res)
{
int tmp;
for (int i = 0; i < 3; i++) {
tmp = (int)((p[i] - _orig[i]) / _cell_size[i]);
if (tmp < 0) {
res[i] = 0;
}
else if ((uint)tmp >= _cells_nb[i]) {
res[i] = _cells_nb[i] - 1;
}
else {
res[i] = tmp;
}
}
}
/** Fills the case corresponding to coord with the cell */
virtual void fillCell(const Vec3u &coord, Cell &cell) = 0;
/** returns the cell whose coordinates are passed as argument */
virtual Cell *getCell(const Vec3u &coord) = 0;
/** returns the cell containing the point passed as argument.
* If the cell is empty (contains no occluder), nullptr is returned:
* p
* The point for which we're looking the cell
*/
inline Cell *getCell(const Vec3r &p)
{
Vec3u coord;
getCellCoordinates(p, coord);
return getCell(coord);
}
/** Retrieves the x,y,z coordinates of the origin of the cell whose coordinates (i,j,k)
* is passed as argument:
* cell_coord
* i,j,k integer coordinates for the cell
* orig
* x,y,x vector to be filled in with the cell origin's coordinates
*/
inline void getCellOrigin(const Vec3u &cell_coord, Vec3r &orig)
{
for (uint i = 0; i < 3; i++) {
orig[i] = _orig[i] + cell_coord[i] * _cell_size[i];
}
}
/** Retrieves the box corresponding to the cell whose coordinates are passed as argument:
* cell_coord
* i,j,k integer coordinates for the cell
* min_out
* The min x,y,x vector of the box. Filled in by the method.
* max_out
* The max x,y,z coordinates of the box. Filled in by the method.
*/
inline void getCellBox(const Vec3u &cell_coord, Vec3r &min_out, Vec3r &max_out)
{
getCellOrigin(cell_coord, min_out);
max_out = min_out + _cell_size;
}
/** inserts a convex polygon occluder
* This method is quite coarse insofar as it adds all cells intersecting the polygon bounding
* box convex_poly The list of 3D points constituting a convex polygon
*/
void insertOccluder(Polygon3r *occluder);
/** Adds an occluder to the list of occluders */
void addOccluder(Polygon3r *occluder)
{
_occluders.push_back(occluder);
}
/** Casts a ray between a starting point and an ending point
* Returns the list of occluders contained in the cells intersected by this ray
* Starts with a call to InitRay.
*/
void castRay(const Vec3r &orig, const Vec3r &end, OccludersSet &occluders, uint timestamp);
// Prepares to cast ray without generating OccludersSet
void initAcceleratedRay(const Vec3r &orig, const Vec3r &end, uint timestamp);
/** Casts an infinite ray (still finishing at the end of the grid) from a starting point and in a
* given direction. Returns the list of occluders contained in the cells intersected by this ray
* Starts with a call to InitRay.
*/
void castInfiniteRay(const Vec3r &orig,
const Vec3r &dir,
OccludersSet &occluders,
uint timestamp);
// Prepares to cast ray without generating OccludersSet.
bool initAcceleratedInfiniteRay(const Vec3r &orig, const Vec3r &dir, uint timestamp);
/** Casts an infinite ray (still finishing at the end of the grid) from a starting point and in a
* given direction. Returns the first intersection (occluder,t,u,v) or null. Starts with a call
* to InitRay.
*/
Polygon3r *castRayToFindFirstIntersection(
const Vec3r &orig, const Vec3r &dir, double &t, double &u, double &v, uint timestamp);
/** Init all structures and values for computing the cells intersected by this new ray */
void initRay(const Vec3r &orig, const Vec3r &end, uint timestamp);
/** Init all structures and values for computing the cells intersected by this infinite ray.
* Returns false if the ray doesn't intersect the grid.
*/
bool initInfiniteRay(const Vec3r &orig, const Vec3r &dir, uint timestamp);
/** Accessors */
inline const Vec3r &getOrigin() const
{
return _orig;
}
inline Vec3r gridSize() const
{
return _size;
}
inline Vec3r getCellSize() const
{
return _cell_size;
}
// ARB profiling only:
inline OccludersSet *getOccluders()
{
return &_occluders;
}
void displayDebug()
{
cerr << "Cells nb : " << _cells_nb << endl;
cerr << "Cell size : " << _cell_size << endl;
cerr << "Origin : " << _orig << endl;
cerr << "Occluders nb : " << _occluders.size() << endl;
}
protected:
/** Core of castRay and castInfiniteRay, find occluders along the given ray */
inline void castRayInternal(GridVisitor &visitor)
{
Cell *current_cell = nullptr;
do {
current_cell = getCell(_current_cell);
if (current_cell) {
visitor.discoverCell(current_cell);
OccludersSet &occluders =
current_cell->getOccluders(); // FIXME: I had forgotten the ref &
for (OccludersSet::iterator it = occluders.begin(); it != occluders.end(); it++) {
if (POINTER_AS_UINT((*it)->userdata2) != _timestamp) {
(*it)->userdata2 = POINTER_FROM_UINT(_timestamp);
visitor.examineOccluder(*it);
}
}
visitor.finishCell(current_cell);
}
} while ((!visitor.stop()) && (nextRayCell(_current_cell, _current_cell)));
}
/** Returns the cell next to the cell passed as argument. */
bool nextRayCell(Vec3u &current_cell, Vec3u &next_cell);
uint _timestamp;
Vec3u _cells_nb; // number of cells for x,y,z axis
Vec3r _cell_size; // cell x,y,z dimensions
Vec3r _size; // grid x,y,x dimensions
Vec3r _orig; // grid origin
Vec3r _ray_dir; // direction vector for the ray
Vec3u _current_cell; // The current cell being processed (designated by its 3 coordinates)
Vec3r _pt; // Points corresponding to the incoming and outgoing intersections of one cell with
// the ray
real _t_end; // To know when we are at the end of the ray
real _t;
// OccludersSet _ray_occluders; // Set storing the occluders contained in the cells traversed by
// a ray
OccludersSet _occluders; // List of all occluders inserted in the grid
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Grid")
};
//
// Class to walk through occluders in grid without building intermediate data structures
//
///////////////////////////////////////////////////////////////////////////////
class VirtualOccludersSet {
public:
VirtualOccludersSet(Grid &_grid) : grid(_grid) {};
Polygon3r *begin();
Polygon3r *next();
Polygon3r *next(bool stopOnNewCell);
private:
Polygon3r *firstOccluderFromNextCell();
Grid &grid;
OccludersSet::iterator it, end;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:VirtualOccludersSet")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the projected image of a scene
*/
#include "GridHelpers.h"
namespace Freestyle {
void GridHelpers::getDefaultViewProscenium(real viewProscenium[4])
{
// Get proscenium boundary for culling
// bufferZone determines the amount by which the area processed should exceed the actual image
// area. This is intended to avoid visible artifacts generated along the proscenium edge. Perhaps
// this is no longer needed now that entire view edges are culled at once, since that
// theoretically should eliminate visible artifacts. To the extent it is still useful, bufferZone
// should be put into the UI as configurable percentage value
const real bufferZone = 0.05;
// borderZone describes a blank border outside the proscenium, but still inside the image area.
// Only intended for exposing possible artifacts along or outside the proscenium edge during
// debugging.
const real borderZone = 0.0;
viewProscenium[0] = blender::g_freestyle.viewport[2] * (borderZone - bufferZone);
viewProscenium[1] = blender::g_freestyle.viewport[2] * (1.0f - borderZone + bufferZone);
viewProscenium[2] = blender::g_freestyle.viewport[3] * (borderZone - bufferZone);
viewProscenium[3] = blender::g_freestyle.viewport[3] * (1.0f - borderZone + bufferZone);
}
GridHelpers::Transform::~Transform() = default;
} /* namespace Freestyle */

View File

@@ -0,0 +1,199 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the projected image of a scene
*/
#include <vector>
#include "FRS_freestyle.h"
#include "GeomUtils.h"
#include "Polygon.h"
#include "../winged_edge/WEdge.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
namespace GridHelpers {
/** Computes the distance from a point P to a segment AB */
template<class T> T closestPointToSegment(const T &P, const T &A, const T &B, real &distance)
{
T AB, AP, BP;
AB = B - A;
AP = P - A;
BP = P - B;
real c1(AB * AP);
if (c1 <= 0) {
distance = AP.norm();
return A; // A is closest point
}
real c2(AB * AB);
if (c2 <= c1) {
distance = BP.norm();
return B; // B is closest point
}
real b = c1 / c2;
T Pb, PPb;
Pb = A + b * AB;
PPb = P - Pb;
distance = PPb.norm();
return Pb; // closest point lies on AB
}
inline Vec3r closestPointOnPolygon(const Vec3r &point, const Polygon3r &poly)
{
// First cast a ray from the point onto the polygon plane
// If the ray intersects the polygon, then the intersection point
// is the closest point on the polygon
real t, u, v;
if (poly.rayIntersect(point, poly.getNormal(), t, u, v)) {
return point + poly.getNormal() * t;
}
// Otherwise, get the nearest point on each edge, and take the closest
real distance;
Vec3r closest = closestPointToSegment(
point, poly.getVertices()[2], poly.getVertices()[0], distance);
for (uint i = 0; i < 2; ++i) {
real t;
Vec3r p = closestPointToSegment(point, poly.getVertices()[i], poly.getVertices()[i + 1], t);
if (t < distance) {
distance = t;
closest = p;
}
}
return closest;
}
inline real distancePointToPolygon(const Vec3r &point, const Polygon3r &poly)
{
// First cast a ray from the point onto the polygon plane
// If the ray intersects the polygon, then the intersection point
// is the closest point on the polygon
real t, u, v;
if (poly.rayIntersect(point, poly.getNormal(), t, u, v)) {
return (t > 0.0) ? t : -t;
}
// Otherwise, get the nearest point on each edge, and take the closest
real distance = GeomUtils::distPointSegment(point, poly.getVertices()[2], poly.getVertices()[0]);
for (uint i = 0; i < 2; ++i) {
real t = GeomUtils::distPointSegment(point, poly.getVertices()[i], poly.getVertices()[i + 1]);
if (t < distance) {
distance = t;
}
}
return distance;
}
class Transform {
public:
virtual ~Transform() = 0;
virtual Vec3r operator()(const Vec3r &point) const = 0;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:GridHelpers:Transform")
};
inline bool insideProscenium(const real proscenium[4], const Polygon3r &polygon)
{
// N.B. The bounding box check is redundant for inserting occluders into cells, because the cell
// selection code in insertOccluders has already guaranteed that the bounding boxes will overlap.
// First check the viewport edges, since they are the easiest case
// Check if the bounding box is entirely outside the proscenium
Vec3r bbMin, bbMax;
polygon.getBBox(bbMin, bbMax);
if (bbMax[0] < proscenium[0] || bbMin[0] > proscenium[1] || bbMax[1] < proscenium[2] ||
bbMin[1] > proscenium[3])
{
return false;
}
Vec3r boxCenter(proscenium[0] + (proscenium[1] - proscenium[0]) / 2.0,
proscenium[2] + (proscenium[3] - proscenium[2]) / 2.0,
0.0);
Vec3r boxHalfSize(
(proscenium[1] - proscenium[0]) / 2.0, (proscenium[3] - proscenium[2]) / 2.0, 1.0);
Vec3r triverts[3] = {
Vec3r(polygon.getVertices()[0][0], polygon.getVertices()[0][1], 0.0),
Vec3r(polygon.getVertices()[1][0], polygon.getVertices()[1][1], 0.0),
Vec3r(polygon.getVertices()[2][0], polygon.getVertices()[2][1], 0.0),
};
return GeomUtils::overlapTriangleBox(boxCenter, boxHalfSize, triverts);
}
inline vector<Vec3r> enumerateVertices(const vector<WOEdge *> &fedges)
{
vector<Vec3r> points;
// Iterate over vertices, storing projections in points
for (vector<WOEdge *>::const_iterator woe = fedges.begin(), woend = fedges.end(); woe != woend;
woe++)
{
points.push_back((*woe)->GetaVertex()->GetVertex());
}
return points;
}
void getDefaultViewProscenium(real viewProscenium[4]);
inline void expandProscenium(real proscenium[4], const Polygon3r &polygon)
{
Vec3r bbMin, bbMax;
polygon.getBBox(bbMin, bbMax);
const real epsilon = 1.0e-6;
if (bbMin[0] <= proscenium[0]) {
proscenium[0] = bbMin[0] - epsilon;
}
if (bbMin[1] <= proscenium[2]) {
proscenium[2] = bbMin[1] - epsilon;
}
if (bbMax[0] >= proscenium[1]) {
proscenium[1] = bbMax[0] + epsilon;
}
if (bbMax[1] >= proscenium[3]) {
proscenium[3] = bbMax[1] + epsilon;
}
}
inline void expandProscenium(real proscenium[4], const Vec3r &point)
{
const real epsilon = 1.0e-6;
if (point[0] <= proscenium[0]) {
proscenium[0] = point[0] - epsilon;
}
if (point[1] <= proscenium[2]) {
proscenium[2] = point[1] - epsilon;
}
if (point[0] >= proscenium[1]) {
proscenium[1] = point[0] + epsilon;
}
if (point[1] >= proscenium[3]) {
proscenium[3] = point[1] + epsilon;
}
}
}; // namespace GridHelpers
} /* namespace Freestyle */

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the bounding box of the scene
*/
#include "HashGrid.h"
#include "BLI_sys_types.h"
namespace Freestyle {
void HashGrid::clear()
{
if (!_cells.empty()) {
for (GridHashTable::iterator it = _cells.begin(); it != _cells.end(); it++) {
Cell *cell = (*it).second;
delete cell;
}
_cells.clear();
}
Grid::clear();
}
void HashGrid::configure(const Vec3r &orig, const Vec3r &size, uint nb)
{
Grid::configure(orig, size, nb);
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,91 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a cell grid surrounding the bounding box of the scene
*/
#if 0
# if defined(__GNUC__) && (__GNUC__ >= 3)
// hash_map is not part of the C++ standard anymore;
// hash_map.h has been kept though for backward compatibility
# include <hash_map.h>
# else
# include <hash_map>
# endif
#endif
#include <map>
#include "Grid.h"
namespace Freestyle {
/** Defines a hash table used for searching the Cells */
struct GridHasher {
#define _MUL 950706376UL
#define _MOD 2147483647UL
inline size_t operator()(const Vec3u &p) const
{
size_t res = (ulong(p[0] * _MUL)) % _MOD;
res = ((res + ulong(p[1]) * _MUL)) % _MOD;
return ((res + ulong(p[2]) * _MUL)) % _MOD;
}
#undef _MUL
#undef _MOD
};
/** Class to define a regular grid used for ray casting computations */
class HashGrid : public Grid {
public:
typedef map<Vec3u, Cell *> GridHashTable;
HashGrid() : Grid() {}
virtual ~HashGrid()
{
clear();
}
/** clears the grid
* Deletes all the cells, clears the hash-table, resets size, size of cell, number of cells.
*/
virtual void clear();
/** Sets the different parameters of the grid
* orig
* The grid origin
* size
* The grid's dimensions
* nb
* The number of cells of the grid
*/
virtual void configure(const Vec3r &orig, const Vec3r &size, uint nb);
/** returns the cell whose coordinates are passed as argument */
virtual Cell *getCell(const Vec3u &p)
{
Cell *found_cell = nullptr;
GridHashTable::const_iterator found = _cells.find(p);
if (found != _cells.end()) {
found_cell = (*found).second;
}
return found_cell;
}
/** Fills the case p with the cell iCell */
virtual void fillCell(const Vec3u &p, Cell &cell)
{
_cells[p] = &cell;
}
protected:
GridHashTable _cells;
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,271 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup freestyle
* \brief Class to define Perlin noise
*/
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include "BLI_compiler_attrs.h"
#include "BLI_rand.h"
#include "BLI_sys_types.h"
#include "Noise.h"
namespace Freestyle {
#define SCURVE(a) ((a) * (a) * (3.0 - 2.0 * (a)))
#if 0 // XXX Unused
# define REALSCALE (2.0 / 65536.0)
# define NREALSCALE (2.0 / 4096.0)
# define HASH3D(a, b, c) \
hashTable[hashTable[hashTable[(a) & 0xfff] ^ ((b) & 0xfff)] ^ ((c) & 0xfff)]
# define HASH(a, b, c) (xtab[(xtab[(xtab[(a) & 0xff] ^ (b)) & 0xff] ^ (c)) & 0xff] & 0xff)
# define INCRSUM(m, s, x, y, z) \
((s) * (RTable[m] * 0.5 + RTable[m + 1] * (x) + RTable[m + 2] * (y) + RTable[m + 3] * (z)))
# define MAXSIZE 500
#endif
#define BM 0xff
#define N 0x1000
#if 0 // XXX Unused
# define NP 12 /* 2^N */
# define NM 0xfff
#endif
#define LERP(t, a, b) ((a) + (t) * ((b) - (a)))
#define SETUP(i, b0, b1, r0, r1) \
{ \
(t) = (i) + (N); \
(r0) = modff((t), &(u)); \
(r1) = (r0) - 1.0; \
(b0) = int(u) & BM; \
(b1) = ((b0) + 1) & BM; \
} \
(void)0
static void normalize2(float v[2])
{
float s;
s = sqrt(v[0] * v[0] + v[1] * v[1]);
v[0] = v[0] / s;
v[1] = v[1] / s;
}
static void normalize3(float v[3])
{
float s;
s = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
v[0] = v[0] / s;
v[1] = v[1] / s;
v[2] = v[2] / s;
}
float Noise::turbulence1(float arg, float freq, float amp, uint oct)
{
float t;
float vec;
for (t = 0; oct > 0 && freq > 0; freq *= 2, amp /= 2, --oct) {
vec = freq * arg;
t += smoothNoise1(vec) * amp;
}
return t;
}
float Noise::turbulence2(Vec2f &v, float freq, float amp, uint oct)
{
float t;
Vec2f vec;
for (t = 0; oct > 0 && freq > 0; freq *= 2, amp /= 2, --oct) {
vec.x() = freq * v.x();
vec.y() = freq * v.y();
t += smoothNoise2(vec) * amp;
}
return t;
}
float Noise::turbulence3(Vec3f &v, float freq, float amp, uint oct)
{
float t;
Vec3f vec;
for (t = 0; oct > 0 && freq > 0; freq *= 2, amp /= 2, --oct) {
vec.x() = freq * v.x();
vec.y() = freq * v.y();
vec.z() = freq * v.z();
t += smoothNoise3(vec) * amp;
}
return t;
}
// Noise functions over 1, 2, and 3 dimensions
float Noise::smoothNoise1(float arg)
{
int bx0, bx1;
float rx0, rx1, sx, t, u, v, vec;
vec = arg;
SETUP(vec, bx0, bx1, rx0, rx1);
sx = SCURVE(rx0);
u = rx0 * g1[p[bx0]];
v = rx1 * g1[p[bx1]];
return LERP(sx, u, v);
}
float Noise::smoothNoise2(Vec2f &vec)
{
int bx0, bx1, by0, by1, b00, b10, b01, b11;
float rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
int i, j;
SETUP(vec.x(), bx0, bx1, rx0, rx1);
SETUP(vec.y(), by0, by1, ry0, ry1);
i = p[bx0];
j = p[bx1];
b00 = p[i + by0];
b10 = p[j + by0];
b01 = p[i + by1];
b11 = p[j + by1];
sx = SCURVE(rx0);
sy = SCURVE(ry0);
#define AT2(rx, ry) ((rx) * q[0] + (ry) * q[1])
q = g2[b00];
u = AT2(rx0, ry0);
q = g2[b10];
v = AT2(rx1, ry0);
a = LERP(sx, u, v);
q = g2[b01];
u = AT2(rx0, ry1);
q = g2[b11];
v = AT2(rx1, ry1);
b = LERP(sx, u, v);
#undef AT2
return LERP(sy, a, b);
}
float Noise::smoothNoise3(Vec3f &vec)
{
int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
float rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
int i, j;
SETUP(vec.x(), bx0, bx1, rx0, rx1);
SETUP(vec.y(), by0, by1, ry0, ry1);
SETUP(vec.z(), bz0, bz1, rz0, rz1);
i = p[bx0];
j = p[bx1];
b00 = p[i + by0];
b10 = p[j + by0];
b01 = p[i + by1];
b11 = p[j + by1];
t = SCURVE(rx0);
sy = SCURVE(ry0);
sz = SCURVE(rz0);
#define AT3(rx, ry, rz) ((rx) * q[0] + (ry) * q[1] + (rz) * q[2])
q = g3[b00 + bz0];
u = AT3(rx0, ry0, rz0);
q = g3[b10 + bz0];
v = AT3(rx1, ry0, rz0);
a = LERP(t, u, v);
q = g3[b01 + bz0];
u = AT3(rx0, ry1, rz0);
q = g3[b11 + bz0];
v = AT3(rx1, ry1, rz0);
b = LERP(t, u, v);
c = LERP(sy, a, b);
q = g3[b00 + bz1];
u = AT3(rx0, ry0, rz1);
q = g3[b10 + bz1];
v = AT3(rx1, ry0, rz1);
a = LERP(t, u, v);
q = g3[b01 + bz1];
u = AT3(rx0, ry1, rz1);
q = g3[b11 + bz1];
v = AT3(rx1, ry1, rz1);
b = LERP(t, u, v);
d = LERP(sy, a, b);
#undef AT3
return LERP(sz, c, d);
}
Noise::Noise(long seed)
{
/* Use Blender RNG for repeatable results across platforms. */
blender::RNG *rng = blender::BLI_rng_new(seed);
int i, j, k;
for (i = 0; i < _NOISE_B; i++) {
p[i] = i;
g1[i] = float((BLI_rng_get_int(rng) % (_NOISE_B + _NOISE_B)) - _NOISE_B) / _NOISE_B;
for (j = 0; j < 2; j++) {
g2[i][j] = float((BLI_rng_get_int(rng) % (_NOISE_B + _NOISE_B)) - _NOISE_B) / _NOISE_B;
}
normalize2(g2[i]);
for (j = 0; j < 3; j++) {
g3[i][j] = float((BLI_rng_get_int(rng) % (_NOISE_B + _NOISE_B)) - _NOISE_B) / _NOISE_B;
}
normalize3(g3[i]);
}
while (--i) {
k = p[i];
p[i] = p[j = BLI_rng_get_int(rng) % _NOISE_B];
p[j] = k;
}
for (i = 0; i < _NOISE_B + 2; i++) {
p[_NOISE_B + i] = p[i];
g1[_NOISE_B + i] = g1[i];
for (j = 0; j < 2; j++) {
g2[_NOISE_B + i][j] = g2[i][j];
}
for (j = 0; j < 3; j++) {
g3[_NOISE_B + i][j] = g3[i][j];
}
}
BLI_rng_free(rng);
}
} /* namespace Freestyle */

View File

@@ -0,0 +1,64 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define Perlin noise
*/
#include "Geom.h"
#include "../system/FreestyleConfig.h"
#include "MEM_guardedalloc.h"
using namespace std;
namespace Freestyle {
#define _NOISE_B 0x100
using namespace Geometry;
/** Class to provide Perlin Noise functionalities */
class Noise {
public:
/** Builds a Noise object */
Noise(long seed = -1);
/** Destructor */
~Noise() {}
/** Returns a noise value for a 1D element */
float turbulence1(float arg, float freq, float amp, uint oct = 4);
/** Returns a noise value for a 2D element */
float turbulence2(Vec2f &v, float freq, float amp, uint oct = 4);
/** Returns a noise value for a 3D element */
float turbulence3(Vec3f &v, float freq, float amp, uint oct = 4);
/** Returns a smooth noise value for a 1D element */
float smoothNoise1(float arg);
/** Returns a smooth noise value for a 2D element */
float smoothNoise2(Vec2f &vec);
/** Returns a smooth noise value for a 3D element */
float smoothNoise3(Vec3f &vec);
private:
int p[_NOISE_B + _NOISE_B + 2];
float g3[_NOISE_B + _NOISE_B + 2][3];
float g2[_NOISE_B + _NOISE_B + 2][2];
float g1[_NOISE_B + _NOISE_B + 2];
/* UNUSED */
// int start;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Noise")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,208 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a polygon
*/
#include <vector>
#include "Geom.h"
#include "GeomUtils.h"
#include "MEM_guardedalloc.h"
using namespace std;
namespace Freestyle {
namespace Geometry {
template<class Point> class Polygon {
public:
inline Polygon()
{
_id = 0;
userdata = 0;
userdata2 = 0;
}
inline Polygon(const vector<Point> &vertices)
{
_vertices = vertices;
computeBBox();
_id = 0;
userdata = 0;
userdata2 = 0;
}
inline Polygon(const Polygon<Point> &poly)
{
Point p;
for (typename vector<Point>::const_iterator it = poly.getVertices().begin();
it != poly.getVertices().end();
it++)
{
p = *it;
_vertices.push_back(p);
}
_id = poly.getId();
poly.getBBox(_min, _max);
userdata = 0;
userdata2 = 0;
}
virtual ~Polygon() {}
//
// Accessors
//
/////////////////////////////////////////////////////////////////////////////
inline const vector<Point> &getVertices() const
{
return _vertices;
}
inline void getBBox(Point &min, Point &max) const
{
min = _min;
max = _max;
}
inline Point getBBoxCenter()
{
Point result;
result = (_min + _max) / 2;
return result;
}
inline Point getCenter()
{
Point result;
for (typename vector<Point>::iterator it = _vertices.begin(); it != _vertices.end(); it++) {
result += *it;
}
result /= _vertices.size();
return result;
}
inline uint getId() const
{
return _id;
}
//
// Modifiers
//
/////////////////////////////////////////////////////////////////////////////
inline void setVertices(const vector<Point> &vertices)
{
_vertices.clear();
Point p;
for (typename vector<Point>::const_iterator it = vertices.begin(); it != vertices.end(); it++)
{
p = *it;
_vertices.push_back(p);
}
computeBBox();
}
inline void setId(uint id)
{
_id = id;
}
//
// Other methods
//
/////////////////////////////////////////////////////////////////////////////
inline void computeBBox()
{
if (_vertices.empty()) {
return;
}
_max = _vertices[0];
_min = _vertices[0];
for (typename vector<Point>::iterator it = _vertices.begin(); it != _vertices.end(); it++) {
for (uint i = 0; i < Point::dim(); i++) {
if ((*it)[i] > _max[i]) {
_max[i] = (*it)[i];
}
if ((*it)[i] < _min[i]) {
_min[i] = (*it)[i];
}
}
}
}
// FIXME Is it possible to get rid of userdatas ?
void *userdata;
void *userdata2; // Used during ray casting
protected:
vector<Point> _vertices;
Point _min;
Point _max;
uint _id;
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Geometry:Polygon")
};
//
// Polygon3r class
//
///////////////////////////////////////////////////////////////////////////////
class Polygon3r : public Polygon<Vec3r> {
public:
inline Polygon3r() : Polygon<Vec3r>() {}
inline Polygon3r(const vector<Vec3r> &vertices, const Vec3r &normal) : Polygon<Vec3r>(vertices)
{
setNormal(normal);
}
inline Polygon3r(const Polygon3r &poly) : Polygon<Vec3r>(poly), _normal(poly._normal) {}
virtual ~Polygon3r() {}
void setNormal(const Vec3r &normal)
{
_normal = normal;
}
inline Vec3r getNormal() const
{
return _normal;
}
/** Check whether the Polygon intersects with the ray or not */
inline bool rayIntersect(const Vec3r &orig,
const Vec3r &dir,
real &t,
real &u,
real &v,
real epsilon = M_EPSILON) const
{
#if 0
if (_vertices.size() < 3) {
return false;
}
#endif
return GeomUtils::intersectRayTriangle(
orig, dir, _vertices[0], _vertices[1], _vertices[2], t, u, v, epsilon);
}
private:
Vec3r _normal;
};
} // end of namespace Geometry
} /* namespace Freestyle */

View File

@@ -0,0 +1,321 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Class to define a Sweep Line
*/
#include <list>
#include <vector>
#include "MEM_guardedalloc.h"
namespace Freestyle {
/** Class to define the intersection between two segments. */
template<class Edge> class Intersection {
public:
template<class EdgeClass> Intersection(EdgeClass *eA, real ta, EdgeClass *eB, real tb)
{
EdgeA = eA;
EdgeB = eB;
tA = ta;
tB = tb;
userdata = 0;
}
Intersection(const Intersection &iBrother)
{
EdgeA = iBrother.EdgeA;
EdgeB = iBrother.EdgeB;
tA = iBrother.tA;
tB = iBrother.tB;
userdata = 0;
}
/** returns the parameter giving the intersection, for the edge iEdge */
real getParameter(Edge *iEdge)
{
if (iEdge == EdgeA) {
return tA;
}
if (iEdge == EdgeB) {
return tB;
}
return 0;
}
public:
void *userdata; // FIXME
Edge *EdgeA; // first segment
Edge *EdgeB; // second segment
real tA; // parameter defining the intersection point with respect to the segment EdgeA.
real tB; // parameter defining the intersection point with respect to the segment EdgeB.
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Intersection")
};
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4521) // disable warning C4521: multiple copy constructors specified
#endif
template<class T, class Point> class Segment {
public:
Segment() {}
Segment(T &s, const Point &iA, const Point &iB)
{
_edge = s;
if (iA < iB) {
A = iA;
B = iB;
_order = true;
}
else {
A = iB;
B = iA;
_order = false;
}
}
Segment(Segment<T, Point> &iBrother)
{
_edge = iBrother.edge();
A = iBrother.A;
B = iBrother.B;
_Intersections = iBrother._Intersections;
_order = iBrother._order;
}
Segment(const Segment<T, Point> &iBrother)
{
_edge = iBrother._edge;
A = iBrother.A;
B = iBrother.B;
_Intersections = iBrother._Intersections;
_order = iBrother._order;
}
~Segment()
{
_Intersections.clear();
}
inline Point operator[](const ushort &i) const
{
return (i % 2 == 0) ? A : B;
}
inline bool operator==(const Segment<T, Point> &iBrother)
{
if (_edge == iBrother._edge) {
return true;
}
return false;
}
/* Adds an intersection for this segment */
inline void AddIntersection(Intersection<Segment<T, Point>> *i)
{
_Intersections.push_back(i);
}
/** Checks for a common vertex with another edge */
inline bool CommonVertex(const Segment<T, Point> &S, Point &CP)
{
if ((A == S[0]) || (A == S[1])) {
CP = A;
return true;
}
if ((B == S[0]) || (B == S[1])) {
CP = B;
return true;
}
return false;
}
inline vector<Intersection<Segment<T, Point>> *> &intersections()
{
return _Intersections;
}
inline bool order()
{
return _order;
}
inline T &edge()
{
return _edge;
}
private:
T _edge;
Point A;
Point B;
std::vector<Intersection<Segment<T, Point>> *>
_Intersections; // list of intersections parameters
bool _order; // true if A and B are in the same order than _edge.A and _edge.B. false otherwise.
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Segment")
};
#ifdef _MSC_VER
# pragma warning(pop)
#endif
/** defines a binary function that can be overload by the user to specify at each condition the
* intersection between 2 edges must be computed
*/
template<class T1, class T2> struct binary_rule {
binary_rule() {}
template<class T3, class T4> binary_rule(const binary_rule<T3, T4> & /*brother*/) {}
virtual ~binary_rule() {}
virtual bool operator()(T1 &, T2 &)
{
return true;
}
};
template<class T, class Point> class SweepLine {
public:
SweepLine() {}
~SweepLine()
{
for (typename vector<Intersection<Segment<T, Point>> *>::iterator i = _Intersections.begin(),
iend = _Intersections.end();
i != iend;
i++)
{
delete (*i);
}
}
inline void process(Point &p,
vector<Segment<T, Point> *> &segments,
#if 0
binary_rule<Segment<T, Point>, Segment<T, Point>> &binrule =
binary_rule<Segment<T, Point>, Segment<T, Point>>(),
#else
binary_rule<Segment<T, Point>, Segment<T, Point>> &binrule,
#endif
real epsilon = M_EPSILON)
{
// first we remove the segments that need to be removed and then we add the segments to add
vector<Segment<T, Point> *> toadd;
typename vector<Segment<T, Point> *>::iterator s, send;
for (s = segments.begin(), send = segments.end(); s != send; s++) {
if (p == (*(*s))[0]) {
toadd.push_back((*s));
}
else {
remove((*s));
}
}
for (s = toadd.begin(), send = toadd.end(); s != send; s++) {
add((*s), binrule, epsilon);
}
}
inline void add(Segment<T, Point> *S,
#if 0
binary_rule<Segment<T, Point>, Segment<T, Point>> &binrule =
binary_rule<Segment<T, Point>, Segment<T, Point>>(),
#else
binary_rule<Segment<T, Point>, Segment<T, Point>> &binrule,
#endif
real epsilon)
{
real t, u;
Point CP;
Vec2r v0, v1, v2, v3;
if (true == S->order()) {
v0[0] = ((*S)[0])[0];
v0[1] = ((*S)[0])[1];
v1[0] = ((*S)[1])[0];
v1[1] = ((*S)[1])[1];
}
else {
v1[0] = ((*S)[0])[0];
v1[1] = ((*S)[0])[1];
v0[0] = ((*S)[1])[0];
v0[1] = ((*S)[1])[1];
}
for (typename std::list<Segment<T, Point> *>::iterator s = _set.begin(), send = _set.end();
s != send;
s++)
{
Segment<T, Point> *currentS = (*s);
if (true != binrule(*S, *currentS)) {
continue;
}
if (true == currentS->order()) {
v2[0] = ((*currentS)[0])[0];
v2[1] = ((*currentS)[0])[1];
v3[0] = ((*currentS)[1])[0];
v3[1] = ((*currentS)[1])[1];
}
else {
v3[0] = ((*currentS)[0])[0];
v3[1] = ((*currentS)[0])[1];
v2[0] = ((*currentS)[1])[0];
v2[1] = ((*currentS)[1])[1];
}
if (S->CommonVertex(*currentS, CP)) {
continue; // the two edges have a common vertex->no need to check
}
if (GeomUtils::intersect2dSeg2dSegParametric(v0, v1, v2, v3, t, u, epsilon) ==
GeomUtils::DO_INTERSECT)
{
// create the intersection
Intersection<Segment<T, Point>> *inter = new Intersection<Segment<T, Point>>(
S, t, currentS, u);
// add it to the intersections list
_Intersections.push_back(inter);
// add this intersection to the first edge intersections list
S->AddIntersection(inter);
// add this intersection to the second edge intersections list
currentS->AddIntersection(inter);
}
}
// add the added segment to the list of active segments
_set.push_back(S);
}
inline void remove(Segment<T, Point> *s)
{
if (s->intersections().size() > 0) {
_IntersectedEdges.push_back(s);
}
_set.remove(s);
}
vector<Segment<T, Point> *> &intersectedEdges()
{
return _IntersectedEdges;
}
vector<Intersection<Segment<T, Point>> *> &intersections()
{
return _Intersections;
}
private:
std::list<Segment<T, Point> *>
_set; // set of active edges for a given position of the sweep line
std::vector<Segment<T, Point> *> _IntersectedEdges; // the list of intersected edges
std::vector<Intersection<Segment<T, Point>> *> _Intersections; // the list of all intersections.
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:SweepLine")
};
} /* namespace Freestyle */

View File

@@ -0,0 +1,955 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup freestyle
* \brief Vectors and Matrices definition and manipulation
*/
#include <iostream>
#include <math.h>
#include <vector>
#include "MEM_guardedalloc.h"
namespace Freestyle {
namespace VecMat {
namespace Internal {
template<bool B> struct is_false {};
template<> struct is_false<false> {
static inline void ensure() {}
};
} // end of namespace Internal
//
// Vector class
// - T: value type
// - N: dimension
//
/////////////////////////////////////////////////////////////////////////////
template<class T, uint N> class Vec {
public:
typedef T value_type;
// constructors
inline Vec()
{
for (uint i = 0; i < N; i++) {
this->_coord[i] = 0;
}
}
~Vec()
{
Internal::is_false<(N == 0)>::ensure();
}
template<class U> explicit inline Vec(const U tab[N])
{
for (uint i = 0; i < N; i++) {
this->_coord[i] = (T)tab[i];
}
}
template<class U> explicit inline Vec(const std::vector<U> &tab)
{
for (uint i = 0; i < N; i++) {
this->_coord[i] = (T)tab[i];
}
}
template<class U> explicit inline Vec(const Vec<U, N> &v)
{
for (uint i = 0; i < N; i++) {
this->_coord[i] = (T)v[i];
}
}
// accessors
inline value_type operator[](const uint i) const
{
return this->_coord[i];
}
inline value_type &operator[](const uint i)
{
return this->_coord[i];
}
static inline uint dim()
{
return N;
}
// various useful methods
inline value_type norm() const
{
return (T)sqrt((float)squareNorm());
}
inline value_type squareNorm() const
{
return (*this) * (*this);
}
inline Vec<T, N> &normalize()
{
value_type n = norm();
for (uint i = 0; i < N; i++) {
this->_coord[i] /= n;
}
return *this;
}
inline Vec<T, N> &normalizeSafe()
{
value_type n = norm();
if (n) {
for (uint i = 0; i < N; i++) {
this->_coord[i] /= n;
}
}
return *this;
}
// classical operators
inline Vec<T, N> operator+(const Vec<T, N> &v) const
{
Vec<T, N> res(v);
res += *this;
return res;
}
inline Vec<T, N> operator-(const Vec<T, N> &v) const
{
Vec<T, N> res(*this);
res -= v;
return res;
}
inline Vec<T, N> operator*(const typename Vec<T, N>::value_type r) const
{
Vec<T, N> res(*this);
res *= r;
return res;
}
inline Vec<T, N> operator/(const typename Vec<T, N>::value_type r) const
{
Vec<T, N> res(*this);
if (r) {
res /= r;
}
return res;
}
// dot product
inline value_type operator*(const Vec<T, N> &v) const
{
value_type sum = 0;
for (uint i = 0; i < N; i++) {
sum += (*this)[i] * v[i];
}
return sum;
}
template<class U> inline Vec<T, N> &operator=(const Vec<U, N> &v)
{
if (this != &v) {
for (uint i = 0; i < N; i++) {
this->_coord[i] = (T)v[i];
}
}
return *this;
}
template<class U> inline Vec<T, N> &operator+=(const Vec<U, N> &v) &
{
for (uint i = 0; i < N; i++) {
this->_coord[i] += (T)v[i];
}
return *this;
}
template<class U> inline Vec<T, N> &operator-=(const Vec<U, N> &v) &
{
for (uint i = 0; i < N; i++) {
this->_coord[i] -= (T)v[i];
}
return *this;
}
template<class U> inline Vec<T, N> &operator*=(const U r) &
{
for (uint i = 0; i < N; i++) {
this->_coord[i] *= r;
}
return *this;
}
template<class U> inline Vec<T, N> &operator/=(const U r) &
{
if (r) {
for (uint i = 0; i < N; i++) {
this->_coord[i] /= r;
}
}
return *this;
}
inline bool operator==(const Vec<T, N> &v) const
{
for (uint i = 0; i < N; i++) {
if (this->_coord[i] != v[i]) {
return false;
}
}
return true;
}
inline bool operator!=(const Vec<T, N> &v) const
{
for (uint i = 0; i < N; i++) {
if (this->_coord[i] != v[i]) {
return true;
}
}
return false;
}
inline bool operator<(const Vec<T, N> &v) const
{
for (uint i = 0; i < N; i++) {
if (this->_coord[i] < v[i]) {
return true;
}
if (this->_coord[i] > v[i]) {
return false;
}
if (this->_coord[i] == v[i]) {
continue;
}
}
return false;
}
inline bool operator>(const Vec<T, N> &v) const
{
for (uint i = 0; i < N; i++) {
if (this->_coord[i] > v[i]) {
return true;
}
if (this->_coord[i] < v[i]) {
return false;
}
if (this->_coord[i] == v[i]) {
continue;
}
}
return false;
}
protected:
value_type _coord[N];
enum {
_dim = N,
};
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:VecMat:Vec")
};
//
// Vec2 class (2D Vector)
// - T: value type
//
/////////////////////////////////////////////////////////////////////////////
template<class T> class Vec2 : public Vec<T, 2> {
public:
typedef typename Vec<T, 2>::value_type value_type;
inline Vec2() : Vec<T, 2>() {}
template<class U> explicit inline Vec2(const U tab[2]) : Vec<T, 2>(tab) {}
template<class U> explicit inline Vec2(const std::vector<U> &tab) : Vec<T, 2>(tab) {}
template<class U> inline Vec2(const Vec<U, 2> &v) : Vec<T, 2>(v) {}
inline Vec2(const value_type x, const value_type y = 0) : Vec<T, 2>()
{
this->_coord[0] = (T)x;
this->_coord[1] = (T)y;
}
inline value_type x() const
{
return this->_coord[0];
}
inline value_type &x()
{
return this->_coord[0];
}
inline value_type y() const
{
return this->_coord[1];
}
inline value_type &y()
{
return this->_coord[1];
}
inline void setX(const value_type v)
{
this->_coord[0] = v;
}
inline void setY(const value_type v)
{
this->_coord[1] = v;
}
// FIXME: hack swig -- no choice
inline Vec2<T> operator+(const Vec2<T> &v) const
{
Vec2<T> res(v);
res += *this;
return res;
}
inline Vec2<T> operator-(const Vec2<T> &v) const
{
Vec2<T> res(*this);
res -= v;
return res;
}
inline Vec2<T> operator*(const value_type r) const
{
Vec2<T> res(*this);
res *= r;
return res;
}
inline Vec2<T> operator/(const value_type r) const
{
Vec2<T> res(*this);
if (r) {
res /= r;
}
return res;
}
// dot product
inline value_type operator*(const Vec2<T> &v) const
{
value_type sum = 0;
for (uint i = 0; i < 2; i++) {
sum += (*this)[i] * v[i];
}
return sum;
}
};
//
// HVec3 class (3D Vector in homogeneous coordinates)
// - T: value type
//
/////////////////////////////////////////////////////////////////////////////
template<class T> class HVec3 : public Vec<T, 4> {
public:
typedef typename Vec<T, 4>::value_type value_type;
inline HVec3() : Vec<T, 4>() {}
template<class U> explicit inline HVec3(const U tab[4]) : Vec<T, 4>(tab) {}
template<class U> explicit inline HVec3(const std::vector<U> &tab) : Vec<T, 4>(tab) {}
template<class U> inline HVec3(const Vec<U, 4> &v) : Vec<T, 4>(v) {}
inline HVec3(const value_type sx,
const value_type sy = 0,
const value_type sz = 0,
const value_type s = 1)
{
this->_coord[0] = sx;
this->_coord[1] = sy;
this->_coord[2] = sz;
this->_coord[3] = s;
}
template<class U> inline HVec3(const Vec<U, 3> &sv, const U s = 1)
{
this->_coord[0] = (T)sv[0];
this->_coord[1] = (T)sv[1];
this->_coord[2] = (T)sv[2];
this->_coord[3] = (T)s;
}
inline value_type sx() const
{
return this->_coord[0];
}
inline value_type &sx()
{
return this->_coord[0];
}
inline value_type sy() const
{
return this->_coord[1];
}
inline value_type &sy()
{
return this->_coord[1];
}
inline value_type sz() const
{
return this->_coord[2];
}
inline value_type &sz()
{
return this->_coord[2];
}
inline value_type s() const
{
return this->_coord[3];
}
inline value_type &s()
{
return this->_coord[3];
}
// Access to non-homogeneous coordinates in 3D
inline value_type x() const
{
return this->_coord[0] / this->_coord[3];
}
inline value_type y() const
{
return this->_coord[1] / this->_coord[3];
}
inline value_type z() const
{
return this->_coord[2] / this->_coord[3];
}
};
//
// Vec3 class (3D Vec)
// - T: value type
//
/////////////////////////////////////////////////////////////////////////////
template<class T> class Vec3 : public Vec<T, 3> {
public:
typedef typename Vec<T, 3>::value_type value_type;
inline Vec3() : Vec<T, 3>() {}
template<class U> explicit inline Vec3(const U tab[3]) : Vec<T, 3>(tab) {}
template<class U> explicit inline Vec3(const std::vector<U> &tab) : Vec<T, 3>(tab) {}
template<class U> inline Vec3(const Vec<U, 3> &v) : Vec<T, 3>(v) {}
template<class U> inline Vec3(const HVec3<U> &v)
{
this->_coord[0] = (T)v.x();
this->_coord[1] = (T)v.y();
this->_coord[2] = (T)v.z();
}
inline Vec3(const value_type x, const value_type y = 0, const value_type z = 0) : Vec<T, 3>()
{
this->_coord[0] = x;
this->_coord[1] = y;
this->_coord[2] = z;
}
inline value_type x() const
{
return this->_coord[0];
}
inline value_type &x()
{
return this->_coord[0];
}
inline value_type y() const
{
return this->_coord[1];
}
inline value_type &y()
{
return this->_coord[1];
}
inline value_type z() const
{
return this->_coord[2];
}
inline value_type &z()
{
return this->_coord[2];
}
inline void setX(const value_type v)
{
this->_coord[0] = v;
}
inline void setY(const value_type v)
{
this->_coord[1] = v;
}
inline void setZ(const value_type v)
{
this->_coord[2] = v;
}
// classical operators
// FIXME: hack swig -- no choice
inline Vec3<T> operator+(const Vec3<T> &v) const
{
Vec3<T> res(v);
res += *this;
return res;
}
inline Vec3<T> operator-(const Vec3<T> &v) const
{
Vec3<T> res(*this);
res -= v;
return res;
}
inline Vec3<T> operator*(const value_type r) const
{
Vec3<T> res(*this);
res *= r;
return res;
}
inline Vec3<T> operator/(const value_type r) const
{
Vec3<T> res(*this);
if (r) {
res /= r;
}
return res;
}
// dot product
inline value_type operator*(const Vec3<T> &v) const
{
value_type sum = 0;
for (uint i = 0; i < 3; i++) {
sum += (*this)[i] * v[i];
}
return sum;
}
// cross product for 3D Vectors
// FIXME: hack swig -- no choice
inline Vec3<T> operator^(const Vec3<T> &v) const
{
Vec3<T> res((*this)[1] * v[2] - (*this)[2] * v[1],
(*this)[2] * v[0] - (*this)[0] * v[2],
(*this)[0] * v[1] - (*this)[1] * v[0]);
return res;
}
// cross product for 3D Vectors
template<typename U> inline Vec3<T> operator^(const Vec<U, 3> &v) const
{
Vec3<T> res((*this)[1] * v[2] - (*this)[2] * v[1],
(*this)[2] * v[0] - (*this)[0] * v[2],
(*this)[0] * v[1] - (*this)[1] * v[0]);
return res;
}
};
//
// Matrix class
// - T: value type
// - M: rows
// - N: cols
//
/////////////////////////////////////////////////////////////////////////////
// Dirty, but icc under Windows needs this
#define _SIZE (M * N)
template<class T, uint M, uint N> class Matrix {
public:
typedef T value_type;
inline Matrix()
{
for (uint i = 0; i < _SIZE; i++) {
this->_coord[i] = 0;
}
}
~Matrix()
{
Internal::is_false<(M == 0)>::ensure();
Internal::is_false<(N == 0)>::ensure();
}
template<class U> explicit inline Matrix(const U tab[_SIZE])
{
for (uint i = 0; i < _SIZE; i++) {
this->_coord[i] = tab[i];
}
}
template<class U> explicit inline Matrix(const std::vector<U> &tab)
{
for (uint i = 0; i < _SIZE; i++) {
this->_coord[i] = tab[i];
}
}
template<class U> inline Matrix(const Matrix<U, M, N> &m)
{
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] = (T)m(i, j);
}
}
}
inline value_type operator()(const uint i, const uint j) const
{
return this->_coord[i * N + j];
}
inline value_type &operator()(const uint i, const uint j)
{
return this->_coord[i * N + j];
}
static inline uint rows()
{
return M;
}
static inline uint cols()
{
return N;
}
inline Matrix<T, M, N> &transpose() const
{
Matrix<T, N, M> res;
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
res(j, i) = this->_coord[i * N + j];
}
}
*this = res;
return *this;
}
template<class U> inline Matrix<T, M, N> &operator=(const Matrix<U, M, N> &m)
{
if (this != &m) {
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] = (T)m(i, j);
}
}
}
return *this;
}
template<class U> inline Matrix<T, M, N> &operator+=(const Matrix<U, M, N> &m) &
{
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] += (T)m(i, j);
}
}
return *this;
}
template<class U> inline Matrix<T, M, N> &operator-=(const Matrix<U, M, N> &m) &
{
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] -= (T)m(i, j);
}
}
return *this;
}
template<class U> inline Matrix<T, M, N> &operator*=(const U lambda) &
{
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] *= lambda;
}
}
return *this;
}
template<class U> inline Matrix<T, M, N> &operator/=(const U lambda) &
{
if (lambda) {
for (uint i = 0; i < M; i++) {
for (uint j = 0; j < N; j++) {
this->_coord[i * N + j] /= lambda;
}
}
}
return *this;
}
protected:
value_type _coord[_SIZE];
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:VecMat:Matrix")
};
#undef _SIZE
//
// SquareMatrix class
// - T: value type
// - N: rows & cols
//
/////////////////////////////////////////////////////////////////////////////
// Dirty, but icc under Windows needs this
#define _SIZE (N * N)
template<class T, uint N> class SquareMatrix : public Matrix<T, N, N> {
public:
typedef T value_type;
inline SquareMatrix() : Matrix<T, N, N>() {}
template<class U> explicit inline SquareMatrix(const U tab[_SIZE]) : Matrix<T, N, N>(tab) {}
template<class U> explicit inline SquareMatrix(const std::vector<U> &tab) : Matrix<T, N, N>(tab)
{
}
template<class U> inline SquareMatrix(const Matrix<U, N, N> &m) : Matrix<T, N, N>(m) {}
static inline SquareMatrix<T, N> identity()
{
SquareMatrix<T, N> res;
for (uint i = 0; i < N; i++) {
res(i, i) = 1;
}
return res;
}
};
#undef _SIZE
//
// Vector external functions
//
/////////////////////////////////////////////////////////////////////////////
#if 0
template<class T, uint N> inline Vec<T, N> operator+(const Vec<T, N> &v1, const Vec<T, N> &v2)
{
Vec<T, N> res(v1);
res += v2;
return res;
}
template<class T, uint N> inline Vec<T, N> operator-(const Vec<T, N> &v1, const Vec<T, N> &v2)
{
Vec<T, N> res(v1);
res -= v2;
return res;
}
template<class T, uint N>
inline Vec<T, N> operator*(const Vec<T, N> &v, const typename Vec<T, N>::value_type r)
{
Vec<T, N> res(v);
res *= r;
return res;
}
#endif
template<class T, uint N>
inline Vec<T, N> operator*(const typename Vec<T, N>::value_type r, const Vec<T, N> &v)
{
Vec<T, N> res(v);
res *= r;
return res;
}
#if 0
template<class T, uint N>
inline Vec<T, N> operator/(const Vec<T, N> &v, const typename Vec<T, N>::value_type r)
{
Vec<T, N> res(v);
if (r) {
res /= r;
}
return res;
}
// dot product
template<class T, uint N>
inline typename Vec<T, N>::value_type operator*(const Vec<T, N> &v1, const Vec<T, N> &v2)
{
typename Vec<T, N>::value_type sum = 0;
for (uint i = 0; i < N; i++) {
sum += v1[i] * v2[i];
}
return sum;
}
// cross product for 3D Vectors
template<typename T> inline Vec3<T> operator^(const Vec<T, 3> &v1, const Vec<T, 3> &v2)
{
Vec3<T> res(
v1[1] * v2[2] - v1[2] * v2[1], v1[2] * v2[0] - v1[0] * v2[2], v1[0] * v2[1] - v1[1] * v2[0]);
return res;
}
#endif
// stream operator
template<class T, uint N> inline std::ostream &operator<<(std::ostream &s, const Vec<T, N> &v)
{
uint i;
s << "[";
for (i = 0; i < N - 1; i++) {
s << v[i] << ", ";
}
s << v[i] << "]";
return s;
}
//
// Matrix external functions
//
/////////////////////////////////////////////////////////////////////////////
template<class T, uint M, uint N>
inline Matrix<T, M, N> operator+(const Matrix<T, M, N> &m1, const Matrix<T, M, N> &m2)
{
Matrix<T, M, N> res(m1);
res += m2;
return res;
}
template<class T, uint M, uint N>
inline Matrix<T, M, N> operator-(const Matrix<T, M, N> &m1, const Matrix<T, M, N> &m2)
{
Matrix<T, M, N> res(m1);
res -= m2;
return res;
}
template<class T, uint M, uint N>
inline Matrix<T, M, N> operator*(const Matrix<T, M, N> &m1,
const typename Matrix<T, M, N>::value_type lambda)
{
Matrix<T, M, N> res(m1);
res *= lambda;
return res;
}
template<class T, uint M, uint N>
inline Matrix<T, M, N> operator*(const typename Matrix<T, M, N>::value_type lambda,
const Matrix<T, M, N> &m1)
{
Matrix<T, M, N> res(m1);
res *= lambda;
return res;
}
template<class T, uint M, uint N>
inline Matrix<T, M, N> operator/(const Matrix<T, M, N> &m1,
const typename Matrix<T, M, N>::value_type lambda)
{
Matrix<T, M, N> res(m1);
res /= lambda;
return res;
}
template<class T, uint M, uint N, uint P>
inline Matrix<T, M, P> operator*(const Matrix<T, M, N> &m1, const Matrix<T, N, P> &m2)
{
uint i, j, k;
Matrix<T, M, P> res;
typename Matrix<T, N, P>::value_type scale;
for (j = 0; j < P; j++) {
for (k = 0; k < N; k++) {
scale = m2(k, j);
for (i = 0; i < N; i++) {
res(i, j) += m1(i, k) * scale;
}
}
}
return res;
}
template<class T, uint M, uint N>
inline Vec<T, M> operator*(const Matrix<T, M, N> &m, const Vec<T, N> &v)
{
Vec<T, M> res;
typename Matrix<T, M, N>::value_type scale;
for (uint j = 0; j < M; j++) {
scale = v[j];
for (uint i = 0; i < N; i++) {
res[i] += m(i, j) * scale;
}
}
return res;
}
// stream operator
template<class T, uint M, uint N>
inline std::ostream &operator<<(std::ostream &s, const Matrix<T, M, N> &m)
{
uint i, j;
for (i = 0; i < M; i++) {
s << "[";
for (j = 0; j < N - 1; j++) {
s << m(i, j) << ", ";
}
s << m(i, j) << "]" << std::endl;
}
return s;
}
} // end of namespace VecMat
} /* namespace Freestyle */

View File

@@ -0,0 +1,233 @@
/* SPDX-FileCopyrightText: 2000 `Bruno Levy <levy@loria.fr>`
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* The Original Code is:
* - GXML/Graphite: Geometry and Graphics Programming Library + Utilities.
*/
/** \file
* \ingroup freestyle
*/
#include "matrix_util.h"
#include "BLI_math_base.h"
#include "BLI_utildefines.h"
namespace Freestyle::OGF::MatrixUtil {
static const double EPS = 0.00001;
static int MAX_ITER = 100;
void semi_definite_symmetric_eigen(const double *mat, int n, double *eigen_vec, double *eigen_val)
{
double *a, *v;
double a_norm, a_normEPS, thr, thr_nn;
int nb_iter = 0;
int jj;
int i, j, k, ij, ik, l, m, lm, mq, lq, ll, mm, imv, im, iq, ilv, il, nn;
int *index;
double a_ij, a_lm, a_ll, a_mm, a_im, a_il;
double a_lm_2;
double v_ilv, v_imv;
double x;
double sinx, sinx_2, cosx, cosx_2, sincos;
double delta;
// Number of entries in mat
nn = (n * (n + 1)) / 2;
// Step 1: Copy mat to a
a = new double[nn];
for (ij = 0; ij < nn; ij++) {
a[ij] = mat[ij];
}
// Ugly Fortran-porting trick: indices for a are between 1 and n
a--;
// Step 2 : Init diagonalization matrix as the unit matrix
v = new double[n * n];
ij = 0;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
if (i == j) {
v[ij++] = 1.0;
}
else {
v[ij++] = 0.0;
}
}
}
// Ugly Fortran-porting trick: indices for v are between 1 and n
v--;
// Step 3 : compute the weight of the non diagonal terms
ij = 1;
a_norm = 0.0;
for (i = 1; i <= n; i++) {
for (j = 1; j <= i; j++) {
if (i != j) {
a_ij = a[ij];
a_norm += a_ij * a_ij;
}
ij++;
}
}
if (a_norm != 0.0) {
a_normEPS = a_norm * EPS;
thr = a_norm;
// Step 4 : rotations
while (thr > a_normEPS && nb_iter < MAX_ITER) {
nb_iter++;
thr_nn = thr / nn;
for (l = 1; l < n; l++) {
for (m = l + 1; m <= n; m++) {
// compute sinx and cosx
lq = (l * l - l) / 2;
mq = (m * m - m) / 2;
lm = l + mq;
a_lm = a[lm];
a_lm_2 = a_lm * a_lm;
if (a_lm_2 < thr_nn) {
continue;
}
ll = l + lq;
mm = m + mq;
a_ll = a[ll];
a_mm = a[mm];
delta = a_ll - a_mm;
if (delta == 0.0) {
x = -M_PI_4;
}
else {
x = -atan((a_lm + a_lm) / delta) / 2.0;
}
sinx = sin(x);
cosx = cos(x);
sinx_2 = sinx * sinx;
cosx_2 = cosx * cosx;
sincos = sinx * cosx;
// rotate L and M columns
ilv = n * (l - 1);
imv = n * (m - 1);
for (i = 1; i <= n; i++) {
if (!ELEM(i, l, m)) {
iq = (i * i - i) / 2;
if (i < m) {
im = i + mq;
}
else {
im = m + iq;
}
a_im = a[im];
if (i < l) {
il = i + lq;
}
else {
il = l + iq;
}
a_il = a[il];
a[il] = a_il * cosx - a_im * sinx;
a[im] = a_il * sinx + a_im * cosx;
}
ilv++;
imv++;
v_ilv = v[ilv];
v_imv = v[imv];
v[ilv] = cosx * v_ilv - sinx * v_imv;
v[imv] = sinx * v_ilv + cosx * v_imv;
}
x = a_lm * sincos;
x += x;
a[ll] = a_ll * cosx_2 + a_mm * sinx_2 - x;
a[mm] = a_ll * sinx_2 + a_mm * cosx_2 + x;
a[lm] = 0.0;
thr = fabs(thr - a_lm_2);
}
}
}
}
// Step 5: index conversion and copy eigen values
// back from Fortran to C++
a++;
for (i = 0; i < n; i++) {
k = i + (i * (i + 1)) / 2;
eigen_val[i] = a[k];
}
delete[] a;
// Step 6: sort the eigen values and eigen vectors
index = new int[n];
for (i = 0; i < n; i++) {
index[i] = i;
}
for (i = 0; i < (n - 1); i++) {
x = eigen_val[i];
k = i;
for (j = i + 1; j < n; j++) {
if (x < eigen_val[j]) {
k = j;
x = eigen_val[j];
}
}
eigen_val[k] = eigen_val[i];
eigen_val[i] = x;
jj = index[k];
index[k] = index[i];
index[i] = jj;
}
// Step 7: save the eigen vectors
// back from Fortran to C++
v++;
ij = 0;
for (k = 0; k < n; k++) {
ik = index[k] * n;
for (i = 0; i < n; i++) {
eigen_vec[ij++] = v[ik++];
}
}
delete[] v;
delete[] index;
}
//_________________________________________________________
} // namespace Freestyle::OGF::MatrixUtil

View File

@@ -0,0 +1,46 @@
/* SPDX-FileCopyrightText: 2000 `Bruno Levy <levy@loria.fr>`
* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* The Original Code is:
* - GXML/Graphite: Geometry and Graphics Programming Library + Utilities.
*/
#pragma once
/** \file
* \ingroup freestyle
*/
#include "../system/FreestyleConfig.h"
namespace Freestyle {
namespace OGF {
namespace MatrixUtil {
/**
* computes the eigen values and eigen vectors of a semi definite symmetric matrix
*
* \param mat: The matrix stored in column symmetric storage, i.e.
* <pre>
* matrix = { m11, m12, m22, m13, m23, m33, m14, m24, m34, m44 ... }
* size = n(n+1)/2
* </pre>
*
* \param eigen_vec: (return) = { v1, v2, v3, ..., vn }
* where `vk = vk0, vk1, ..., vkn`
* `size = n^2`, must be allocated by caller.
*
* \param eigen_val: (return) are in decreasing order
* `size = n`, must be allocated by caller.
*/
void semi_definite_symmetric_eigen(const double *mat, int n, double *eigen_vec, double *eigen_val);
} // namespace MatrixUtil
} // namespace OGF
} /* namespace Freestyle */

View File

@@ -0,0 +1,66 @@
/* SPDX-FileCopyrightText: 2000 `Bruno Levy <levy@loria.fr>`
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* The Original Code is:
* - OGF/Graphite: Geometry and Graphics Programming Library + Utilities.
*/
/** \file
* \ingroup freestyle
*/
#include "normal_cycle.h"
#include "matrix_util.h"
namespace Freestyle::OGF {
//_________________________________________________________
void NormalCycle::begin()
{
M_[0] = M_[1] = M_[2] = M_[3] = M_[4] = M_[5] = 0;
}
void NormalCycle::end()
{
double eigen_vectors[9];
MatrixUtil::semi_definite_symmetric_eigen(M_, 3, eigen_vectors, eigen_value_);
axis_[0] = Vec3r(eigen_vectors[0], eigen_vectors[1], eigen_vectors[2]);
axis_[1] = Vec3r(eigen_vectors[3], eigen_vectors[4], eigen_vectors[5]);
axis_[2] = Vec3r(eigen_vectors[6], eigen_vectors[7], eigen_vectors[8]);
// Normalize the eigen vectors
for (int i = 0; i < 3; i++) {
axis_[i].normalize();
}
// Sort the eigen vectors
i_[0] = 0;
i_[1] = 1;
i_[2] = 2;
double l0 = ::fabs(eigen_value_[0]);
double l1 = ::fabs(eigen_value_[1]);
double l2 = ::fabs(eigen_value_[2]);
if (l1 > l0) {
ogf_swap(l0, l1);
ogf_swap(i_[0], i_[1]);
}
if (l2 > l1) {
ogf_swap(l1, l2);
ogf_swap(i_[1], i_[2]);
}
if (l1 > l0) {
ogf_swap(l0, l1);
ogf_swap(i_[0], i_[1]);
}
}
//_________________________________________________________
} // namespace Freestyle::OGF

View File

@@ -0,0 +1,123 @@
/* SPDX-FileCopyrightText: 2000 `Bruno Levy <levy@loria.fr>`
* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* The Original Code is:
* - OGF/Graphite: Geometry and Graphics Programming Library + Utilities.
*/
#pragma once
/** \file
* \ingroup freestyle
*/
#include "Geom.h"
#include "../system/FreestyleConfig.h"
#include "MEM_guardedalloc.h"
namespace Freestyle {
using namespace Geometry;
namespace OGF {
template<class T> inline void ogf_swap(T &x, T &y)
{
T z = x;
x = y;
y = z;
}
//_________________________________________________________
/**
* NormalCycle evaluates the curvature tensor in function
* of a set of dihedral angles and associated vectors.
* Reference:
* Restricted Delaunay Triangulation and Normal Cycle,
* D. Cohen-Steiner and J.M. Morvan,
* SOCG 2003
*/
class NormalCycle {
public:
void begin();
void end();
/**
* NOTE: the specified edge vector needs to be pre-clipped by the neighborhood.
*/
void accumulate_dihedral_angle(const Vec3r &edge, real angle, real neigh_area = 1.0);
const Vec3r &eigen_vector(int i) const
{
return axis_[i_[i]];
}
real eigen_value(int i) const
{
return eigen_value_[i_[i]];
}
const Vec3r &N() const
{
return eigen_vector(2);
}
const Vec3r &Kmax() const
{
return eigen_vector(1);
}
const Vec3r &Kmin() const
{
return eigen_vector(0);
}
real n() const
{
return eigen_value(2);
}
real kmax() const
{
return eigen_value(1);
}
real kmin() const
{
return eigen_value(0);
}
private:
/* UNUSED */
// real center_[3];
Vec3r axis_[3];
real eigen_value_[3];
real M_[6];
int i_[3];
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:OGF:NormalCycle")
};
inline void NormalCycle::accumulate_dihedral_angle(const Vec3r &edge,
const double beta,
double neigh_area)
{
double s = beta * neigh_area / edge.norm();
M_[0] += s * edge.x() * edge.x();
M_[1] += s * edge.x() * edge.y();
M_[2] += s * edge.y() * edge.y();
M_[3] += s * edge.x() * edge.z();
M_[4] += s * edge.y() * edge.z();
M_[5] += s * edge.z() * edge.z();
}
//_________________________________________________________
} // namespace OGF
} /* namespace Freestyle */