Files
create/src/Mod/Path/libarea/kurve/Construction.cpp
2015-07-23 23:17:15 -03:00

869 lines
28 KiB
C++
Raw Blame History

// ***************************************************************************************************************************************
// Point, CLine & Circle classes part of geometry.lib
// ***************************************************************************************************************************************
/*==============================
Copyright (c) 2006 g.j.hawkesford
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
==============================*/
#include "geometry.h"
using namespace geoff_geometry;
namespace geoff_geometry {
int UNITS = MM;
double TOLERANCE = 1.0e-06;
double TOLERANCE_SQ = TOLERANCE * TOLERANCE;
double TIGHT_TOLERANCE = 1.0e-09;
double UNIT_VECTOR_TOLERANCE = 1.0e-10;
double RESOLUTION = 1.0e-06;
// dummy functions
const wchar_t* getMessage(const wchar_t* original, int messageGroup, int stringID){return original;}
const wchar_t* getMessage(const wchar_t* original){return original;}
void FAILURE(const wchar_t* str){throw(str);}
void FAILURE(const std::wstring& str){throw(str);}
void set_Tolerances(int mode) {
UNIT_VECTOR_TOLERANCE = 1.0e-10;
switch (UNITS = mode)
{
case MM:
geoff_geometry::TOLERANCE = 1.0e-03; // Peps
RESOLUTION = 1.0e-03;
TIGHT_TOLERANCE = 1.0e-06;
break;
case INCHES:
TOLERANCE = 1.0e-04; // Peps
RESOLUTION = 1.0e-04;
TIGHT_TOLERANCE = 1.0e-7;
break;
case METRES:
TOLERANCE = 1.0e-06; // p4c...SW
RESOLUTION = 1.0e-06;
TIGHT_TOLERANCE = 1.0e-09;
break;
default:
FAILURE(L"INVALID UNITS");
}
TOLERANCE_SQ = TOLERANCE * TOLERANCE;
}
double mm(double value) {
switch(UNITS) {
default:
return value;
case METRES:
return value * .001;
case INCHES:
return value / 25.4;
}
}
// ostream operators = non-member overload
// *********************************************************************************************************
wostream& operator << (wostream& op, Point& p){
// for debug - print point to file
if(p.ok == false)
op << L" ok=\"false\"";
else
op << L" x=\"" << p.x << L"\" y=\"" << p.y << L"\"";
return op;
}
wostream& operator <<(wostream& op, CLine& cl){
// for debug - print cline to file
if(cl.ok == false)
op << L"(CLine UNSET)";
else
op << L"sp=" << cl.p << L" v=" << cl.v;
return op;
}
wostream& operator <<(wostream& op, Plane& pl){
// for debug - print plane to file stream
if(pl.ok == false)
op << L"(Plane UNSET)";
else
op << L"d=" << pl.d << L" normal=" << pl.normal;
return op;
}
ostream& operator << (ostream& op, Point3d& p){
// for debug - print point to file
// if(p.ok == false)
// op << "ok=\"false\"";
// else
op << "x=\"" << p.x << "\" y=\"" << p.y << "\" z=" << p.z << "\"";
return op;
}
wostream& operator <<(wostream& op, Vector2d& v){
// for debug - print vector to file
op << L"(" << v.getx() << L", " << v.gety() << L")";
return op;
}
wostream& operator <<(wostream& op, Vector3d& v){
// for debug - print vector to file
op << L"(" << v.getx() << L", " << v.gety() << L"," << v.getz() << L")";
return op;
}
wostream& operator <<(wostream& op, Circle& c){
// for debug - print circle to file
if(c.ok == false)
op << L"ok=\"false\"";
else
op << L" x=\"" << c.pc.x << L"\" y=\"" << c.pc.y << L"\" radius=\"" << c.radius << L"\"";
return op;
}
wostream& operator <<(wostream& op, Span& sp){
// for debug - print span to file stream
op << L"p0 = " << sp.p0 << L" p1=" << sp.p1;
if(sp.dir) {
op << L" pc=" << sp.pc << L" dir=" << ((sp.dir == CW)?L"CW" : L"ACW") << L" radius=" << sp.radius;
}
return op;
}
// ***************************************************************************************************************************************
// point classes
// ***************************************************************************************************************************************
Point::Point( const Point3d& p ) { // copy constructor Point p1(p2);
x = p.x;
y = p.y;
// ok = p.ok;
ok = true;
}
Point::Point(const Vector2d& v)
{
x = v.getx(); y = v.gety();
}
Point3d::Point3d(const Vector3d& v) {
x = v.getx(); y = v.gety(); z = v.getz();// ok = true;
}
bool Point3d::operator==(const Point3d &p)const{
// p1 == p2 (uses TOLERANCE)
if(FNE(this->x, p.x, TOLERANCE) || FNE(this->y, p.y, TOLERANCE) || FNE(this->z, p.z, TOLERANCE)) return false;
return true;
}
Point Point::Transform(const Matrix& m) {
// transform Point
Point ret;
m.Transform2d(&x, &ret.x);
ret.ok = true;
return ret;
}
Point3d Point3d::Transform(const Matrix& m) {
// transform Point
Point3d ret;
m.Transform(&x, &ret.x);
// ret.ok = true;
return ret;
}
Point Point::operator+(const Vector2d &v)const{
return Point(x + v.getx(), y + v.gety());
}
Point3d Point3d::operator+(const Vector3d &v)const{
return Point3d(x + v.getx(), y + v.gety(), z + v.getz());
}
bool Point::operator==(const Point &p) const{
// p1 == p2 (uses TOLERANCE)
if(FNE(this->x, p.x, TOLERANCE) || FNE(this->y, p.y, TOLERANCE)) return false;
return true;
}
double Point::Dist(const Point& p)const{ // distance between 2 points
return Vector2d(*this, p).magnitude();
}
double Point::DistSq(const Point& p)const{ // distance squared between 2 points
return Vector2d(*this, p).magnitudesqd();
}
double Point3d::Dist(const Point3d& p)const { // distance between 2 points
return Vector3d(*this, p).magnitude();
}
double Point3d::DistSq(const Point3d& p)const { // distance squared
return (this->x - p.x) * (this->x - p.x) + (this->y - p.y) * (this->y - p.y) + (this->z - p.z) * (this->z - p.z);
}
Point Point::Mid(const Point& p1, double factor)const{
// Mid
return geoff_geometry::Mid(*this, p1, factor);
}
Point3d Point3d::Mid(const Point3d& p, double factor)const{
// Mid
return Vector3d(*this, p) * factor + *this;
}
Point Mid(const Point& p0, const Point& p1, double factor){
// mid or partway between 2 points
return Vector2d(p0, p1) * factor + p0;
}
Point Rel(const Point& p, double x0, double y0) {
// Relative point
return (p.ok)?Point(p.x + x0, p.y + y0) : INVALID_POINT;
}
Point Polar(const Point& p, double angle, double r) {
// polar from this point
angle *= DegreesToRadians;
return (p.ok)?Point(p.x + r * cos(angle), p.y + r * sin(angle)) : INVALID_POINT;
}
// ***************************************************************************************************************************************
// clines
// ***************************************************************************************************************************************
//const CLine horiz(Point(0, 0), 1, 0); // define global horizontal line
double CLine::c() {
// returns c for ax + by + c = 0 format (peps format where needed)
return (v.getx() * p.y - v.gety() * p.x);
}
void CLine::Normalise() {
// normalise the cline vector
ok = v.normalise() >= TOLERANCE;
}
CLine::CLine(const Span& sp){
p = sp.p0;
v = sp.vs;
ok = sp.returnSpanProperties && !sp.NullSpan;
}
CLine Normal(const CLine& s) {
// returns normal to this line
return CLine(s.p, ~s.v, false);
}
const CLine CLine::operator ~(void){
return CLine(this->p, ~v, false);
}
CLine Normal(const CLine& s, const Point& p) {
// returns normal to this line thro' p
return CLine(p, ~s.v, false);
}
CLine CLine::Transform(Matrix& m) {
Point p0 = this->p;
Point p1(p0.x + v.getx(), p0.y + v.gety());
return CLine(p0.Transform(m), p1.Transform(m));
}
double CLine::Dist(const Point& p0)const {
// distance between cline & point >0 cw about point <0 acw about point
return this->v ^ Vector2d(p0, this->p);
}
double Point::Dist(const CLine& cl)const {
// distance between cline & point >0 cw about point <0 acw about point
return cl.v ^ Vector2d(*this, cl.p);
}
Point CLine::Intof(const CLine& s) {
// Intof 2 Clines
return geoff_geometry::Intof(*this, s);
}
Point CLine::Intof(int NF, const Circle& c) {
// Intof Cline & Circleconst
return geoff_geometry::Intof(NF, *this, c);
}
Point CLine::Intof(int NF, const Circle& c, Point& otherInters) {
// Intof Cline & Circle & other intersection
return geoff_geometry::Intof(NF, *this, c, otherInters);
}
Point Intof(const CLine& s0, const CLine& s1) {
// inters of 2 clines (parameterise lines x = x0 + t * dx)
double cp = s1.v ^ s0.v;
if(fabs (cp) > 1.0e-6) {
double t = (s1.v ^ Vector2d(s0.p, s1.p)) / cp;
return s0.v * t + s0.p;
}
return INVALID_POINT;
}
Point XonCLine(CLine& s, double xval) {
// return point given X on a line
return Intof(s, CLine(Point(xval,0),0,1,false));
}
Point YonCLine(CLine& s, double yval) {
// return point given Y on a line
return Intof(s, CLine(Point(0,yval),1,0,false));
}
Point Along(const CLine& s, double d) {
// distance along line
return Point(s.p.x + d * s.v.getx(), s.p.y + d * s.v.gety(), s.ok);
}
Point Along(const CLine& s, double d, Point& p) {
// distance along line from point
return Point(p.x + d * s.v.getx(), p.y + d * s.v.gety(), p.ok);
}
Point Around(const Circle& c, double d, const Point& p) {
// distance around circle from point
CLine radial(c.pc, p);
if(radial.ok) {
if(fabs(c.radius) > TOLERANCE ) {
double a = sin(- d / c.radius);
double b = cos(- d / c.radius);
return Point(c.pc.x - c.radius * (radial.v.gety() * a - radial.v.getx() * b), c.pc.y + c.radius * (radial.v.gety() * b + radial.v.getx() * a));
}
}
return INVALID_POINT;
}
CLine AtAngle(double angle, const Point& p0, const CLine& s) {
// cline at angle [to a cline] thro' a point
angle *= DegreesToRadians;
Vector2d v(cos(angle), sin(angle));
return CLine(p0, v.getx() * s.v.getx() - v.gety() * s.v.gety(), v.gety() * s.v.getx() + v.getx() * s.v.gety());
}
CLine Parallel(int side, const CLine& s0, double distance) {
// parallel to line by distance
Vector2d v = ~s0.v;
return CLine(v * ((double)side * distance) + s0.p, s0.v.getx(), s0.v.gety());
}
CLine Parallel(const CLine& s0, Point& p) {
// parallel to line through point
return CLine(p, s0.v.getx(), s0.v.gety());
}
CLine CLine::Bisector(const CLine& s) {
// bisector of 2 clines
return CLine (this->Intof(s), this->v.getx() + s.v.getx(), this->v.gety() + s.v.gety());
}
// ***************************************************************************************************************************************
// circle methods
// ***************************************************************************************************************************************
Circle::Circle(const Point& p, double rad, bool okay){
// Circle
pc = p;
radius = rad;
ok = pc.ok;
}
Circle::Circle( const Point& p, const Point& pc0){
if((ok = (p.ok && pc0.ok))) {
pc = pc0;
radius = p.Dist(pc0);
}
}
Circle::Circle( const Span& sp){
pc = sp.pc;
radius = sp.radius;
ok = sp.returnSpanProperties;
}
bool Circle::operator==(const Circle &c)const{
// c1 == c2 (uses TOLERANCE)
return FEQ(this->radius, c.radius, TOLERANCE) && (this->pc == c.pc);
}
Circle Circle::Transform(Matrix& m) { // transform
Point p0 = this->pc;
double scale;
if(m.GetScale(scale) == false) FAILURE(getMessage(L"Differential Scale not allowed for this method", GEOMETRY_ERROR_MESSAGES, MES_DIFFSCALE));
return Circle(p0.Transform(m), radius * scale);
}
Point Circle::Intof(int LR, const Circle& c1) {
// intof 2 circles
return geoff_geometry::Intof(LR, *this, c1);
}
Point Circle::Intof(int LR, const Circle& c1, Point& otherInters) {
// intof 2 circles, (returns the other intersection)
return geoff_geometry::Intof(LR, *this, c1, otherInters);
}
int Circle::Intof(const Circle& c1, Point& leftInters, Point& rightInters) {
// intof 2 circles, (returns the other intersection)
return geoff_geometry::Intof(*this, c1, leftInters, rightInters);
}
CLine Circle::Tanto(int AT, double angle, const CLine& s0) const{
// cline tanto circle at angle to optional cline
return geoff_geometry::Tanto(AT, *this, angle, s0);
}
CLine Tanto(int AT, const Circle& c, const Point& p) {
// CLine tangent to a circle through a point
Vector2d v(p, c.pc);
double d = v.magnitude();
CLine s(p, ~v, false); // initialise cline
if ( d < TOLERANCE || d < fabs(c.radius) - TOLERANCE) // point inside circle ?
return INVALID_CLINE;
else {
if(d > fabs(c.radius) + TOLERANCE) { // point outside circle
v.Rotate(sqrt((d - c.radius) * (d + c.radius)), - AT * c.radius);
s.v = v;
}
}
s.Normalise();
return s;
}
CLine Tanto(int AT0, const Circle& c0, int AT1, const Circle& c) {
// cline tanto 2 circles
CLine s;
Circle c1 = c;
c1.radius -= (double) (AT0 * AT1) * c0.radius;
s = Tanto(AT1, c1, c0.pc);
s.p.x += (double) AT0 * c0.radius * s.v.gety();
s.p.y -= (double) AT0 * c0.radius * s.v.getx();
return s;
}
CLine Tanto(int AT, const Circle& c, double angle, const CLine& s0) {
// cline at an angle [to a cline] tanto a circle
CLine s = AtAngle(angle, c.pc, s0);
s.p.x += (double) AT * c.radius * s.v.gety();
s.p.y -= (double) AT * c.radius * s.v.getx();
// s.p += ~s.v * (AT * c.radius);
s.ok = true;
return s;
}
Point AtAngle(const Circle& c, double angle) {
// Point at an angle on circle
angle *= DegreesToRadians;
return Point(c.pc.x + c.radius * cos(angle), c.pc.y + c.radius * sin(angle));
}
Point On(const CLine& s, const Point& p) {
// returns point that is nearest to s from p
double t = s.v * Vector2d(s.p, p);
return s.v * t + s.p;
}
Point On(const Circle& c, const Point& p) {
// returns point that is nearest to c from p
double r = p.Dist(c.pc);
if(r < TOLERANCE) FAILURE(getMessage(L",Point on Circle centre - On(Circle& c, Point& p)", GEOMETRY_ERROR_MESSAGES, MES_POINTONCENTRE));
return(Mid(p, c.pc, (r - c.radius) / r));
}
Point Intof( int NF, const CLine& s, const Circle& c) {
// inters of cline & circle eg. p1 = Intof(NEARINT, s1, c1);
Point otherInters;
return Intof(NF, s, c, otherInters);
}
Point Intof( int NF, const CLine& s, const Circle& c, Point& otherInters) {
// inters of cline & circle eg. p1 = Intof(NEARINT, s1, c1);
// otherInters returns the other intersection
#if 1
// solving x = x0 + dx * t x = y0 + dy * t
// x = xc + R * cos(a) y = yc + R * sin(a) for t
// gives :- t<> (dx<64> + dy<64>) + 2t(dx*dx0 + dy*dy0) + (x0-xc)<29> + (y0-yc)<29> - R<> = 0
int nRoots;
double t, tFar, tNear, tOther;
Vector2d v0(c.pc, s.p);
if((nRoots = quadratic(1, 2 * (v0 * s.v), v0.magnitudesqd() - c.radius * c.radius, tFar, tNear)) != 0) {
if(nRoots == 2 && NF == NEARINT) {
t = tNear;
tOther = tFar;
} else {
t = tFar;
tOther = (nRoots == 2)?tNear : tFar;
}
otherInters = s.v * tOther + s.p;
return s.v * t + s.p;
}
return INVALID_POINT;
}
#else
// geometric solution - this is similar to the peps method, and it may offer better tolerancing than above??
Point intof;
CLine normal = Normal(s, c.pc);
intof = s.Intof(normal);
double d = intof.Dist(c.pc);
if(fabs(d - c.radius) < TOLERANCE) return intof; // tangent (near enough for non-large radius I suppose?)
if(d > c.radius + TOLERANCE) return INVALID_POINT; // no intersection
double q = (c.radius - d) * (c.radius + d);
if(q < 0) return intof; // line inside tolerance
return Along(s, -(double)NF * sqrt(q), intof); // 2 intersections (return near/far case)
}
#endif
Point Intof( int intMode, const Circle& c0, const Circle& c1) {
// inters of 2 circles eg. p1 = Intof(LEFTINT, c1, c2)
Point otherInters;
return Intof(intMode, c0, c1, otherInters);
}
Point Intof( int intMode, const Circle& c0, const Circle& c1, Point& otherInters) {
// inters of 2 circles eg. p1 = Intof(LEFTINT, c1, c2);u
Point pLeft, pRight;
switch(Intof(c0, c1, pLeft, pRight)) {
default:
return INVALID_POINT;
case 1:
otherInters = pLeft;
return pLeft;
case 2:
if(intMode == LEFTINT) {
otherInters = pRight;
return pLeft;
}else {
otherInters = pLeft;
return pRight;
}
}
}
int Intof(const Circle& c0, const Circle& c1, Point& pLeft, Point& pRight) {
// inters of 2 circles
// returns the number of intersctions
Vector2d v(c0.pc, c1.pc);
double d = v.normalise();
if(d < TOLERANCE) return 0; // co-incident circles
double sum = fabs(c0.radius) + fabs(c1.radius);
double diff = fabs(fabs(c0.radius) - fabs(c1.radius));
if(d > sum + TOLERANCE || d < diff - TOLERANCE) return 0;
// dist from centre of this circle to mid intersection
double d0 = 0.5 * (d + (c0.radius + c1.radius) * (c0.radius - c1.radius) / d);
if(d0 - c0.radius > TOLERANCE) return 0; // circles don't intersect
double h = (c0.radius - d0) * (c0.radius + d0); // half distance between intersects squared
if(h < 0) d0 = c0.radius; // tangent
pLeft = v * d0 + c0.pc; // mid-point of intersects
if(h < TOLERANCE_SQ) return 1; // tangent
h = sqrt(h);
v = ~v; // calculate 2 intersects
pRight = v * h + pLeft;
v = -v;
pLeft = v * h + pLeft;
return 2;
}
Circle Tanto(int NF, CLine& s0, Point& p, double rad) {
// circle tanto a CLine thro' a point
double d = s0.Dist(p);
if(fabs(d) > rad + TOLERANCE) return INVALID_CIRCLE; // point too far from line
CLine s0offset = Parallel(RIGHTINT, s0, rad);
return Circle(Intof(NF, s0offset, Circle(p, rad)), rad);
}
Circle Tanto(int AT1, CLine& s1, int AT2, CLine& s2, double rad) {
// circle tanto 2 clines with radius
CLine Offs1 = Parallel(AT1, s1, rad);
CLine Offs2 = Parallel(AT2, s2, rad);
Point pc = Intof(Offs1, Offs2);
return (pc.ok)? Circle(pc, rad) : INVALID_CIRCLE;
}
Circle Tanto(int AT1, CLine s1, int AT2, CLine s2, int AT3, CLine s3) {
// circle tanto 3 CLines
double s1c = s1.c(), s2c = s2.c(), s3c = s3.c();
double d = s1.v.gety() * (AT2 * s3.v.getx() - AT3 * s2.v.getx())
+ s2.v.gety() * (AT3 * s1.v.getx() - AT1 * s3.v.getx())
+ s3.v.gety() * (AT1 * s2.v.getx() - AT2 * s1.v.getx());
if(fabs(d) < UNIT_VECTOR_TOLERANCE) return INVALID_CIRCLE;
double radius = (s1.v.gety() * (s2.v.getx() * s3c - s3.v.getx() * s2c)
+ s2.v.gety() * (s3.v.getx() * s1c - s1.v.getx() * s3c)
+ s3.v.gety() * (s1.v.getx() * s2c - s2.v.getx() * s1c)) / d ;
if(radius < TOLERANCE) return INVALID_CIRCLE;
CLine Offs1 = Parallel(AT1, s1, radius);
CLine Offs2 = Parallel(AT2, s2, radius);
Point p = Intof(Offs1, Offs2);
if(!p.ok) {
CLine Offs3 = Parallel(AT3, s3, radius); // s1 & s2 parallel
p = Intof(Offs1, Offs3);
if(!p.ok) return INVALID_CIRCLE; // 3 parallel lines
}
return Circle(p, radius);
}
Circle Thro(int LR, const Point& p0, const Point& p1, double rad) {
// circle thro' 2 points, given radius and side
CLine thro(p0, p1);
if(thro.ok) {
double d = 0.5 * p0.Dist(p1);
Point pm = Mid(p0, p1);
if(d > rad + TOLERANCE) return INVALID_CIRCLE;
else if(d > rad - TOLERANCE) {
// within tolerance of centre of 2 points
return Circle(pm, d);
}
else {
// 2 solutions
return Circle(Along(Normal(thro, pm), (double)LR * sqrt((rad + d) * (rad - d)), pm), rad);
}
}
return INVALID_CIRCLE;
}
Circle Thro(const Point& p0, const Point& p1) {
// circle thro 2 points (diametric)
return Circle(p0.Mid(p1), .5*p0.Dist(p1));
}
Circle Thro(const Point& p0, const Point& p1, const Point& p2) {
// circle thro 3 points
CLine s0(p0, p1);
if(!s0.ok) return Thro(p1,p2); // p0 & p1 coincident
CLine s1(p0, p2);
if(!s1.ok) return Thro(p0, p1); // p0 & p2 coincident
CLine s2(p2, p1);
if(!s2.ok) return Thro(p0, p2); // p1 & p2 coincident
Point p = Intof(Normal(s0, Mid(p0, p1)), Normal(s1, Mid(p0, p2)));
return (p.ok)? Circle(p, p0.Dist(p), true) : INVALID_CIRCLE;
}
Circle Tanto(int NF, int AT0, const CLine& s0, int AT1, const Circle &c1, double rad) {
// circle tanto cline & circle with radius
CLine Offs0 = Parallel(AT0, s0, rad);
Circle c2 = c1;
c2.radius += AT1 * rad;
Point pc = Intof(NF, Offs0, c2);
return (pc.ok)? Circle(pc, rad) : INVALID_CIRCLE;
}
Circle Tanto( int LR, int AT0, const Circle& c0, const Point& p, double rad) {
// circle tanto circle & thro' a point
Circle c2 = c0;
c2.radius += AT0 * rad;
Circle c1(p, rad);
Point pc = Intof(LR, c2, c1);
return (pc.ok)? Circle(pc, rad) : INVALID_CIRCLE;
}
Circle Tanto(int LR, int AT0, const Circle& c0, int AT1, const Circle& c1, double rad) {
// circle tanto 2 circles
Circle c2 = c0;
Circle c3 = c1;
c2.radius += AT0 * rad;
c3.radius += AT1 * rad;
Point pc = Intof(LR, c2, c3);
return (pc.ok)? Circle(pc, rad) : INVALID_CIRCLE;
}
Circle Parallel(int side, const Circle& c0, double distance) {
// parallel to circle by distance
return Circle(c0.pc, c0.radius + (double) side * distance);
}
// distance
double atn360(double dy, double dx) {
// angle 0 to 2pi
double ang = atan2(dy, dx);
return ((ang < 0)? 2 * PI + ang : ang);
}
double Dist(const Point& p0, const Circle& c, const Point& p1) {
// clockwise distance around c from p0 to p1
double a0 = atn360(p0.y - c.pc.y, p0.x - c.pc.x);
double a1 = atn360(p1.y - c.pc.y ,p1.x - c.pc.x);
if ( a1 > a0 ) a1 -= 2 * PI ;
return (a0 - a1) * c.radius;
}
double Dist(const CLine& s, const Circle& c) {
// distance between line and circle
return fabs(s.Dist(c.pc)) - c.radius;
}
double Dist(const Circle& c0, const Circle& c1) {
// distance between 2 circles
return c0.pc.Dist(c1.pc) - c0.radius - c1.radius;
}
double Dist(const Circle& c, const Point& p) {
// distance between circle and point
return p.Dist(On(c, p));
}
double IncludedAngle(const Vector2d& v0, const Vector2d& v1, int dir) {
// returns the absolute included angle between 2 vectors in the direction of dir ( 1=acw -1=cw)
double inc_ang = v0 * v1;
if(inc_ang > 1. - UNIT_VECTOR_TOLERANCE) return 0;
if(inc_ang < -1. + UNIT_VECTOR_TOLERANCE)
inc_ang = PI;
else { // dot product, v1 . v2 = cos ang
if(inc_ang > 1.0) inc_ang = 1.0;
inc_ang = acos(inc_ang); // 0 to pi radians
if(dir * (v0 ^ v1) < 0) inc_ang = 2 * PI - inc_ang ; // cp
}
return dir * inc_ang;
}
double IncludedAngle(const Vector3d& v0, const Vector3d& v1, const Vector3d& normal, int dir) {
// returns the absolute included angle between 2 vectors in the direction of dir ( 1=acw -1=cw) about normal
double inc_ang = v0 * v1;
if(inc_ang >= -NEARLY_ONE) { // dot product, v1 . v2 = cos ang
inc_ang = acos(inc_ang); // 0 to pi radians
if(dir * (normal * (v0 ^ v1)) < 0) inc_ang = 2 * PI - inc_ang ; // cp
}
else
inc_ang = PI; // semi-cicle
return dir * inc_ang;
}
int corner(const Vector2d& v0, const Vector2d& v1, double cpTol) {
// returns corner
// 0 (TANGENT) = tangent
// 1 (LEFT) = left turn
// -1 (RIGHT) = right turn
double cp = v0 ^ v1;
if(fabs(cp) < cpTol) return TANGENT;
return (cp > 0)?GEOFF_LEFT : GEOFF_RIGHT;
}
int quadratic(double a, double b, double c, double& x0, double& x1) {
// solves quadratic equation ax² + bx + c = 0
// returns number of real roots
// double epsilon = 1.0e-6;
double epsilon = (geoff_geometry::UNITS == METRES)?1.0e-09 : 1.0e-06;
double epsilonsq = epsilon * epsilon;
if(fabs(a) < epsilon) {
if(fabs(b) < epsilon) return 0; // invalid
x0 = - c / b;
return 1;
}
b /= a;
c /= a;
double s = b * b - 4 * c;
if(s < -epsilon) return 0; // imaginary roots
x0 = - 0.5 * b;
if(s > epsilonsq) {
s = 0.5 * sqrt(s);
x1 = x0 - s;
x0 += s;
return 2;
}
return 1;
}
Plane::Plane(const Point3d& p0, const Point3d& p1, const Point3d& p2) {
// constructor plane from 3 points
normal = Vector3d(p0, p1) ^ Vector3d(p0, p2);
normal.normalise();
ok = (normal != NULL_VECTOR);
d = -(normal * Vector3d(p0));
}
Plane::Plane(const Point3d& p0, const Vector3d& v, bool normalise) {
// constructor plane from point & vector
normal = v;
if(normalise == true) normal.normalise();
d = -(normal * Vector3d(p0));
}
Plane::Plane(double dist, const Vector3d& n) {
normal = n;
double mag = normal.normalise();
if((ok = (normal != NULL_VECTOR))) d = dist / mag;
}
double Plane::Dist(const Point3d& p)const{
// returns signed distance to plane from point p
return (normal * Vector3d(p)) + d;
}
Point3d Plane::Near(const Point3d& p)const {
// returns near point to p on the plane
return - normal * Dist(p) + p;
}
bool Plane::Intof(const Line& l, Point3d& intof, double& t) const{
// intersection between plane and line
// input this plane, line
// output intof
// method returns true for valid intersection
double den = l.v * this->normal;
if(fabs(den) < UNIT_VECTOR_TOLERANCE) return false; // line is parallel to the plane, return false, even if the line lies on the plane
t = -(normal * Vector3d(l.p0) + d) / den;
intof = l.v * t + l.p0;
return true;
}
bool Plane::Intof(const Plane& pl, Line& intof)const {
// intersection of 2 planes
Vector3d d = this->normal ^ pl.normal;
d.normalise();
intof.ok = false;
if(d == NULL_VECTOR) return false; // parallel planes
intof.v = d;
intof.length = 1;
double dot = this->normal * pl.normal;
double den = dot * dot - 1.;
double a = (this->d - pl.d * dot) / den;
double b = (pl.d - this->d * dot) / den;
intof.p0 = a * this->normal + b * pl.normal;
intof.ok = true;
return true;
}
bool Plane::Intof(const Plane& pl0, const Plane& pl1, Point3d& intof) const{
// intersection of 3 planes
Line tmp;
if(Intof(pl0, tmp)) {
double t;
return pl1.Intof(tmp, intof, t);
}
return false;
}
}