home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 52
/
Amiga_Dream_52.iso
/
Linux
/
Divers
/
freedraft.tar.gz
/
freedraft.tar
/
FREEdraft-050298
/
GEOMLIB2D
/
geomlib2d.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1998-04-27
|
41KB
|
1,455 lines
// Copyright (C) 1997 Cliff Johnson //
// Copyright (C) 1998 Cliff Johnson //
// //
// This program is free software; you can redistribute it and/or //
// modify it under the terms of the GNU General Public //
// License as published by the Free Software Foundation; either //
// version 2 of the License, or (at your option) any later version. //
// //
// This software is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
// General Public License for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this software (see COPYING.LIB); if not, write to the //
// Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// FreeEngineer 2D Drafting Library Functions - libFE2D
//
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include <math.h>
#include <vector>
#include "geomlib2d.h"
#include "point.h"
#include "line.h"
#include "segment.h"
#include "circle.h"
#include "arc.h"
#include "geomexception.h"
#include "pick.h"
#include "pickgeom.h"
#include "geom_enum.h"
//#include <menuhandler_enum.h>
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// GL2D_PointByConstraints(Pick,Pick)
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
Point GL2D_PointByConstraints(const Pick& pk1, const Pick& pk2)
throw (GeomException)
{
// point point
if( pk1.Type()==POINT && pk2.Type()==POINT)
{
Point p1 = GetPointFromPick(pk1);
Point p2 = GetPointFromPick(pk2);
return Point(p1 + 0.5 * (p2 - p1));
}
// point vs. line/segment
if( ( pk1.Type()==POINT && ( pk2.Type()==LINE || pk2.Type()==SEGMENT ) )
|| ( pk2.Type()==POINT && ( pk1.Type()==LINE || pk1.Type()==SEGMENT ) ) )
{
// swap around if necessary to get point first
Pick lpk1 = pk1;
Pick lpk2 = pk2;
if( pk2.Type()==POINT)
{
Pick lpktmp = lpk2;
lpk2 = lpk1;
lpk1 = lpktmp;
}
// get the point and line
Point p = GetPointFromPick(lpk1);
Line l = GetLineFromPick(lpk2);
// project point onto line
return l.Project(p);
}
// point vs. circle/arc
if( ( pk1.Type()==POINT && ( pk2.Type()==CIRCLE || pk2.Type()==ARC ) )
|| ( pk2.Type()==POINT && ( pk1.Type()==CIRCLE || pk1.Type()==ARC ) ) )
{
// swap around if necessary to get point first
Pick lpk1 = pk1;
Pick lpk2 = pk2;
if( pk2.Type()==POINT)
{
Pick lpktmp = lpk2;
lpk2 = lpk1;
lpk1 = lpktmp;
}
// get the point and circle
Point p = GetPointFromPick(lpk1);
Circle c = GetCircleFromPick(lpk2);
// project point onto circle
Point px1 = c.Project(p);
// find opposite solution for circle
Point px2 = ( 2 * c.Center()) - px1 ;
// return solution closest to pick 2 point
if (Distance(px1,lpk2.GetPoint()) < Distance(px2,lpk2.GetPoint()))
return px1;
return px2;
}
// line/segment vs. line/segment
if( ( pk1.Type()==LINE || pk1.Type()==SEGMENT ) &&
( pk2.Type()==LINE || pk2.Type()==SEGMENT ))
{
Line l1 = GetLineFromPick(pk1);
Line l2 = GetLineFromPick(pk2);
// solve the system
return GL2D_SolveLineLineIntersection(l1,l2);
}
// line/segment vs. circle/arc
if(( ( pk1.Type()==LINE || pk1.Type()==SEGMENT ) &&
( pk2.Type()==CIRCLE || pk2.Type()==ARC ) ) ||
( ( pk1.Type()==CIRCLE || pk1.Type()==ARC ) &&
( pk2.Type()==LINE || pk2.Type()==SEGMENT ) ) )
{
// swap around if necessary to get line/segment first
Pick lpk1 = pk1;
Pick lpk2 = pk2;
if( pk2.Type()==LINE || pk2.Type()==SEGMENT)
{
Pick lpktmp = lpk2;
lpk2 = lpk1;
lpk1 = lpktmp;
}
// get the line
Line l = GetLineFromPick(lpk1);
// get the circle;
Circle c = GetCircleFromPick(lpk2);
// get the solutions
return GL2D_SolveLineCircleIntersection(l,c,pk2.GetPoint());
}
// circle/arc vs. circle/arc
if(( pk1.Type()==CIRCLE || pk1.Type()==ARC ) &&
( pk2.Type()==CIRCLE || pk2.Type()==ARC ))
{
// in case of arcs, get circles
Circle c1 = GetCircleFromPick(pk1);
Circle c2 = GetCircleFromPick(pk2);
// solve
return GL2D_SolveCircleCircleIntersection(c1,c2,pk2.GetPoint());
}
throw GeomException("GL2D_PointByConstraints() : a Pick is not any expected type");
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
Line GL2D_LineParallel(const Line& l, const Pick& pk)
throw (GeomException)
{
switch( pk.Type())
{
// Line/Segment, Point - Parallel to Line/Segment, passing throgh point
case POINT:
{
Point p = GetPointFromPick(pk);
return Line(p,l.Direction());
break;
}
// Circle/arc - tangent to Circle/arc
case CIRCLE:
case ARC:
{
Circle c = GetCircleFromPick(pk);
Point R = Cross(l.Direction(),Point(0,0,1)) * c.Radius();
Point p1 = c.Center() + R;
Point p2 = c.Center() - R;
//choose the solution closest to the selected point.
if(Distance( pk.GetPoint(), p1 ) < Distance( pk.GetPoint(), p2))
{
return Line(p1,l.Direction());
}
else
{
return Line(p2,l.Direction());
}
break;
}
}
throw GeomException("GL2D_LineParallel() : a Pick is not any expected type");
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
Line GL2D_LineByConstraints(const Pick& pk1, const Pick& pk2)
throw (GeomException)
{
// determine which case to solve (lets get this out of the way up front)
// POINT-POINT
if(pk1.Type() == POINT && pk2.Type() == POINT)
{
Point p1 = GetPointFromPick(pk1);
Point p2 = GetPointFromPick(pk2);
if (p1 == p2 )
throw GeomException("GeomLib2d::GL2D_LineByConstraints() : Points are equal");
return Line(p1, p2 - p1);
}
// POINT-LINE
else if(( pk1.Type() == POINT && (pk2.Type() == LINE || pk2.Type() == SEGMENT )) ||
((pk1.Type() == LINE || pk1.Type() == SEGMENT) && pk2.Type() == POINT ))
{
// local copies
Pick lpk1 = pk1;
Pick lpk2 = pk2;
// reorder picks - point first
if(lpk2.Type()==POINT)
{
Pick tmp = lpk1;
lpk1 = lpk2;
lpk2 = tmp;
}
// get point
Point p = GetPointFromPick(lpk1);
// get line
Line l = GetLineFromPick(lpk2);
// parallel to line, passing through point
return Line(p,l.Direction());
}
// POINT-CIRCLE
else if(( pk1.Type() == POINT && (pk2.Type() == CIRCLE || pk2.Type() == ARC )) ||
((pk1.Type() == CIRCLE || pk1.Type() == ARC) && pk2.Type() == POINT ))
{
// local copies
Pick lpk1 = pk1;
Pick lpk2 = pk2;
// reorder picks - point first
if(lpk2.Type()==POINT)
{
Pick tmp = lpk1;
lpk1 = lpk2;
lpk2 = tmp;
}
// get geometry
Point p = GetPointFromPick(lpk1);
Circle c = GetCircleFromPick(lpk2);
return GL2D_SolveLineTangentPointCircle(p,c,lpk2.GetPoint());
}
// LINE-LINE
else if((pk1.Type() == LINE || pk1.Type() == SEGMENT ) &&
(pk2.Type() == LINE || pk2.Type() == SEGMENT ) )
{
Line l1 = GetLineFromPick(pk1);
Line l2 = GetLineFromPick(pk2);
return GL2D_SolveLineConstrainedLineLine(l1,l2,pk1.GetPoint(), pk2.GetPoint());
}
// LINE-CIRCLE
else if(( ( pk1.Type() == LINE || pk1.Type() == SEGMENT) &&
( pk2.Type() == CIRCLE || pk2.Type() == ARC ) ) ||
( (pk1.Type() == CIRCLE || pk1.Type() == ARC ) &&
(pk2.Type() == LINE || pk2.Type() == SEGMENT ) ))
{
// local copies
Pick lpk1 = pk1;
Pick lpk2 = pk2;
// reorder picks - line /segment first
if(lpk2.Type()==LINE || lpk2.Type() == SEGMENT)
{
Pick tmp = lpk1;
lpk1 = lpk2;
lpk2 = tmp;
}
// get line
Line l = GetLineFromPick(lpk1);
Circle c = GetCircleFromPick(lpk2);
return GL2D_LineParallel(l,lpk2);
}
// CIRCLE-CIRCLE
else if((pk1.Type() == CIRCLE || pk1.Type() == ARC ) &&
(pk2.Type() == CIRCLE || pk2.Type() == ARC ) )
{
Circle c1 = GetCircleFromPick(pk1);
Circle c2 = GetCircleFromPick(pk2);
return GL2D_SolveLineTangentCircleCircle(c1,c2,pk1.GetPoint(),pk2.GetPoint());
}
throw GeomException("GeomLib2d::GL2D_LineByConstraints() : syntax error");
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Line Orthoginal(const Pick& pk1 , const Pick& pk2 ) // Constraint, Subject
// {
// // Line/Segment, Point - Ortho to Line/Segment, passing throgh point
// // Circle/arc, Point - Ortho to circle/arc, passing through point
// // Line/Segment, Circle/arc - Ortho to Line/Segment, tangent to Circle/arc
// // Circle/arc, Circle/arc - Ortho to circle/arc, tangent to circle/arc
// }
//
// Circles:
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Circle GL2D_CircleCenter(const Point& p, double r){ // solve }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
Circle GL2D_CircleCenter(const Pick& pk1, const Pick& pk2)
throw (GeomException)
{
// pk1 must be a Point
// pk2 can be Point,Line or Circle, segment or arc
if(pk1.Type() != POINT)
throw GeomException("GL2D_CircleCenter() : Pick 1 is not a point");
Point ctr = GetPointFromPick(pk1);
Point origin1,origin2;
switch(pk2.Type())
{
// Passing another point
case POINT:
{
origin1 = GetPointFromPick(pk2);
if(ctr == origin1)
throw GeomException("GL2D_CircleCenter() : center and origin points are equal");
return Circle(ctr,origin1);
}
// Tangent to a line
case LINE:
case SEGMENT:
{
Line l = GetLineFromPick(pk2);
origin1 = l.Project(ctr);
if(ctr == origin1)
throw GeomException("GL2D_CircleCenter() : Line passes through Circle center");
return Circle(ctr,origin1);
}
// Tangent to a Circle, arc
case CIRCLE:
case ARC:
{
Circle c = GetCircleFromPick(pk2);
// selected center is center of selected circle
if(c.Center() == ctr)
throw GeomException("GL2D_CircleCenter() : selected Point is center of selected Circle");
// point is tangent to circle
if(fabs(c.Radius() - Distance(ctr,c.Center())) < Point::NullDist)
throw GeomException("GL2D_CircleCenter() : selected Point is tangent to selected Circle");
// find two solutions
origin1 = c.Project(ctr); // projection
origin2 = ( 2 * c.Center()) - origin1 ;
if( origin1.Distance(pk2.GetPoint()) < origin2.Distance(pk2.GetPoint()) )
// return solution closest to pick point
return Circle(ctr,origin1);
else
return Circle(ctr,origin2);
}
default:
throw GeomException("GL2D_CircleCenter() : second Pick is not one of the expected types");
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Circle CircleParallel(const Pick& pk, double d)
// {
// //Circle/Arc
// }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Circle CircleParallel(const Pick& c, const Pick& t) // parallel to c, tangent to t
// {
// // t is Point
// // t is Line/Segment
// // t is Circle/Arc
// }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Circle CircleConstraint(const Pick&, const Pick&, const Pick&)
// {
// // Point-Point-Point
// // Point-Point-Line
// // Point-Point-Circle
//
// // Point-Line-Line PLL
// // Point-Line-Circle PLC
// // Point-Circle-Circle PCC
// // Line-Line-Line LLL
// // Line-Line-Circle LLC
// // Line-Circle-Circle LCC
// // Circle-Circle-Circle CCC
// }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// private "subroutines"
Point GL2D_SolveLineLineIntersection(const Line& l1, const Line& l2)
throw (GeomException)
{
double a1,b1,c1;
double a2,b2,c2;
//cerr << endl << " GL2D_SolveLineLineIntersection( " << endl;
//cerr << l1 << " , " << l2 << " ) " << endl;
// get directions
Vector v1 = l1.Direction();
Vector v2 = l2.Direction();
//cerr << "v1 = " << v1 << " v2 = " << v2 << endl;
//+ + + + + + + + + + + + + +
// check for degenerate cases
//+ + + + + + + + + + + + + +
// parallel lines
a1 = Angle(v1,v2);
//cerr << " a1 = " << a1 << endl;
a2 = Angle(v1,-v2);
//cerr << " a2 = " << a2 << endl;
if(a1 < Point::NullAngle || a2 < Point::NullAngle)
{
//cerr << "throwing exception" << endl;
throw GeomException("GL2D_SolveLineLineIntersection() : parallel lines");
}
//cerr << " lines are NOT parallel " << endl;
//+ + ++ + + + + + + + + + +
// Formulate explicit standard form ax + by + c = 0
// reference: faux and pratt, chapter 1
//+ + + + + + + + + + + + + +
// account for vertical lines as well
int lineflag1=0,lineflag2=0;
// line 1
lineflag1 = GL2D_NormalizedExplicitForm(a1,b1,c1,l1);
// line 2
lineflag2 = GL2D_NormalizedExplicitForm(a2,b2,c2,l2);
// vertical line 1
Point p;
// check the results of the last operations
if(lineflag1 == LINE_IS_VERTICAL)
// ax + by + c = 0: y = -(ax + c ) /b
// line 1 vertical: y value on line 2 => c = -x --> y = -(ax -x)/b = (-ax + x)/b
// = x(1-a)/b
{
// solve line 2 at x = -c1
// by = -a*c1 + c2
double x = -c1/a1;
p = Point( x , -(a2*x+c2)/b2 );
}
// vertical line 2
else if(lineflag2 == LINE_IS_VERTICAL)
{
// solve line 1 at Y = -c2;
double x = -c2/a2;
p = Point( x , -(a1*x+c1)/b1 );
}
// general case
else
{
//f & p eq. 1.8
p = Point((b1*c2 - b2*c1)/(a1*b2 - a2*b1),(c1*a2 - c2*a1)/(a1*b2 - a2*b1));
}
return Point(p);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
int GL2D_NormalizedExplicitForm(double& a, double& b, double& c,const Line& l)
throw (GeomException)
{
double x1,y1,x2,y2;
Point p1 = l.Origin();
Point p2 = p1 + l.Direction();
x1 = p1[0];
y1 = p1[1];
x2 = p2[0];
y2 = p2[1];
if ( fabs(x2 - x1) < Point::NullDist )
{
a=1;
b=0;
c=-x1;
if(c > 0){ a = -a; c = -c; }
return LINE_IS_VERTICAL;
}
a = (y2 - y1) / (x2 - x1 );
b = -1;
c = ( x2 * y1 - x1 * y2 ) / ( x2 - x1) ;
// normalize into standard form
if( fabs(a) < Point::NullDist&& fabs(b) < Point::NullDist)
throw GeomException("GL2D_NormalizedExplicitForm() : zero denomenator");
if(c > 0){ a = -a; b = -b; c = -c; } // 1st rule: c < 0, multiply by -1
// now scale so that a**2 + b**2 = 1
double s = sqrt(1/ (a * a + b * b));
a = a * s;
b = b * s;
c = c * s;
return 0;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// rotate a vector in the plane
Vector GL2D_Rotate(const Vector& v ,double theta) throw(GeomException)
{
// rotate v by angle theta
return Vector(v[0]*cos(theta)-v[1]*sin(theta), v[0]*sin(theta)+v[1]*cos(theta));
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
Line GL2D_SolveLineTangentPointCircle(const Point& p, const Circle& c, const Point& pkpt)
throw (GeomException)
{
// check for degenerate cases
Point ctr = c.Center();
double r = c.Radius();
double d = Distance(ctr, p);
// point inside circle
if(d < r)throw GeomException("GL2D_SolveLineTangentPointCircle : point is inside circle");
// general solution
double theta = acos( r / Distance(c.Center(),p) );
Vector v = p - c.Center();
v = v.Normal(); // should be safe from exception :)
v = v * r;
Point s1 = c.Center() + GL2D_Rotate(v,theta);
Point s2 = c.Center() + GL2D_Rotate(v,-theta);
// return solution closest to pick point.
if(Distance(s1,pkpt) < Distance(s2,pkpt) ) return Line(s1, p - s1);
return Line(s2, p - s2);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
Line GL2D_SolveLineConstrainedLineLine(const Line& l1,const Line& l2, const Point& p1, const Point& p2)
throw (GeomException)
{
//cerr << " GL2D_SolveLineConstrainedLineLine( " << endl;
//cerr << l1 << " , " << endl;
//cerr << l2 << " , " << endl;
//cerr << p1 << " , " << endl;
//cerr << p2 << " ) " << endl;
// general solution
// intersect the lines, get the point
Point p;
try
{
//cerr << "trying" << endl;
p = GL2D_SolveLineLineIntersection(l1,l2);
}
catch(GeomException & ge)
{
//cerr << "catching" << endl;
return Line(l1.Origin() + 0.5 * (l2.Origin() - l1.Origin()), l1.Direction());
}
// otherwise
// find both solutions
Line lx1(p,l1.Direction() + l2.Direction()); // remember, line directions are
Line lx2(p,l1.Direction() - l2.Direction()); // unit vectors so I can do this!
// solution is closest to midpoint between selection points
p = p1 + 0.5 * ( p2 - p1 );
if(Distance(p,lx1.Project(p)) < Distance(p,lx2.Project(p)))return lx1;
else return lx2;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
Line GL2D_SolveLineTangentCircleCircle(const Circle& c1, const Circle& c2, const Point& p1, const Point& p2)
throw (GeomException)
{
// Degenerate cases
// circles have same center - no solution
double dc = Distance(c1.Center(), c2.Center()); // do these only once cause they
double r1 = c1.Radius(); // use square roots
double r2 = c2.Radius();
// make local copies ( must make locals so I can swap them if necessary)
Circle cx1(c1);
Circle cx2(c2);
Point px1(p1);
Point px2(p2);
// assure larger circle is cx1
if(r1 < r2)
{
cx1 = c2;
cx2 = c1;
double tmp = r2;
r2 = r1;
r1 = tmp;
Point ptmp = px2;
px2 = px1;
px1 = ptmp;
}
if( dc < Point::NullDist )
throw GeomException("GL2D_SolveLineTangentCircleCircle() : coincident Circle centers");
// one circle completly inside the other - no solution
else if( (dc+r2-r1) < -Point::NullDist)
throw GeomException("GL2D_SolveLineTangentCircleCircle() : Circle inside Circle ");
// one circle inside another and tangent to it - one solution
else if( fabs(dc+r2-r1) < Point::NullDist)
{
Vector v = cx2.Center() - cx1.Center();
v = v.Normal() * cx2.Radius(); //should be exception safe
return Line( cx2.Center() + v, Cross(v,Point(0,0,1)));
}
// first find classical - outside solutions
Vector v = cx2.Center() - cx1.Center();
Vector v1= v.Normal() * cx1.Radius(); // v normalized to larger radius
Vector v2= v.Normal() * cx2.Radius(); // v normalized to smaller radius
double theta = acos((r1-r2)/dc); // calculate critical angle
Point p11 = cx1.Center() + GL2D_Rotate(v1,theta); // p11 - circle 1 solution 2
Point p12 = cx1.Center() + GL2D_Rotate(v1,-theta); // p12 - circle 1 solution 2
Point p21 = cx2.Center() + GL2D_Rotate(v2,theta); // p21 - circle 2 solution 1
Point p22 = cx2.Center() + GL2D_Rotate(v2,-theta); // p22 - circle 2 solution 2
// if only two solutions, solve now
if((dc - r2) < (r1 - Point::NullDist))
{
if(Distance(p1,p11) < Distance(p1,p12))
{
return Line(p11,p21-p11);
}
return Line(p12,p22-p12);
}
// now find possible third solution ---- circles are tangent ----
if( fabs(dc - r2 - r1) < Point::NullDist)
{
Point p13 = cx1.Center() + v1;
Point p23 = Cross(v1,Point(0,0,1));
// choose closest solution based on c1 picks
double d1 = Distance(p11,p1);
double d2 = Distance(p12,p1);
double d3 = Distance(p13,p1);
if(d1 < d2 && d1 < d3)return Line(p11,p21-p11);
else if(d2 < d3)return Line(p12,p22-p12);
return Line(p13,p23-p13);
}
// distinct non-intersecting, non-tangent, circles - four solutions
theta = acos((r1+r2)/dc); // calculate secondary critical angle
Point p13 = cx1.Center() + GL2D_Rotate(v1,theta); // circle 1 solution 3
Point p14 = cx1.Center() + GL2D_Rotate(v1,-theta); // circle 1 solution 4
Point p23 = cx2.Center() + GL2D_Rotate(v2,-(M_PI-theta)); // circle 2 solution 3
Point p24 = cx2.Center() + GL2D_Rotate(v2,M_PI-theta); // circle 2 solution 4
// now return correct solution ... hmmmm
double d[4];
d[0] = Distance(p11,px1) + Distance(p21,px2);
d[1] = Distance(p12,px1) + Distance(p22,px2);
d[2] = Distance(p13,px1) + Distance(p23,px2);
d[3] = Distance(p14,px1) + Distance(p24,px2);
double dmin = d[0];
int idx = 0;
for(int i=1;i<4;i++)
{
if(d[i] < dmin)
{
dmin = d[i];
idx = i;
}
}
switch (idx)
{
case 0:
// outside solution 1
{
return Line(p11,p21-p11);
}
case 1:
// outside solution 2
{
return Line(p12,p22-p12);
}
case 2:
// inside solution 3
{
return Line(p13,p23-p13);
}
}
// inside solution 4
return Line(p14,p24-p14);
}
//***********************************************************************************
Arc GL2D_SolveArc3Points(const Point& p1, const Point& p2, const Point& p3) throw (GeomException)
{
Circle c = GL2D_SolveCircle3Points(p1,p2,p3);
return Arc(c.Center(),p1,p3);
}
//***********************************************************************************
Circle GL2D_SolveCircle3Points(const Point& p1, const Point& p2, const Point& p3)
throw (GeomException)
{
Point px1(p1);
Point px2(p2);
Point px3(p3);
// error conditions
// points are equal
if(px1 == px2 || px1 == px3 || px2 == px3)
throw GeomException("GL2D_SolveArc3Points() : some Points are equal");
// points are co-linear
Vector v1 = px2 - px1;
Vector v2 = px3 - px1;
if(Angle(v1,v2) < Point::NullAngle)
throw GeomException("GL2D_SolveArc3Points() : Points are colinear");
// check for the sense of rotation - reverse the points if necessary
Vector n = Cross(v1,v2);
if(n[2] < 0)
{
Point tmpx = px3;
px3 = px1;
px1 = tmpx;
v1 = px2 - px1;
v2 = px3 - px1;
}
// find the cener from the 3 points
Point m1 = px1 + 0.5 * v1;
Point m2 = px1 + 0.5 * v2;
Vector d1 = Cross(v1,Vector(0,0,1));
Vector d2 = Cross(v2,Vector(0,0,1));
Line l1(m1,d1);
Line l2(m2,d2);
Point c = GL2D_SolveLineLineIntersection(l1,l2);
return Circle(c,px1);
}
//***********************************************************************************
Point GL2D_SolveLineCircleIntersection(const Line& l, const Circle& c, const Point& pickloc)
throw (GeomException)
{
// copy the line and circle
Line lx(l);
Circle cx(c);
// get distance from center to line
Point center = cx.Center();
double d = lx.Distance(cx.Center());
// line tangent to circle
if(fabs(d - cx.Radius()) < Point::NullDist)
{
return lx.Project(cx.Center());
}
// line is outside circle
if(d > cx.Radius())
throw GeomException("GL2D_SolveLineCircleIntersection() : no intersection between Line and Circle");
// two solution cases
Point p1,p2;
// passing through center
if(fabs(d) < Point::NullDist)
{
Vector vx = lx.Direction();
vx = vx.Normal();
p1 = cx.Center() + (cx.Radius() * vx);
p2 = cx.Center() - (cx.Radius() * vx);
}
else
{
Point px = lx.Project(cx.Center());
Vector v1 = px - cx.Center();
v1 = cx.Radius() * v1.Normal();
double theta = acos(d/cx.Radius());
p1 = cx.Center() + GL2D_Rotate(v1,theta);
p2 = cx.Center() + GL2D_Rotate(v1,-theta);
}
// take closet of 2 solutions to 2nd pick point
if (pickloc.Distance(p1) < pickloc.Distance(p2)) return p1;
return p2;
}
//***********************************************************************************
Point GL2D_SolveCircleCircleIntersection(const Circle& c1,const Circle& c2,const Point& pickloc)
throw (GeomException)
{
// make local copies ( must make locals so I can swap them if necessary)
Circle cx1(c1);
Circle cx2(c2);
// assure larger circle is cx1
if(cx1.Radius() < cx2.Radius())
{
Circle tmp = cx2;
cx2 = cx1;
cx1 = tmp;
}
// circle centers distance
double dc = Distance(cx1.Center(), cx2.Center());
// common centers or one circle completly inside the other - no solution
// dc > r1 + r2 --- no solution
if( dc < Point::NullDist ||
(dc+ cx2.Radius()-cx1.Radius()) < -Point::NullDist ||
(dc- cx1.Radius()-cx2.Radius()) > Point::NullDist )
throw GeomException("GL2D_SolveCircleCircleIntersection() : no intersections between Circles");
// one circle tangent to another - one solution
if( fabs( dc - ( cx1.Radius() + cx2.Radius() ) ) < Point::NullDist ||
fabs( dc - ( cx1.Radius() - cx2.Radius() ) ) < Point::NullDist )
{
Vector v = cx2.Center() - cx1.Center();
v = v.Normal();
return cx1.Center() + v * cx1.Radius();
}
// classical two solutions
// solve for trianlge height using Heron's formula
double s = 0.5 * ( dc + cx1.Radius() + cx2.Radius());
double h = 2 * sqrt( s * ( s - cx1.Radius() ) * ( s - cx2.Radius()) * ( s - dc ) ) /dc;
// now solve for solution points
Vector v = cx2.Center() - cx1.Center();
v = v.Normal();
double theta = asin( h / cx1.Radius());
Point p1 = cx1.Center() + cx1.Radius() * GL2D_Rotate(v,theta) ;
Point p2 = cx1.Center() + cx1.Radius() * GL2D_Rotate(v,-theta) ;
// take closet of 2 solutions to 2nd pick point
if (pickloc.Distance(p1) < pickloc.Distance(p2)) return p1;
return p2;
}
//***********************************************************************************
Line GL2D_SolveLineTangentCircleParallelLine(const Circle& c, const Line& l, const Point& p)
throw (GeomException)
{
// degenerate cases:
// selection point at circle center - no way to correctly interpret.
if(p.Distance(c.Center()) < Point::NullDist )
throw GeomException("GL2D_SolveLineTangentCircleParallelLine() : selection point is at Circle center");
// line passes through circle center
if(l.Distance(c.Center()) < Point::NullDist )
throw GeomException("GL2D_SolveLineTangentCircleParallelLine() : Line passes through Circle center");
Point px;
// unit vector from center parallel to line
Vector v = l.Project(c.Center()) - c.Center();
v = v.Normal();
// vector from center to selection point
Vector vx = p - c.Center();
if(( v * vx ) > 0 )
return Line(c.Center() + ( c.Radius() * v ), l.Direction());
else
return Line(c.Center() - ( c.Radius() * v ), l.Direction());
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Point GL2D_PointByModifier(const Pick& pk,int modifier) throw(GeomException)
{
int type = pk.Type();
switch(modifier)
{
case GL2D_MIDPOINT:
{
switch(type)
{
case POINT:
{
return GetPointFromPick(pk);
}
case SEGMENT:
{
Segment s = GetSegmentFromPick(pk);
return s.U(0.5);
}
case ARC:
{
Arc a = GetArcFromPick(pk);
return a.U(0.5);
}
}
throw GeomException("GL2D_PointByModifier() : midpoint is not a valid modifier for the selected type");
break;
}
case GL2D_ENDPOINT:
{
switch(type)
{
case POINT:
{
return GetPointFromPick(pk);
}
case SEGMENT:
{
Segment s = GetSegmentFromPick(pk);
Point p0 = s.U(0.0);
Point p1 = s.U(1.0);
if(Distance(p0,pk.GetPoint()) < Distance(p1,pk.GetPoint()) )
return p0;
return p1;
}
case ARC:
{
Arc a = GetArcFromPick(pk);
Point p0 = a.U(0.0);
Point p1 = a.U(1.0);
if(Distance(p0,pk.GetPoint()) < Distance(p1,pk.GetPoint()) )
return p0;
return p1;
}
}
throw GeomException("GL2D_PointByModifier() : endpoint is not a valid modifier for the selected type");
break;
}
case GL2D_CENTER:
{
switch(type)
{
case POINT:
{
return GetPointFromPick(pk);
}
case CIRCLE:
{
Circle c = GetCircleFromPick(pk);
return c.Center();
}
case ARC:
{
Arc a = GetArcFromPick(pk);
return a.Center();
}
}
throw GeomException("GL2D_PointByModifier() : center is not a valid modifier for the selected type");
break;
}
case GL2D_NEAREST:
{
switch(type)
{
case POINT:
{
return Point();
}
case LINE:
{
return Point();
}
case CIRCLE:
{
return Point();
}
case SEGMENT:
{
return Point();
}
case ARC:
{
return Point();
}
}
throw GeomException("GL2D_PointByModifier() : endpoint is not a valid modifier for the selected type");
break;
}
break;
}
throw GeomException("GL2D_PointByModifier() : invalid modifier value");
}
Circle GL2D_CircleRadiusAndConstraints(double r, const Pick& pk1, const Pick& pk2)
throw (GeomException)
{
// cases
// 1 line 1 circle - minimizing total distance...
// 2 circle minimize total distance
// 2 points - right handed circle from p1 to p2
if(pk1.Type() == POINT && pk2.Type() == POINT)
{
// failures : points are the same, distance between points is more than 2r
Point p1 = GetPointFromPick(pk1);
Point p2 = GetPointFromPick(pk2);
return GL2D_SolveCircleTanPointPointRadius(p1,p2,r);
}
// 1 point 1 line - return solution closest to where line was picked
// 1 point 1 circle - solution closest to where circle was picked
else if(pk1.Type()==POINT || pk2.Type() == POINT)
{
// point vs.line/segment
if(pk1.Type()==LINE || pk1.Type()==SEGMENT || pk2.Type()==LINE || pk2.Type()==SEGMENT)
{
// get the data
Point p;
Line l;
Point pkloc;
if(pk1.Type() == POINT)
{
p = GetPointFromPick(pk1);
l = GetLineFromPick(pk2);
pkloc = pk2.GetPoint();
}
else
{
p = GetPointFromPick(pk2);
l = GetLineFromPick(pk1);
pkloc = pk1.GetPoint();
}
return GL2D_SolveCircleTanPointLineRadius(p,l,r,pkloc);
}
else if(pk1.Type()==CIRCLE || pk1.Type()==ARC || pk2.Type()==CIRCLE || pk2.Type()==ARC)
{
// get circle and point
Point p,px;
Circle c,coffset,cx1,cx2;
Point pkloc;
if(pk1.Type() == POINT)
{
p = GetPointFromPick(pk1);
c = GetCircleFromPick(pk2);
pkloc = pk2.GetPoint();
}
else
{
p = GetPointFromPick(pk2);
c = GetCircleFromPick(pk1);
pkloc = pk1.GetPoint();
}
return GL2D_SolveCircleTanPointCircleRadius(p,c,r,pkloc);
}
}
// 2 lines - take midpoint of line selection location, offset lines on that side by r, find intersection
else if((pk1.Type() == LINE || pk1.Type() == SEGMENT) && ( pk2.Type() == LINE || pk2.Type() ==SEGMENT))
{
Line l1,l2;
Point mpt;
l1 = GetLineFromPick(pk1);
l2 = GetLineFromPick(pk2);
mpt = pk1.GetPoint() + 0.5 * (pk2.GetPoint() - pk1.GetPoint());
return GL2D_SolveCircleTanLineLineRadius(l1,l2,r,mpt);
}
// circle/circle
else if((pk1.Type() == CIRCLE || pk1.Type() == ARC ) && (pk2.Type() == CIRCLE || pk2.Type() == ARC))
{
Circle c1,c2;
Point pkloc1, pkloc2;
c1 = GetCircleFromPick(pk1);
c2 = GetCircleFromPick(pk2);
pkloc1 = pk1.GetPoint();
pkloc2 = pk2.GetPoint();
return GL2D_SolveCircleTanCircleCircleRadius(c1,c2,r,pkloc1,pkloc2);
}
else if(((pk1.Type() == CIRCLE || pk1.Type() == ARC ) && (pk2.Type() == LINE || pk2.Type() == SEGMENT)) ||
((pk2.Type() == CIRCLE || pk2.Type() == ARC ) && (pk1.Type() == LINE || pk1.Type() == SEGMENT)))
{
Line l;
Circle c;
Point pkloc_c, pkloc_l;
if(pk1.Type() == LINE || pk1.Type() == SEGMENT)
{
l = GetLineFromPick(pk1);
pkloc_l = pk1.GetPoint();
c = GetCircleFromPick(pk2);
pkloc_c = pk2.GetPoint();
}
else
{
l = GetLineFromPick(pk2);
pkloc_l = pk2.GetPoint();
c = GetCircleFromPick(pk1);
pkloc_c = pk1.GetPoint();
}
return GL2D_SolveCircleTanLineCircleRadius(l,c,r,pkloc_l,pkloc_c);
}
throw GeomException("GL2D_CircleByRadiusAndConstraints() : combination not implemented");
}
//=======================================================================================
Circle GL2D_SolveCircleTanPointCircleRadius(const Point& p, const Circle& c, const double& r, const Point& pkloc)
throw (GeomException)
{
Circle coffset;
Point px;
double d;
Circle cx1,cx2;
// get distance from point to circle
d = c.Distance(p);
// if point is on circle -> no solution (questionable...)
if(d < Point::NullDist)
throw GeomException("GL2D_CircleTanPointCircleRadius() : Point on circle -> no solution");
// if distance from circle to point is > 2r -> no solution
if(d > (2 * r + Point::NullDist))
throw GeomException("GL2D_CircleTanPointCircleRadius() :: Point too far from circle -> no solution");
// if point is at circle center, infinite solutions
if(p == c.Center())
throw GeomException("GL2D_CircleTanPointCircleRadius() : Point is at circle center, infinite solutions");
d = p.Distance(c.Center());
// is point inside circle?
if( d < c.Radius())
{
// if point is inside circle and r > circle radius -> no solution possible
if(r > c.Radius())
throw GeomException("GL2D_CircleTanPointCircleRadius() : Point inside circle and r > circle radius -> no solution");
// otherwise 1 of 2 solutions
coffset = Circle(c.Center(), c.Radius() - r);
px = GL2D_SolveCircleCircleIntersection(coffset,Circle(p,r),pkloc);
return Circle(px,r);
}
// point is outside circle - there are from 2 to 4 solutions remaining...
// is there a possible enclosing solution?
// if the diameter of the requested circle is less than or equal to the
// distance to the point + the radius of the existing circle...
bool multi = false;
if((2 * r - d - c.Radius() ) > -(Point::NullDist) )
{
// yup
coffset = Circle(c.Center(), fabs(c.Radius() - r)); // interior offset circle
// pkloc for solution selection should be opposite side of circle
Line lx(p,c.Center() - p );
Point pkmirror = 2 * lx.Project(pkloc) - pkloc;
px = GL2D_SolveCircleCircleIntersection(coffset,Circle(p,r),pkmirror);
cx1 = Circle(px,r);
multi = true;
}
// the 'near' solution
coffset = Circle(c.Center(),c.Radius() + r);
px = GL2D_SolveCircleCircleIntersection(coffset,Circle(p,r),pkloc);
// return solution nearest to circle pick point
cx2 = Circle(px,r);
if(multi && cx1.Distance(pkloc) < cx2.Distance(pkloc)) return cx1;
return cx2;
}
//=======================================================================================
Circle GL2D_SolveCircleTanPointLineRadius(const Point& p, const Line& l, const double& r, const Point& pkloc)
throw (GeomException)
{
// point on line - ambiguous - no solution ( could choose from side of line, ehhh...)
if(l.Distance(p) < Point::NullDist)
throw GeomException("GL2D_CircleByTanPointLineRadius() : Ambiguous solution > point is on line");
if(l.Distance(p) > 2 * r + Point::NullDist)
throw GeomException("GL2D_CircleByTanPointLineRadius() : no solution > point is more than 2r from line");
// solve the case
// offset the line
Vector v = p - l.Project(p);
v = v.Normal();
Line offset(l.Origin() + r * v,l.Direction());
// circle around r
Circle cx(p,r);
Point pc = GL2D_SolveLineCircleIntersection(offset,cx,pkloc);
return Circle(pc,r);
}
//============================================================================================
Circle GL2D_SolveCircleTanLineLineRadius(const Line& l1, const Line& l2, const double& r, const Point& pkloc)
throw (GeomException)
{
Line lx1,lx2;
Vector v;
Point px;
if(Angle(l1.Direction(), l2.Direction()) < Point::NullAngle)
throw GeomException("GL2D_CircleTanLineLineRadius() : lines are parallel -> no solution");
// midpoint between picks
// offset first line
v = pkloc - l1.Project(pkloc);
v = v.Normal();
lx1 = Line(l1.Origin() + (r * v), l1.Direction());
// offset second line
v = pkloc - l2.Project(pkloc);
v = v.Normal();
lx2 = Line(l2.Origin() + (r * v), l2.Direction());
// get intersection
px = GL2D_SolveLineLineIntersection(lx1,lx2);
return Circle(px,r);
}
//============================================================================================
Circle GL2D_SolveCircleTanPointPointRadius(const Point& p1, const Point& p2, const double& r)
throw (GeomException)
{
if(p1 == p2 || p1.Distance(p2) > (2 * r ))
throw GeomException("GL2D_CircleTanPointPointRadius() : No solution for the selected points");
Circle c1(p1,r);
Circle c2(p2,r);
Vector v = p2 - p1;
v = GL2D_Rotate(v,90);
Point px = GL2D_SolveCircleCircleIntersection(c1,c2,p2+v);
return Circle(px,r);
}
//============================================================================================
Circle GL2D_SolveCircleTanCircleCircleRadius(const Circle& c1, const Circle& c2, const double& r, const Point& pkloc1, const Point& pkloc2)
throw (GeomException)
{
// try this : consider the Rsmall, radius of the smaller of the two circles Csmall, and Psmall it's
// center point. find the centers of (up to) 4 circles :
// passing Psmall, tangent Cbig + Rsmall, radius r + Rsmall
// passing Psmall, tangent Cbig + Rsmall, radius r - Rsmall
// passing Psmall, tangent Cbig - Rsmall, radius r + Rsmall
// passing Psmall, tangent Cbig - Rsmall, radius r - Rsmall
// constitute 4 solution circles from these centers and choose the best to return
vector<Circle> sols;
Circle cx,czzz;
double Rsmall,Rzzz;
Point Psmall,pkbig,pkzzz;
Circle Csmall,Cbig;
Vector v;
if(c1.Radius() < c2.Radius())
{
Csmall = c1;
Cbig = c2;
pkbig = pkloc2;
}
else
{
Csmall = c2;
Cbig = c1;
pkbig = pkloc1;
}
Psmall = Csmall.Center();
Rsmall = Csmall.Radius();
v = pkbig - Cbig.Center(); // vector from center of Cbig to pkpoint on Cbig, normalized
v = v.Normal();
// passing Psmall, tangent Cbig + Rsmall, radius r + Rsmall
czzz = Circle(Cbig.Center(), Cbig.Radius() + Rsmall);
Rzzz = r + Rsmall;
pkzzz = pkbig + Rsmall * v;
try
{
cx = GL2D_SolveCircleTanPointCircleRadius(Psmall,czzz,Rzzz,pkzzz);
sols.push_back(Circle(cx.Center(),r));
}
catch (GeomException& ge)
{
}
// passing Psmall, tangent Cbig + Rsmall, radius r - Rsmall
czzz = Circle(Cbig.Center(), Cbig.Radius() + Rsmall);
Rzzz = r - Rsmall;
try
{
cx = GL2D_SolveCircleTanPointCircleRadius(Psmall,czzz,Rzzz,pkzzz);
sols.push_back(Circle(cx.Center(),r));
}
catch (GeomException& ge)
{
}
// passing Psmall, tangent Cbig - Rsmall, radius r + Rsmall
czzz = Circle(Cbig.Center(), Cbig.Radius() - Rsmall);
Rzzz = r + Rsmall;
pkzzz = pkbig - Rsmall * v;
try
{
cx = GL2D_SolveCircleTanPointCircleRadius(Psmall,czzz,Rzzz,pkzzz);
sols.push_back(Circle(cx.Center(),r));
}
catch (GeomException& ge)
{
}
// passing Psmall, tangent Cbig - Rsmall, radius r - Rsmall
czzz = Circle(Cbig.Center(), Cbig.Radius() - Rsmall);
Rzzz = r - Rsmall;
try
{
cx = GL2D_SolveCircleTanPointCircleRadius(Psmall,czzz,Rzzz,pkzzz);
sols.push_back(Circle(cx.Center(),r));
}
catch (GeomException& ge)
{
}
// figure out the best of the solution points
if(sols.size() == 0) throw GeomException("Gl2D_SolveCircleTanCircleCircleRadius() : no solutions found");
double minDist = 100000.;
vector<Circle>::iterator minIx;
for(vector<Circle>::iterator ix = sols.begin(); ix != sols.end() ; ix++)
{
double totDist = ix->Distance(pkloc1) + ix->Distance(pkloc2);
if(totDist < minDist)
{
minDist = totDist;
minIx = ix;
}
}
return Circle(*minIx);
// throw GeomException("GL2D_SolveCircleTanCircleCircleRadius() : function not implemented");
}
//============================================================================================
Circle GL2D_SolveCircleTanLineCircleRadius(const Line& l, const Circle& c,const double& r, const Point& pkloc_l, const Point& pkloc_c)
throw (GeomException)
{
Circle cx,cs1,cs2;
Point ps1,ps2;
bool multi = false;
double d = l.Distance(c.Center());
// no solution if... circle is more than r from line
if(( d - c.Radius() -r ) > Point::NullDist)
throw GeomException("GL2D_SolveCircleTanLineCircleRadius() : No solution -> circle too far from line");
// 2 solutions if line intersects circle, 4 otherwise
// both solutions require offsets of line and circle
// offset the line on the circle selection side
Vector v = pkloc_c - l.Project(pkloc_c);
v = v.Normal();
Line lx(l.Origin() + r * v , l.Direction());
// offset the circle on the outside
cx = Circle(c.Center(), c.Radius() + r);
// get the first solution ( this automatically resolves from 2 solutions based on pkloc_l )
ps1 = GL2D_SolveLineCircleIntersection(lx,cx,pkloc_l);
cs1 = Circle(ps1,r);
// check for 4 solutions - if the circle does not intersect the line AND the circle is not
// too far from the line, there are an extra 2 solutions
if(d > c.Radius() && (d + c.Radius()) < r )
{
cx = Circle(c.Center(), fabs(c.Radius() - r));
ps2 = GL2D_SolveLineCircleIntersection(lx,cx,pkloc_l);
cs2 = Circle(ps2,r);
multi = true;
}
// return the correct solution - closest to pkloc_c
if(multi && cs2.Distance(pkloc_c) < cs1.Distance(pkloc_c)) return cs2;
return cs1;
}