AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: BSD-3-Clause
// written by g.j.hawkesford 2006 for Camtek Gmbh
//
// This program is released under the BSD license. See the file COPYING for details.
//
#include "geometry.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// finite intersections
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef WIN32
# define __min(a, b) ((a < b) ? a : b)
# define __max(a, b) ((a > b) ? a : b)
#endif
namespace geoff_geometry
{
int Intof(const Span& sp0, const Span& sp1, Point& p0, Point& p1, double t[4])
{
// returns the number of intersects (lying within spans sp0, sp1)
if (sp0.box.outside(sp1.box)) {
return 0;
}
if (!sp0.dir) {
if (!sp1.dir) {
// line line
return LineLineIntof(sp0, sp1, p0, t);
}
else {
// line arc
return LineArcIntof(sp0, sp1, p0, p1, t);
}
}
else {
if (!sp1.dir) {
// arc line
return LineArcIntof(sp1, sp0, p0, p1, t);
}
else {
// arc arc
return ArcArcIntof(sp0, sp1, p0, p1);
}
}
}
int LineLineIntof(const Span& sp0, const Span& sp1, Point& p, double t[2])
{
// intersection between 2 Line2d
// returns 0 for no intersection in range of either span
// returns 1 for intersction in range of both spans
// t[0] is parameter on sp0,
// t[1] is parameter on sp1
Vector2d v0(sp0.p0, sp0.p1);
Vector2d v1(sp1.p0, sp1.p1);
Vector2d v2(sp0.p0, sp1.p0);
double cp = v1 ^ v0;
if (fabs(cp) < UNIT_VECTOR_TOLERANCE) {
p = INVALID_POINT;
return 0; // parallel or degenerate lines
}
t[0] = (v1 ^ v2) / cp;
p = v0 * t[0] + sp0.p0;
p.ok = true;
double toler = geoff_geometry::TOLERANCE / sp0.length; // calc a parametric tolerance
t[1] = (v0 ^ v2) / cp;
if (t[0] < -toler || t[0] > 1 + toler) { // intersection on first?
return 0;
}
toler = geoff_geometry::TOLERANCE / sp1.length; // calc a parametric tolerance
if (t[1] < -toler || t[1] > 1 + toler) { // intersection on second?
return 0;
}
return 1;
}
int LineArcIntof(const Span& line, const Span& arc, Point& p0, Point& p1, double t[4])
{
// inters of line arc
// solving x = x0 + dx * t x = y0 + dy * t
// x = xc + R * cos(a) y = yc + R * sin(a) for t
// gives :- t² (dx² + dy²) + 2t(dx*dx0 + dy*dy0) + (x0-xc)² + (y0-yc)² - R² = 0
int nRoots;
Vector2d v0(arc.pc, line.p0);
Vector2d v1(line.p0, line.p1);
double s = v1.magnitudesqd();
p0.ok = p1.ok = false;
if ((nRoots = quadratic(s, 2 * (v0 * v1), v0.magnitudesqd() - arc.radius * arc.radius, t[0], t[1]))
!= 0) {
double toler = geoff_geometry::TOLERANCE / sqrt(s); // calc a parametric tolerance
if (t[0] > -toler && t[0] < 1 + toler) {
p0 = v1 * t[0] + line.p0;
p0.ok = arc.OnSpan(p0, &t[2]);
}
if (nRoots == 2) {
if (t[1] > -toler && t[1] < 1 + toler) {
p1 = v1 * t[1] + line.p0;
p1.ok = arc.OnSpan(p1, &t[3]);
}
}
if (!p0.ok && p1.ok) {
p0 = p1;
p1.ok = false;
}
nRoots = (int)p0.ok + (int)p1.ok;
}
return nRoots;
}
int ArcArcIntof(const Span& arc0, const Span& arc1, Point& pLeft, Point& pRight)
{
// Intof 2 arcs
int numInts = Intof(Circle(arc0.pc, arc0.radius), Circle(arc1.pc, arc1.radius), pLeft, pRight);
if (numInts == 0) {
pLeft = arc0.p1;
pLeft.ok = false;
return 0;
}
int nLeft = arc0.OnSpan(pLeft) && arc1.OnSpan(pLeft);
int nRight = (numInts == 2) ? arc0.OnSpan(pRight) && arc1.OnSpan(pRight) : 0;
if (nLeft == 0 && nRight) {
pLeft = pRight;
}
return nLeft + nRight;
}
bool Span::OnSpan(const Point& p) const
{
double t;
return OnSpan(p, &t);
}
bool Span::OnSpan(const Point& p, double* t) const
{
// FAST OnSpan test - assumes that p lies ON the unbounded span
#if _DEBUG
if (!this->returnSpanProperties) {
FAILURE(L"OnSpan - properties no set, incorrect calling code");
}
#endif
bool ret;
if (dir == LINEAR) {
#if _DEBUG
// check p is on line
CLine cl(*this);
double d = fabs(cl.Dist(p));
if (d > geoff_geometry::TOLERANCE) {
FAILURE(L"OnSpan - point not on linear span, incorrect calling code");
}
#endif
Vector2d v0(p0, p);
*t = vs * v0;
*t = *t / length;
ret = (*t >= 0 && *t <= 1.0);
}
else {
// true if p lies on arc span sp (p must be on circle of span)
#if _DEBUG
// check that p lies on the arc
double d = p.Dist(pc);
if (FNE(d, radius, geoff_geometry::TOLERANCE)) {
FAILURE(L"OnSpan - point not on circular span, incorrect calling code");
}
#endif
#if 0 // alt method (faster, but doesn't provide t)
Vector2d v0(p0, p);
Vector2d v1(p0, p1);
// check angle to point from start
double cp;
ret = ((cp = (dir * (v0 ^ v1))) > 0);
*t = 0.0;// incorrect !!!
#else
Vector2d v = ~Vector2d(pc, p);
v.normalise();
if (dir == CW) {
v = -v;
}
double ang = IncludedAngle(vs, v, dir);
*t = ang / angle;
ret = (*t >= 0 && *t <= 1.0);
#endif
}
return ret;
}
Line::Line(const Point3d& p, const Vector3d& v0, bool boxed)
{
// constructor from point & vector
p0 = p;
v = v0;
length = v.magnitude();
if (boxed) {
minmax();
}
ok = (length > geoff_geometry::TOLERANCE);
}
Line::Line(const Point3d& p, const Point3d& p1)
{
// constructor from 2 points
p0 = p;
v = Vector3d(p, p1);
length = v.magnitude();
minmax();
ok = (length > geoff_geometry::TOLERANCE);
}
Line::Line(const Span& sp)
{
// constructor from linear span
p0 = sp.p0;
v = sp.vs * sp.length;
length = sp.length;
// box = sp.box;
box.min = Point3d(sp.box.min);
box.max = Point3d(sp.box.max);
ok = !sp.NullSpan;
}
void Line::minmax()
{
MinMax(this->p0, box.min, box.max);
MinMax(this->v + this->p0, box.min, box.max);
}
bool Line::atZ(double z, Point3d& p) const
{
// returns p at z on line
if (FEQZ(this->v.getz())) {
return false;
}
double t = (z - this->p0.z) / this->v.getz();
p = Point3d(this->p0.x + t * this->v.getx(), this->p0.y + t * this->v.gety(), z);
return true;
}
bool Line::Shortest(const Line& l2, Line& lshort, double& t1, double& t2) const
{
/*
Calculate the line segment PaPb that is the shortest route between
two lines P1P2 and P3P4. Calculate also the values of mua and mub where
Pa = P1 + t1 (P2 - P1)
Pb = P3 + t2 (P4 - P3)
Return FALSE if no solution exists. P Bourke method.
Input this 1st line
Input l2 2nd line
Output lshort shortest line between lines (if !lshort.ok, the line intersect at a point
lshort.p0) Output t1 parameter at intersection on 1st Line Output t2 parameter at intersection
on 2nd Line
*/
Vector3d v13(l2.p0, this->p0);
if (!this->ok || !l2.ok) {
return false;
}
double d1343 = v13 * l2.v; // dot products
double d4321 = l2.v * this->v;
double d1321 = v13 * this->v;
double d4343 = l2.v * l2.v;
double d2121 = this->v * this->v;
double denom = d2121 * d4343 - d4321 * d4321;
if (fabs(denom) < 1.0e-09) {
return false;
}
double numer = d1343 * d4321 - d1321 * d4343;
t1 = numer / denom;
t2 = (d1343 + d4321 * t1) / d4343;
lshort = Line(t1 * this->v + this->p0, t2 * l2.v + l2.p0);
t1 *= this->length;
t2 *= l2.length; // parameter in line length for tolerance checking
return true;
}
int Intof(const Line& l0, const Line& l1, Point3d& intof)
{
/* intersection of 2 vectors
returns 0 for intercept but not within either vector
returns 1 for intercept on both vectors
note that this routine always returns 0 for parallel vectors
method:
x = x0 + dx0 * t0 for l0
...
...
x = x1 + dx1 * t1 for l1
...
...
x0 + dx0 * t0 = x1 + dx1 * t1
dx0 * t0 - dx1 * t1 + x0 - x1 = 0
setup 3 x 3 determinent for
a0 t0 + b0 t1 + c0 = 0
a1 t0 + b1 t1 + c1 = 0
a2 t0 + b2 t1 + c2 = 0
from above a = l0.v
b = -l1.v
c = Vector3d(l1, l0)
*/
// Vector3d a = l0.v;
if (l0.box.outside(l1.box)) {
return 0;
}
Vector3d b = -l1.v;
Vector3d c = Vector3d(l1.p0, l0.p0);
Vector3d det = l0.v ^ b;
Vector3d t = b ^ c;
// choose largest determinant & corresponding parameter for accuracy
double t0 = t.getx();
double d = det.getx();
if (fabs(det.getz()) > fabs(det.gety())) {
if (fabs(det.getz()) > fabs(det.getx())) {
t0 = t.getz();
d = det.getz();
}
}
else {
if (fabs(det.gety()) > fabs(det.getx())) {
t0 = t.gety();
d = det.gety();
}
}
if (fabs(d) < 1.0e-06) {
return 0;
}
t0 /= d;
intof = l0.v * t0 + l0.p0;
Point3d other;
double t1;
if (Dist(l1, intof, other, t1) > geoff_geometry::TOLERANCE) {
return 0;
}
t0 *= l0.length;
if (t0 < -geoff_geometry::TOLERANCE || t0 > l0.length + geoff_geometry::TOLERANCE
|| t1 < -geoff_geometry::TOLERANCE || t1 > l1.length + geoff_geometry::TOLERANCE) {
return 0;
}
return 1;
}
double Dist(const Line& l, const Point3d& p, Point3d& pnear, double& t)
{
// returns the distance of a point from a line and the near point on the extended line and the
// parameter of the near point (0-length) in range
pnear = Near(l, p, t);
return p.Dist(pnear);
}
Point3d Near(const Line& l, const Point3d& p, double& t)
{
// returns the near point from a line on the extended line and the parameter of the near point
// (0-length) in range
t = (Vector3d(l.p0, p) * l.v) / l.length; // t parametrised 0 - line length
return l.v * (t / l.length) + l.p0;
}
Point3d Line::Near(const Point3d& p, double& t) const
{
// returns the near point from a line on the extended line and the parameter of the near point
// (0-length) in range
t = (Vector3d(this->p0, p) * this->v) / this->length; // t parametrised 0 - line length
return this->v * (t / this->length) + this->p0;
}
double DistSq(const Point3d* p, const Vector3d* vl, const Point3d* pf)
{
/// returns the distance squared of pf from the line given by p,vl
/// vl must be normalised
Vector3d v(*p, *pf);
Vector3d vcp = *vl ^ v;
double d = vcp.magnitudeSq(); // l * sina
return d;
}
double Dist(const Point3d* p, const Vector3d* vl, const Point3d* pf)
{
/// returns the distance of pf from the line given by p,vl
/// vl must be normalised
Vector3d v(*p, *pf);
Vector3d vcp = *vl ^ v;
double d = vcp.magnitude(); // l * sina
return d;
}
double Dist(const Span& sp, const Point& p, Point& pnear)
{
// returns distance of p from span, pnear is the nearpoint on the span (or endpoint)
if (!sp.dir) {
double d, t;
Point3d unused_pnear;
d = Dist(Line(sp), Point3d(p), unused_pnear, t);
if (t < -geoff_geometry::TOLERANCE) {
pnear = sp.p0; // nearpoint
d = pnear.Dist(p);
}
else if (t > sp.length + geoff_geometry::TOLERANCE) {
pnear = sp.p1;
d = pnear.Dist(p);
}
return d;
}
else {
// put pnear on the circle
double radiusp;
Vector2d v(sp.pc, p);
if ((radiusp = v.magnitude()) < geoff_geometry::TOLERANCE) {
// point specified on circle centre - use first point as near point
pnear = sp.p0; // nearpoint
return sp.radius;
}
else {
pnear = v * (sp.radius / radiusp) + sp.pc;
// check if projected point is on the arc
if (sp.OnSpan(pnear)) {
return fabs(radiusp - sp.radius);
}
// point not on arc so calc nearest end-point
double ndist = p.Dist(sp.p0);
double dist = p.Dist(sp.p1);
if (ndist >= dist) {
// sp.p1 is near point
pnear = sp.p1;
return dist;
}
// sp.p0 is near point
pnear = sp.p0; // nearpoint
return ndist;
}
}
}
bool OnSpan(const Span& sp, const Point& p)
{
Point nullPoint;
return OnSpan(sp, p, false, nullPoint, nullPoint);
}
bool OnSpan(const Span& sp, const Point& p, bool nearPoints, Point& pNear, Point& pOnSpan)
{
// function returns true if pNear == pOnSpan
// returns pNear & pOnSpan if nearPoints true
// pNear (nearest on unbound span)
// pOnSpan (nearest on finite span)
if (sp.dir) {
// arc
if (fabs(p.Dist(sp.pc) - sp.radius) > geoff_geometry::TOLERANCE) {
if (!nearPoints) {
return false;
}
}
pNear = On(Circle(sp.pc, sp.radius), p);
if (sp.OnSpan(pNear)) {
if (nearPoints) {
pOnSpan = pNear;
}
return true; // near point is on arc - already calculated
}
// point not on arc return the nearest end-point
if (nearPoints) {
pOnSpan = (p.Dist(sp.p0) >= p.Dist(sp.p1)) ? sp.p1 : sp.p0;
}
return false;
}
else {
// straight
if (fabs(CLine(sp.p0, sp.vs).Dist(p)) > geoff_geometry::TOLERANCE) {
if (!nearPoints) {
return false;
}
}
Vector2d v(sp.p0, p);
double t = v * sp.vs;
if (nearPoints) {
pNear = sp.vs * t + sp.p0;
}
bool onSpan = (t > -geoff_geometry::TOLERANCE && t < sp.length + geoff_geometry::TOLERANCE);
if (!onSpan) {
if (nearPoints) {
pOnSpan = (p.Dist(sp.p0) >= p.Dist(sp.p1)) ? sp.p1 : sp.p0;
}
}
else {
if (nearPoints) {
pOnSpan = pNear;
}
}
return onSpan;
}
}
// Triangle3d Constructors
Triangle3d::Triangle3d(const Point3d& p1, const Point3d& p2, const Point3d& p3)
{
vert1 = p1;
vert2 = p2;
vert3 = p3;
v0 = Vector3d(vert1, vert2);
v1 = Vector3d(vert1, vert3);
ok = true;
// set box
box.min.x = __min(__min(vert1.x, vert2.x), vert3.x);
box.min.y = __min(__min(vert1.y, vert2.y), vert3.y);
box.min.z = __min(__min(vert1.z, vert2.z), vert3.z);
box.max.x = __max(__max(vert1.x, vert2.x), vert3.x);
box.max.y = __max(__max(vert1.y, vert2.y), vert3.y);
box.max.z = __max(__max(vert1.z, vert2.z), vert3.z);
}
// Triangle3d methods
bool Triangle3d::Intof(const Line& l, Point3d& intof) const
{
// returns intersection triangle to line in intof
// function returns true for intersection, false for no intersection
// method based on Möller & Trumbore(1997) (Barycentric coordinates)
// based on incorrect Pseudo code from "Geometric Tools for Computer Graphics" p.487
if (box.outside(l.box)) {
return false;
}
Vector3d line(l.v);
line.normalise();
Vector3d p = line ^ v1; // cross product
double tmp = p * v0; // dot product
if (FEQZ(tmp)) {
return false;
}
tmp = 1 / tmp;
Vector3d s(vert1, l.p0);
double u = tmp * (s * p); // barycentric coordinate
if (u < 0 || u > 1) { // not inside triangle
return false;
}
Vector3d q = s ^ v0;
double v = tmp * (line * q); // barycentric coordinate
if (v < 0 || v > 1) { // not inside triangle
return false;
}
if (u + v > 1) { // not inside triangle
return false;
}
double t = tmp * (v1 * q);
intof = line * t + l.p0;
return true;
}
// box class
bool Box::outside(const Box& b) const
{
// returns true if this box is outside b
if (!b.ok || !this->ok) { // no box set
return false;
}
if (this->max.x < b.min.x) {
return true;
}
if (this->max.y < b.min.y) {
return true;
}
if (this->min.x > b.max.x) {
return true;
}
if (this->min.y > b.max.y) {
return true;
}
return false;
}
void Box::combine(const Box& b)
{
if (b.max.x > this->max.x) {
this->max.x = b.max.x;
}
if (b.max.y > this->max.y) {
this->max.y = b.max.y;
}
if (b.min.x < this->min.x) {
this->min.x = b.min.x;
}
if (b.min.y < this->min.y) {
this->min.y = b.min.y;
}
}
void Box3d::combine(const Box3d& b)
{
if (b.max.x > this->max.x) {
this->max.x = b.max.x;
}
if (b.max.y > this->max.y) {
this->max.y = b.max.y;
}
if (b.max.z > this->max.z) {
this->max.z = b.max.z;
}
if (b.min.x < this->min.x) {
this->min.x = b.min.x;
}
if (b.min.y < this->min.y) {
this->min.y = b.min.y;
}
if (b.min.z < this->min.z) {
this->min.z = b.min.z;
}
}
bool Box3d::outside(const Box3d& b) const
{
// returns true if this box is outside b
if (!b.ok || !this->ok) { // no box set
return false;
}
if (this->max.x < b.min.x) {
return true;
}
if (this->max.y < b.min.y) {
return true;
}
if (this->max.z < b.min.z) {
return true;
}
if (this->min.x > b.max.x) {
return true;
}
if (this->min.y > b.max.y) {
return true;
}
if (this->min.z > b.max.z) {
return true;
}
return false;
}
Line IsPtsLine(const double* a, int n, double tolerance, double* deviation)
{
// returns a Line if all points are within tolerance
// deviation is returned as the sum of all deviations of interior points to line(sp,ep)
int np = n / 3; // number of points
*deviation = 0; // cumulative deviation
if (np < 2) { // Invalid line
return Line();
}
Point3d sp(&a[0]);
Point3d ep(&a[n - 3]);
Line line(sp, ep); // line start - end
if (line.ok) {
for (int j = 1; j < np - 1; j++) {
Point3d mp(&a[j * 3]);
double t, d = 0;
if ((d = mp.Dist(line.Near(mp, t))) > tolerance) {
line.ok = false;
return line;
}
*deviation = *deviation + d;
}
}
return line;
}
} // namespace geoff_geometry