/* $NoKeywords: $ */ /* // // Copyright (c) 1993-2012 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 . // //////////////////////////////////////////////////////////////// */ #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_Ellipse::ON_Ellipse() { radius[0] = radius[1] = 0.0; } ON_Ellipse::ON_Ellipse( const ON_Plane& p, double rx, double ry ) { Create(p,rx,ry); } ON_Ellipse::ON_Ellipse( const ON_Circle& c ) { Create(c); } ON_Ellipse::~ON_Ellipse() {} ON_Ellipse& ON_Ellipse::operator=(const ON_Circle& c) { Create( c ); return *this; } bool ON_Ellipse::Create( const ON_Plane& p, double rx, double ry ) { plane = p; radius[0] = rx; radius[1] = ry; return IsValid(); } bool ON_Ellipse::Create( const ON_Circle& c ) { return Create( c.Plane(), c.Radius(), c.Radius() ); } bool ON_Ellipse::IsValid() const { return (plane.IsValid() && radius[0] > ON_ZERO_TOLERANCE && radius[1] > ON_ZERO_TOLERANCE) ? true : false; } bool ON_Ellipse::IsCircle() const { double r0 = radius[0]; return ( ON_IsValid(r0) && fabs(r0-radius[1]) <= fabs(r0)*ON_ZERO_TOLERANCE && IsValid() ) ? true : false; } double ON_Ellipse::Radius( int i ) const { return radius[(i)?1:0]; } const ON_3dPoint& ON_Ellipse::Center() const { return plane.origin; } double ON_Ellipse::FocalDistance() const { int i = (fabs(radius[0]) >= fabs(radius[1])) ? 0 : 1; const double a = fabs(radius[i]); const double b = a > 0.0 ? fabs(radius[1-i])/a : 0.0; return a*sqrt(1.0 - b*b); } bool ON_Ellipse::GetFoci( ON_3dPoint& F1, ON_3dPoint& F2 ) const { const double f = FocalDistance(); const ON_3dVector& majorAxis = (radius[0] >= radius[1]) ? plane.xaxis : plane.yaxis; F1 = plane.origin + f*majorAxis; F2 = plane.origin - f*majorAxis; return true; } const ON_3dVector& ON_Ellipse::Normal() const { return plane.zaxis; } const ON_Plane& ON_Ellipse::Plane() const { return plane; } ON_3dPoint ON_Ellipse::PointAt( double t ) const { return plane.PointAt( cos(t)*radius[0], sin(t)*radius[1] ); } ON_3dVector ON_Ellipse::DerivativeAt( int d, // desired derivative ( >= 0 ) double t // parameter ) const { double r0 = radius[0]; double r1 = radius[1]; switch ( std::abs(d) % 4 ) { case 0: r0 *= cos(t); r1 *= sin(t); break; case 1: r0 *= -sin(t); r1 *= cos(t); break; case 2: r0 *= -cos(t); r1 *= -sin(t); break; case 3: r0 *= sin(t); r1 *= -cos(t); break; } return ( r0*plane.xaxis + r1*plane.yaxis ); } ON_3dVector ON_Ellipse::TangentAt( double t // parameter ) const { ON_3dVector T = DerivativeAt( 1, t ); T.Unitize(); return T; } ON_3dVector ON_Ellipse::CurvatureAt( double t // parameter ) const { ON_3dVector T, K; ON_EvCurvature(DerivativeAt( 1, t ),DerivativeAt( 2, t ),T,K); return K; } static int distSqToEllipse(void* p, double t, double* f, double* df ) { // used in call to TL_NRdbrent(). double dx, dy, st, ct; const double* a = (const double*)p; // a[0], a[1] = x/y radii of 2d ellipse // (a[2],a[3]) = 2d point // f(t) = distance squared from ellipse(t) to 2d point ct = cos(t); st = sin(t); dx = ct*a[0] - a[2]; dy = st*a[1] - a[3]; if ( f ) { *f = dx*dx + dy*dy; } if ( df ) { *df = 2.0*(dy*a[1]*ct - dx*a[0]*st); } return 0; } #if defined(ON_COMPILER_MSC) // Disable the MSC /W4 warning // C4127: conditional expression is constant // on the line // for(...; true; ... ) // // This source code is used on many compilers and // I do not trust all of them to get for(..;;...) // right. #pragma ON_PRAGMA_WARNING_PUSH #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif bool ON_Ellipse::ClosestPointTo( const ON_3dPoint& point, double* t ) const { bool rc = true; if ( t ) { ON_2dPoint uv; rc = plane.ClosestPointTo( point, &uv.x, &uv.y ); if ( uv.x == 0.0 ) { if ( uv.y == 0.0 ) { *t = (radius[0] <= radius[1]) ? 0.0 : 0.5*ON_PI; return true; } if ( uv.y >= radius[1] ) { *t = 0.5*ON_PI; return true; } if ( uv.y <= -radius[1] ) { *t = 1.5*ON_PI; return true; } } else if ( uv.y == 0.0 ) { if ( uv.x >= radius[0] ) { *t = 0.0; return true; } if ( uv.x <= -radius[0] ) { *t = ON_PI; return true; } } { // use circluar approximation to get a seed value double t0, t1; *t = atan2( uv.y, uv.x ); if ( *t < 0.0 ) { *t += 2.0*ON_PI; if ( 2.0*ON_PI <= *t) { // == happens when atan2() <= 0.5*ON_EPSILON*2.0*PI *t = 0.0; } } if ( radius[0] != radius[1] ) { // set limits for search if ( uv.x >= 0.0 ) { if ( uv.y >= 0.0 ) { // search quadrant I t0 = 0.0; t1 = 0.5*ON_PI; } else { // search quadrant IV t0 = 1.5*ON_PI; t1 = 2.0*ON_PI; } } else { if ( uv.y >= 0.0 ) { // search quadrant II t0 = 0.5*ON_PI; t1 = ON_PI; } else { // search quadrant III t0 = ON_PI; t1 = 1.5*ON_PI; } } // solve for closest point using Brent's algorithm { // 6 October 2003 Dale Lear: // Fixed several serious bugs here. // get seed value appropriate for Brent double p[4], et, d0, d1, dt; int i; p[0] = radius[0]; p[1] = radius[1]; p[2] = uv.x; p[3] = uv.y; et = *t; if ( et <= t0 ) et = 0.9*t0 + 0.1*t1; else if ( et >= t1 ) et = 0.9*t1 + 0.1*t0; distSqToEllipse( p, t0, &d0, nullptr ); distSqToEllipse( p, t1, &d1, nullptr ); if ( d0 == 0.0 ) { *t = (t0 == 2.0*ON_PI) ? 0.0 : t0; return true; } if ( d1 == 0.0 ) { *t = (t1 == 2.0*ON_PI) ? 0.0 : t1; return true; } if ( d0 > d1 ) { dt = t0; t0 = t1; t1 = dt; dt = d0; d0 = d1; d1 = dt; } *t = (t0 == 2.0*ON_PI) ? 0.0 : t0; for ( i = 0; true; i++ ) { distSqToEllipse( p, et, &dt, nullptr ); if ( dt < d0 ) { *t = (et >= 2.0*ON_PI) ? 0.0 : et; break; } if ( i >= 100 ) { ON_3dPoint E0 = PointAt(t0); if ( sqrt(d0) <= ON_ZERO_TOLERANCE || sqrt(d0) <= ON_SQRT_EPSILON*E0.DistanceTo(Center()) ) { // Could not find a seed value for dbrent, // but t0 is pretty close. return true; } ON_3dVector T = TangentAt(t0); ON_3dVector V = E0 - point; if ( V.Unitize() ) { // Could not find a seed value for dbrent, // but V and T are orthoganal, so t0 is // pretty close. if ( fabs(V*T) <= 0.087155742747658173558064270837474 ) return true; } return false; // can't get valid seed - bail out } et = (i) ? (0.5*(t0+et)) : 0.5*(t0+t1); if ( et == t0 ) { return true; } } rc = ON_FindLocalMinimum( distSqToEllipse, p, t0, et, t1, ON_EPSILON, ON_SQRT_EPSILON, 100, &et ); if ( rc ) *t = (et >= 2.0*ON_PI) ? 0.0 : et; } } } } return rc; } #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif ON_3dPoint ON_Ellipse::ClosestPointTo( const ON_3dPoint& point ) const { double t; ClosestPointTo( point, &t ); return PointAt( t ); } double ON_Ellipse::EquationAt( const ON_2dPoint& p // coordinates in plane ) const { double e, x, y; if ( radius[0] != 0.0 && radius[1] != 0.0 ) { x = p.x/radius[0]; y = p.y/radius[1]; e = x*x + y*y - 1.0; } else { e = 0.0; } return e; } ON_2dVector ON_Ellipse::GradientAt( const ON_2dPoint& p // coordinates in plane ) const { ON_2dVector g; if ( radius[0] != 0.0 && radius[1] != 0.0 ) { g.x = 2.0*p.x/(radius[0]*radius[0]); g.y = 2.0*p.y/(radius[1]*radius[1]); } else { g = ON_2dVector::ZeroVector; } return g; } bool ON_Ellipse::Rotate( double sin_angle, double cos_angle, const ON_3dVector& axis ) { return plane.Rotate( sin_angle, cos_angle, axis ); } bool ON_Ellipse::Rotate( double angle, const ON_3dVector& axis ) { return plane.Rotate( angle, axis ); } bool ON_Ellipse::Rotate( double sin_angle, double cos_angle, const ON_3dVector& axis, const ON_3dPoint& point ) { return plane.Rotate( sin_angle, cos_angle, axis, point ); } bool ON_Ellipse::Rotate( double angle, const ON_3dVector& axis, const ON_3dPoint& point ) { return plane.Rotate( angle, axis, point ); } bool ON_Ellipse::Translate( const ON_3dVector& delta ) { return plane.Translate( delta ); } int ON_Ellipse::GetNurbForm( ON_NurbsCurve& nurbscurve ) const { int rc = 0; if ( IsValid() ) { nurbscurve.Create( 3, true, 3, 9 ); nurbscurve.m_knot[0] = nurbscurve.m_knot[1] = 0.0; nurbscurve.m_knot[2] = nurbscurve.m_knot[3] = 0.5*ON_PI; nurbscurve.m_knot[4] = nurbscurve.m_knot[5] = ON_PI; nurbscurve.m_knot[6] = nurbscurve.m_knot[7] = 1.5*ON_PI; nurbscurve.m_knot[8] = nurbscurve.m_knot[9] = 2.0*ON_PI; ON_4dPoint* CV = (ON_4dPoint*)nurbscurve.m_cv; CV[0] = plane.PointAt( radius[0], 0.0); CV[1] = plane.PointAt( radius[0], radius[1]); CV[2] = plane.PointAt( 0.0, radius[1]); CV[3] = plane.PointAt(-radius[0], radius[1]); CV[4] = plane.PointAt(-radius[0], 0.0); CV[5] = plane.PointAt(-radius[0], -radius[1]); CV[6] = plane.PointAt( 0.0, -radius[1]); CV[7] = plane.PointAt( radius[0], -radius[1]); CV[8] = CV[0]; const double w = 1.0/sqrt(2.0); int i; for ( i = 1; i < 8; i += 2 ) { CV[i].x *= w; CV[i].y *= w; CV[i].z *= w; CV[i].w = w; } rc = 2; } return rc; }