mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-02-28 19:16:09 +08:00
620 lines
14 KiB
C++
620 lines
14 KiB
C++
//
|
|
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
|
|
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
|
|
// McNeel & Associates.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
|
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
|
|
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
|
|
//
|
|
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#include "opennurbs.h"
|
|
|
|
#if !defined(ON_COMPILING_OPENNURBS)
|
|
// This check is included in all opennurbs source .c and .cpp files to insure
|
|
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
|
|
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
|
|
// and the opennurbs .h files alter what is declared and how it is declared.
|
|
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
|
|
#endif
|
|
|
|
ON_Arc::ON_Arc( const ON_Circle& c, double angle_in_radians )
|
|
{
|
|
Create( c, angle_in_radians );
|
|
}
|
|
|
|
ON_Arc::ON_Arc( const ON_Circle& c, ON_Interval angle_interval_in_radians )
|
|
{
|
|
Create( c, angle_interval_in_radians );
|
|
}
|
|
|
|
ON_Arc::ON_Arc( const ON_Plane& p, double r, double angle_in_radians )
|
|
{
|
|
Create( p, r, angle_in_radians );
|
|
}
|
|
|
|
ON_Arc::ON_Arc( const ON_3dPoint& C, double r, double angle_in_radians )
|
|
{
|
|
Create( C, r, angle_in_radians );
|
|
}
|
|
|
|
ON_Arc::ON_Arc( const ON_Plane& pln, const ON_3dPoint& C, double r, double angle_in_radians )
|
|
{
|
|
Create( pln, C, r, angle_in_radians );
|
|
}
|
|
|
|
ON_Arc::ON_Arc( const ON_2dPoint& P, const ON_2dPoint& Q, const ON_2dPoint& R )
|
|
{
|
|
Create( P, Q, R );
|
|
}
|
|
|
|
ON_Arc::ON_Arc( const ON_3dPoint& P, const ON_3dPoint& Q, const ON_3dPoint& R )
|
|
{
|
|
Create( P, Q, R );
|
|
}
|
|
|
|
ON_Arc& ON_Arc::operator=( const ON_Circle& src )
|
|
{
|
|
if (this != &src)
|
|
{
|
|
ON_Circle::operator=(src);
|
|
m_angle = ON_Interval::ZeroToTwoPi;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
bool ON_Arc::Create(
|
|
const ON_Circle& circle,
|
|
double angle_radians // angle in radians
|
|
)
|
|
{
|
|
return Create( circle, ON_Interval( 0.0, angle_radians ) );
|
|
}
|
|
|
|
bool ON_Arc::Create(
|
|
const ON_Circle& circle,
|
|
ON_Interval angle_interval_in_radians
|
|
)
|
|
{
|
|
bool rc = true;
|
|
plane = circle.plane;
|
|
plane.UpdateEquation();
|
|
radius = circle.radius;
|
|
m_angle = angle_interval_in_radians;
|
|
if ( m_angle.IsDecreasing() )
|
|
{
|
|
rc = false; // bogus input
|
|
// m_angle must never be decreasing
|
|
m_angle.Swap();
|
|
Reverse();
|
|
}
|
|
if ( m_angle.Length() > 2.0*ON_PI )
|
|
{
|
|
rc = false; // bogus input
|
|
m_angle.m_t[1] = m_angle.m_t[0] + 2.0*ON_PI;
|
|
}
|
|
if ( rc )
|
|
rc = IsValid();
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_Arc::Create(
|
|
const ON_Plane& pl, // circle is in this plane with center at m_origin
|
|
double r, // radius
|
|
double angle_radians // angle in radians
|
|
)
|
|
{
|
|
return Create( ON_Circle(pl,r), ON_Interval( 0.0, angle_radians ) );
|
|
}
|
|
|
|
bool ON_Arc::Create( // arc is parallel to XY plane
|
|
const ON_3dPoint& center, // center
|
|
double r, // radius
|
|
double angle_radians // angle in radians
|
|
)
|
|
{
|
|
ON_Plane p;
|
|
p.CreateFromNormal( center, ON_3dVector::ZAxis );
|
|
return Create( ON_Circle(p,r), ON_Interval( 0.0, angle_radians ) );
|
|
}
|
|
|
|
bool ON_Arc::Create( // arc parallel to a plane
|
|
const ON_Plane& pl, // circle will be parallel to this plane
|
|
const ON_3dPoint& center, // center
|
|
double r, // radius
|
|
double angle_radians // angle in radians
|
|
)
|
|
{
|
|
ON_Plane p = pl;
|
|
p.origin = center;
|
|
p.UpdateEquation();
|
|
return Create( ON_Circle( p, r), ON_Interval( 0.0, angle_radians ) );
|
|
}
|
|
|
|
bool ON_Arc::Create( // arc through 3 2d points
|
|
const ON_2dPoint& P, // point P
|
|
const ON_2dPoint& Q, // point Q
|
|
const ON_2dPoint& R // point R
|
|
)
|
|
{
|
|
ON_Circle c(P,Q,R);
|
|
double a = 0.0;
|
|
c.ClosestPointTo( ON_3dPoint(R), &a );
|
|
return Create( c, ON_Interval(0.0,a) );
|
|
}
|
|
|
|
bool ON_Arc::Create( // arc through 3 3d points
|
|
const ON_3dPoint& P, // point P
|
|
const ON_3dPoint& Q, // point Q
|
|
const ON_3dPoint& R // point R
|
|
)
|
|
{
|
|
ON_Circle c;
|
|
double a = 0.0;
|
|
|
|
for (;;)
|
|
{
|
|
|
|
if ( !c.Create(P,Q,R) )
|
|
break;
|
|
|
|
if ( !c.ClosestPointTo( R, &a ) )
|
|
break;
|
|
|
|
if ( !(a > 0.0) )
|
|
break;
|
|
|
|
if ( !Create( c, ON_Interval(0.0,a) ) )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
|
|
plane = ON_Plane::World_xy;
|
|
radius = 0.0;
|
|
m_angle.Set(0.0,0.0);
|
|
|
|
return false;
|
|
}
|
|
|
|
//////////
|
|
// Create an arc from a 2d start point, 2d start direction, and 2d end point.
|
|
bool ON_Arc::Create(
|
|
const ON_2dPoint& P, // [IN] start point
|
|
const ON_2dVector& Pdir, // [IN] arc direction at start
|
|
const ON_2dPoint& Q // [IN] end point
|
|
)
|
|
{
|
|
return Create( ON_3dPoint(P), ON_3dVector(Pdir), ON_3dPoint(Q) );
|
|
}
|
|
|
|
//////////
|
|
// Create an arc from a 3d start point, 3d start direction, and 3d end point.
|
|
bool ON_Arc::Create(
|
|
const ON_3dPoint& P, // [IN] start point
|
|
const ON_3dVector& Pdir, // [IN] arc direction at start
|
|
const ON_3dPoint& Q // [IN] end point
|
|
)
|
|
{
|
|
double a=0.0;
|
|
bool rc = ON_Circle::Create(P,Pdir,Q);
|
|
if ( rc ) {
|
|
m_angle.m_t[0] = 0.0;
|
|
rc = ON_Circle::ClosestPointTo(Q,&a);
|
|
m_angle.m_t[1] = a;
|
|
if (a <= ON_ZERO_TOLERANCE || a >= 2.0*ON_PI-ON_ZERO_TOLERANCE )
|
|
rc = false;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
void ON_Arc::Dump( ON_TextLog& dump ) const
|
|
{
|
|
dump.Print("Arc: normal = ");
|
|
dump.Print(plane.zaxis);
|
|
dump.Print(" center = ");
|
|
dump.Print(plane.origin);
|
|
dump.Print(" start = ");
|
|
dump.Print( StartPoint() );
|
|
dump.Print(" end = ");
|
|
dump.Print( EndPoint() );
|
|
dump.Print(" radius = ");
|
|
dump.Print(Radius());
|
|
dump.Print(" angle = [");
|
|
dump.Print(m_angle[0]);
|
|
dump.Print(",");
|
|
dump.Print(m_angle[1]);
|
|
dump.Print("]\n");
|
|
}
|
|
|
|
ON_3dPoint ON_Arc::StartPoint() const
|
|
{
|
|
return PointAt(m_angle[0]);
|
|
}
|
|
|
|
ON_3dPoint ON_Arc::MidPoint() const
|
|
{
|
|
return PointAt(m_angle.Mid());
|
|
}
|
|
|
|
ON_3dPoint ON_Arc::EndPoint() const
|
|
{
|
|
return PointAt(m_angle[1]);
|
|
}
|
|
|
|
bool ON_Arc::IsValid() const
|
|
{
|
|
return ( ON_Circle::IsValid()
|
|
&& m_angle.IsValid()
|
|
&& AngleRadians() > ON_ZERO_TOLERANCE
|
|
&& AngleRadians() <= 2.0*ON_PI+ON_ZERO_TOLERANCE)
|
|
? true : false;
|
|
}
|
|
|
|
ON_BoundingBox ON_Arc::BoundingBox() const
|
|
{
|
|
// TODO - compute tight arc bounding box
|
|
|
|
// Using these knot[] and cv[] arrays makes this function
|
|
// not use any heap memory.
|
|
double knot[10];
|
|
ON_4dPoint cv[9];
|
|
ON_NurbsCurve c;
|
|
c.m_knot = knot;
|
|
c.m_cv = &cv[0].x;
|
|
if ( GetNurbForm(c) )
|
|
return c.BoundingBox();
|
|
return ON_Circle::BoundingBox();
|
|
}
|
|
|
|
bool ON_Arc::GetBoundingBox(
|
|
ON_BoundingBox& bbox,
|
|
int bGrowBox
|
|
) const
|
|
{
|
|
if (bGrowBox)
|
|
{
|
|
ON_BoundingBox arc_bbox = BoundingBox();
|
|
bbox.Union(arc_bbox);
|
|
}
|
|
else
|
|
bbox = BoundingBox();
|
|
return bbox.IsValid();
|
|
}
|
|
|
|
bool ON_Arc::IsCircle() const
|
|
{
|
|
return (fabs(fabs(AngleRadians()) - 2.0*ON_PI) <= ON_ZERO_TOLERANCE)
|
|
? true : false;
|
|
}
|
|
|
|
bool ON_Arc::IsLinear(double tol) const
|
|
{
|
|
static const double Theta_max = 2*ON_PI - 4.0 * atan(.5); // about 250 degrees
|
|
return AngleRadians() < Theta_max && // Angle must be less than max
|
|
radius * (1.0 - cos(AngleRadians() / 2.0)) < tol && // Segment height less than tol
|
|
radius * 2.0 * sin(AngleRadians() / 2.0) > tol; // Chord length greater than tol
|
|
}
|
|
|
|
double ON_Arc::AngleRadians() const
|
|
{
|
|
return m_angle[1]-m_angle[0];
|
|
}
|
|
|
|
double ON_Arc::AngleDegrees() const
|
|
{
|
|
return (AngleRadians()/ON_PI)*180.0;
|
|
}
|
|
|
|
ON_Interval ON_Arc::Domain() const
|
|
{
|
|
return m_angle;
|
|
}
|
|
|
|
ON_Interval ON_Arc::DomainRadians() const
|
|
{
|
|
return m_angle;
|
|
}
|
|
|
|
ON_Interval ON_Arc::DomainDegrees() const
|
|
{
|
|
const double rtd = 180.0/ON_PI;
|
|
ON_Interval ad = m_angle;
|
|
ad.m_t[0] *= rtd;
|
|
ad.m_t[1] *= rtd;
|
|
return ad;
|
|
}
|
|
|
|
bool ON_Arc::SetAngleRadians( double a )
|
|
{
|
|
if ( a < 0.0 )
|
|
{
|
|
double a0 = m_angle.m_t[0];
|
|
m_angle.Set(a0+a,a0);
|
|
Reverse();
|
|
}
|
|
else
|
|
{
|
|
m_angle.m_t[1] = m_angle.m_t[0] + a;
|
|
}
|
|
return ( fabs(m_angle.Length()) <= 2.0*ON_PI ) ? true : false;
|
|
}
|
|
|
|
bool ON_Arc::SetAngleIntervalRadians( ON_Interval angle_in_radians )
|
|
{
|
|
bool rc = angle_in_radians.IsIncreasing()
|
|
&& angle_in_radians.Length() < (1.0+ON_SQRT_EPSILON)*2.0*ON_PI;
|
|
if (rc)
|
|
{
|
|
m_angle = angle_in_radians;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Arc::SetAngleDegrees( double a )
|
|
{
|
|
return SetAngleRadians( (a/180.0)*ON_PI );
|
|
}
|
|
|
|
bool ON_Arc::Trim( ON_Interval domain)
|
|
{
|
|
bool ok = false;
|
|
|
|
if(domain[0]<domain[1] && domain[1]-domain[0]<=2.0 * ON_PI+ON_ZERO_TOLERANCE){
|
|
m_angle = domain;
|
|
if (m_angle.Length() > 2.0*ON_PI) m_angle[1] = m_angle[0] + 2.0*ON_PI;
|
|
ok = true;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool ON_ArcCurve::IsContinuous(
|
|
ON::continuity c,
|
|
double t,
|
|
int*, // hint - formal parameter intentionally ignored in this virtual function
|
|
double, // point_tolerance - formal parameter intentionally ignored in this virtual function
|
|
double, // d1_tolerance - formal parameter intentionally ignored in this virtual function
|
|
double, // d2_tolerance - formal parameter intentionally ignored in this virtual function
|
|
double, // cos_angle_tolerance - formal parameter intentionally ignored in this virtual function
|
|
double // curvature_tolerance - formal parameter intentionally ignored in this virtual function
|
|
) const
|
|
{
|
|
// 20 March 2003 Dale Lear
|
|
// Added this override of IsContinuous() to
|
|
// speed queries and support the
|
|
// locus favors of ON::continuity.
|
|
|
|
bool rc = true;
|
|
|
|
if ( !IsClosed() )
|
|
{
|
|
switch(c)
|
|
{
|
|
case ON::continuity::unknown_continuity:
|
|
case ON::continuity::C0_continuous:
|
|
case ON::continuity::C1_continuous:
|
|
case ON::continuity::C2_continuous:
|
|
case ON::continuity::G1_continuous:
|
|
case ON::continuity::G2_continuous:
|
|
case ON::continuity::Cinfinity_continuous:
|
|
case ON::continuity::Gsmooth_continuous:
|
|
// rc = true;
|
|
break;
|
|
|
|
case ON::continuity::C0_locus_continuous:
|
|
case ON::continuity::C1_locus_continuous:
|
|
case ON::continuity::C2_locus_continuous:
|
|
case ON::continuity::G1_locus_continuous:
|
|
case ON::continuity::G2_locus_continuous:
|
|
// open arc is locus discontinuous at end parameter.
|
|
// By convention (see ON::continuity comments) it
|
|
// is locus continuous at start parameter.
|
|
if ( t >= Domain()[1] )
|
|
rc = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
bool ON_Arc::Reverse()
|
|
{
|
|
m_angle.Reverse();
|
|
plane.yaxis = -plane.yaxis;
|
|
plane.zaxis = -plane.zaxis;
|
|
plane.UpdateEquation();
|
|
return true;
|
|
}
|
|
|
|
double ON_Arc::Length() const
|
|
{
|
|
return fabs(AngleRadians()*radius);
|
|
}
|
|
|
|
double ON_Arc::SectorArea() const
|
|
{
|
|
return fabs(0.5*AngleRadians()*radius*radius);
|
|
}
|
|
|
|
ON_3dPoint ON_Arc::SectorAreaCentroid() const
|
|
{
|
|
double a = 0.5*fabs(AngleRadians());
|
|
double d = (a > 0.0) ? sin(a)/a : 0.0;
|
|
d *= 2.0*radius/3.0;
|
|
a = 0.5*(m_angle[1]+m_angle[0]);
|
|
return plane.PointAt(d*cos(a),d*sin(a));
|
|
}
|
|
|
|
double ON_Arc::SegmentArea() const
|
|
{
|
|
double a = fabs(AngleRadians());
|
|
return (0.5*(a - sin(a))*radius*radius);
|
|
}
|
|
|
|
ON_3dPoint ON_Arc::SegmentAreaCentroid() const
|
|
{
|
|
double a = fabs(AngleRadians());
|
|
double sin_halfa = sin(0.5*a);
|
|
double d = 3.0*(a - sin(a));
|
|
if ( d > 0.0 )
|
|
d = (sin_halfa*sin_halfa*sin_halfa)/d;
|
|
d *= 4.0*radius;
|
|
a = 0.5*(m_angle[1]+m_angle[0]);
|
|
return plane.PointAt(d*cos(a),d*sin(a));
|
|
}
|
|
|
|
|
|
/* moved to opennurbs_arccurve.cpp
|
|
|
|
|
|
int ON_Arc::GetNurbForm( ON_NurbsCurve& nurbscurve ) const
|
|
{
|
|
int rc = 0;
|
|
if ( IsValid() ) {
|
|
if ( IsCircle() )
|
|
rc = ON_Circle::GetNurbForm( nurbscurve );
|
|
else {
|
|
double a, b, c, t, dt, angle;
|
|
int span_count, i;
|
|
angle = m_angle.Length();
|
|
if (angle <= 0.5*ON_PI + ON_ZERO_TOLERANCE) {
|
|
span_count = 1;
|
|
dt = 0.5;
|
|
}
|
|
else if (angle <= ON_PI + ON_ZERO_TOLERANCE) {
|
|
span_count = 2;
|
|
angle *= 0.5;
|
|
dt = 0.25;
|
|
}
|
|
else if (angle <= 1.5*ON_PI + ON_ZERO_TOLERANCE) {
|
|
span_count = 3;
|
|
angle /= 3.0;
|
|
dt = 1.0/6.0;
|
|
}
|
|
else {
|
|
span_count = 4;
|
|
angle *= 0.25;
|
|
dt = 0.125;
|
|
}
|
|
nurbscurve.Create( 3, true, 3, 2*span_count+1 );
|
|
ON_4dPoint* CV = (ON_4dPoint*)nurbscurve.m_cv;
|
|
t = m_angle[0];
|
|
for ( i = 0; i < span_count; i++ ) {
|
|
nurbscurve.m_knot[2*i] = t;
|
|
nurbscurve.m_knot[2*i+1] = t;
|
|
CV[2*i] = PointAt(m_angle.ParameterAt(t));
|
|
t += dt;
|
|
CV[2*i+1] = PointAt(m_angle.ParameterAt(t));
|
|
t += dt;
|
|
}
|
|
|
|
span_count *= 2;
|
|
t = m_angle[1];
|
|
CV[span_count] = PointAt(t);
|
|
nurbscurve.m_knot[span_count] = t;
|
|
nurbscurve.m_knot[span_count+1] = t;
|
|
|
|
a = cos(0.5*angle);
|
|
b = a - 1.0;
|
|
c = radius*angle;
|
|
|
|
for (i = 1; i < span_count; i += 2) {
|
|
CV[i].x += b * plane.origin.x;
|
|
CV[i].y += b * plane.origin.y;
|
|
CV[i].z += b * plane.origin.z;
|
|
CV[i].w = a;
|
|
}
|
|
|
|
//for ( i = 1; i < span_count; i += 2 ) {
|
|
// t = CV[i].w;
|
|
// c = 1.0/t;
|
|
// a = CV[i].x*c; b = ArcDeFuzz(a); if ( a != b ) CV[i].x = b*t;
|
|
// a = CV[i].y*c; b = ArcDeFuzz(a); if ( a != b ) CV[i].y = b*t;
|
|
// a = CV[i].z*c; b = ArcDeFuzz(a); if ( a != b ) CV[i].z = b*t;
|
|
//}
|
|
}
|
|
rc = 2;
|
|
}
|
|
return rc;
|
|
}
|
|
*/
|
|
|
|
// returns parameters of point on arc that is closest to given point
|
|
bool ON_Arc::ClosestPointTo(
|
|
const ON_3dPoint& pt,
|
|
double* t
|
|
) const
|
|
{
|
|
/*
|
|
double tt, a;
|
|
if ( !t )
|
|
t =&tt;
|
|
bool rc = ON_Circle::ClosestPointTo(pt,t);
|
|
if (rc) {
|
|
if ( *t < m_angle[0] ) {
|
|
a = 0.5*(m_angle[0] + m_angle[1] - 2.0*ON_PI);
|
|
if ( *t < a )
|
|
*t = m_angle[1];
|
|
else
|
|
*t = m_angle[0];
|
|
}
|
|
else if ( *t > m_angle[1] ) {
|
|
a = 0.5*(m_angle[0] + m_angle[1] + 2.0*ON_PI);
|
|
if ( *t > a )
|
|
*t = m_angle[0];
|
|
else
|
|
*t = m_angle[1];
|
|
}
|
|
}
|
|
*/
|
|
double s;
|
|
double twopi = 2.0*ON_PI;
|
|
bool rc = ON_Circle::ClosestPointTo(pt,&s);
|
|
if (rc){
|
|
s -= m_angle[0];
|
|
while (s < 0.0) s += twopi;
|
|
|
|
// Greg Arden April 14 2003. Changed test from ">" to ">=" this ensures that
|
|
// closest point to a circle at the seam will return the least parameter value.
|
|
while (s >= twopi) s -= twopi;
|
|
|
|
double s1 = m_angle.Length();
|
|
|
|
if (s < 0.0) s = 0.0;//shouldn't happen
|
|
if (s > s1){
|
|
if (s > 0.5*s1 + ON_PI)
|
|
s = 0.0;
|
|
else
|
|
s = s1;
|
|
}
|
|
|
|
if (t)
|
|
*t = m_angle[0] + s;
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
// returns point on circle that is arc to given point
|
|
ON_3dPoint ON_Arc::ClosestPointTo(
|
|
const ON_3dPoint& pt
|
|
) const
|
|
{
|
|
double t = m_angle[0];
|
|
ClosestPointTo( pt, &t );
|
|
return PointAt(t);
|
|
}
|
|
|