// // 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 . // //////////////////////////////////////////////////////////////// #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_Circle::ON_Circle( const ON_Plane& p, double r ) { Create( p, r ); } ON_Circle::ON_Circle( const ON_3dPoint& C, double r ) { Create( C, r ); } ON_Circle::ON_Circle( const ON_Plane& pln, const ON_3dPoint& C, double r ) { Create( pln, C, r ); } ON_Circle::ON_Circle( const ON_2dPoint& P, const ON_2dPoint& Q, const ON_2dPoint& R ) { Create(P,Q,R); } ON_Circle::ON_Circle( const ON_3dPoint& P, const ON_3dPoint& Q, const ON_3dPoint& R ) { Create(P,Q,R); } double ON_Circle::Radius() const { return radius; } double ON_Circle::Diameter() const { return 2.0*radius; } const ON_3dPoint& ON_Circle::Center() const { return plane.origin; } const ON_3dVector& ON_Circle::Normal() const { return plane.zaxis; } const ON_Plane& ON_Circle::Plane() const { return plane; } ON_BoundingBox ON_Circle::BoundingBox() const { ON_BoundingBox bbox; ON_3dPoint corners[4]; // = corners of square that contains circle corners[0] = plane.PointAt( radius, radius ); corners[1] = plane.PointAt( radius,-radius ); corners[2] = plane.PointAt(-radius, radius ); corners[3] = plane.PointAt(-radius,-radius ); bbox.Set(3,0,4,3,&corners[0].x,false); return bbox; } bool ON_Circle::Transform( const ON_Xform& xform ) { const ON_Plane plane0(plane); const bool rc = plane.Transform(xform); if (!rc) { // restore original plane = plane0; } else { const double ztol = 1.0e-12; double a,b,c,d,r1,r2,s; // determine scale factor in circle's plane // In practice, transformation are either rotations, // the scale factor is clearly distinct from 1, // or the transformation does not map a circle // to a circle. The code below has tolerance checks // so that anything that is close to a rotation gets // treated does not change the radius. If it is // clearly a uniform scale in the plane of the circle // the scale factor is calculated without using a // determinant. Sine "2d scales" are common, it doesn't // work well use the cubed root of the xform'd determinant. ON_3dVector V = xform*plane0.xaxis; a = V*plane.xaxis; b = V*plane.yaxis; if (fabs(a) >= fabs(b)) { r1 = fabs(a); if ( r1 > 0.0) { a = (a>0.0) ? 1.0 : -1.0; b /= r1; if ( fabs(b) <= ztol ) { b = 0.0; if ( fabs(1.0-r1) <= ztol ) r1 = 1.0; } } } else { r1 = fabs(b); b = (b>0.0) ? 1.0 : -1.0; a /= r1; if ( fabs(a) <= ztol ) { a = 0.0; if ( fabs(1.0-r1) <= ztol ) r1 = 1.0; } } V = xform*plane0.yaxis; c = V*plane.xaxis; d = V*plane.yaxis; if (fabs(d) >= fabs(c)) { r2 = fabs(d); if (r2 > 0.0) { d = (d>0.0) ? 1.0 : -1.0; c /= r2; if ( fabs(c) <= ztol ) { c = 0.0; if ( fabs(1.0-r2) <= ztol ) r2 = 1.0; } } } else { r2 = fabs(c); c = (c>0.0) ? 1.0 : -1.0; d /= r2; if ( fabs(d) <= ztol ) { d = 0.0; if ( fabs(1.0-r2) <= ztol ) r2 = 1.0; } } if ( 0.0 == b && 0.0 == c && fabs(r1-r2) <= ON_SQRT_EPSILON*(r1+r2) ) { // transform is a similarity s = (r1 == r2) ? r1 : (0.5*(r1+r2)); // = sqrt(r1*r2) but more accurate } else { // non-uniform scaling or skew in circle's plane // do something reasonable s = sqrt(fabs(r1*r2*(a*d-b*c))); } if ( s > 0.0 ) { //#if defined(ON_DEBUG) && !defined(ON_COMPILER_GNU) //double det = fabs(xform.Determinant()); //double s0 = pow(det,1.0/3.0); //if ( fabs(s-s0) > ON_SQRT_EPSILON*s0 ) //{ // // non-uniform scale or a bug // // In the non-uniform scal case, b and c should be // // "zero". // int breakpointhere = 0; // (generates gcc warning) //} //#endif if ( fabs(s-1.0) > ON_SQRT_EPSILON ) radius *= s; } } return rc; } double ON_Circle::Circumference() const { return fabs(2.0*ON_PI*radius); } bool ON_Circle::Create( const ON_Plane& p, double r ) { plane = p; if ( !plane.IsValid() ) plane.UpdateEquation(); // people often forget to set equation radius = r; //m_point[0] = plane.PointAt( radius, 0.0 ); //m_point[1] = plane.PointAt( 0.0, radius ); //m_point[2] = plane.PointAt( -radius, 0.0 ); return ( radius > 0.0 ); } bool ON_Circle::Create( const ON_3dPoint& C, double r ) { ON_Plane p = ON_xy_plane; p.origin = C; p.UpdateEquation(); return Create( p, r ); } bool ON_Circle::Create( const ON_Plane& pln, const ON_3dPoint& C, double r ) { ON_Plane p = pln; p.origin = C; p.UpdateEquation(); return Create( p, r ); } bool ON_Circle::Create( // circle through three 3d points const ON_2dPoint& P, const ON_2dPoint& Q, const ON_2dPoint& R ) { return Create(ON_3dPoint(P),ON_3dPoint(Q),ON_3dPoint(R)); } bool ON_Circle::Create( // circle through three 3d points const ON_3dPoint& P, const ON_3dPoint& Q, const ON_3dPoint& R ) { ON_3dPoint C; ON_3dVector X, Y, Z; // return ( radius > 0.0 && plane.IsValid() ); //m_point[0] = P; //m_point[1] = Q; //m_point[2] = R; // get normal for(;;) { if ( !Z.PerpendicularTo( P, Q, R ) ) break; // get center as the intersection of 3 planes ON_Plane plane0( P, Z ); ON_Plane plane1( 0.5*(P+Q), P-Q ); ON_Plane plane2( 0.5*(R+Q), R-Q ); if ( !ON_Intersect( plane0, plane1, plane2, C ) ) break; X = P - C; radius = X.Length(); if ( !(radius > 0.0) ) break; if ( !X.Unitize() ) break; Y = ON_CrossProduct( Z, X ); if ( !Y.Unitize() ) break; plane.origin = C; plane.xaxis = X; plane.yaxis = Y; plane.zaxis = Z; plane.UpdateEquation(); return true; } plane = ON_Plane::World_xy; radius = 0.0; return false; } ////////// // Create an circle from two 2d points and a tangent at the first point. bool ON_Circle::Create( const ON_2dPoint& P, // [IN] point P const ON_2dVector& Pdir, // [IN] tangent at P const ON_2dPoint& Q // [IN] point Q ) { return Create( ON_3dPoint(P), ON_3dVector(Pdir), ON_3dPoint(Q) ); } ////////// // Create an circle from two 3d points and a tangent at the first point. bool ON_Circle::Create( const ON_3dPoint& P, // [IN] point P const ON_3dVector& Pdir, // [IN] tangent at P const ON_3dPoint& Q // [IN] point Q ) { bool rc = false; double a, b; ON_3dVector QP, RM, RP, X, Y, Z; ON_3dPoint M, C; ON_Line A, B; // n = normal to circle QP = Q-P; Z = ON_CrossProduct( QP, Pdir ); if ( Z.Unitize() ) { M = 0.5*(P+Q); RM = ON_CrossProduct( QP, Z ); // vector parallel to center-M A.Create(M,M+RM); RP = ON_CrossProduct( Pdir, Z ); // vector parallel to center-P B.Create(P,P+RP); if ( ON_Intersect( A, B, &a, &b ) ) { C = A.PointAt( a ); // center = intersection of lines A and B X = P-C; radius = C.DistanceTo(P); if ( X.Unitize() ) { Y = ON_CrossProduct( Z, X ); if ( Y*Pdir < 0.0 ) { Z = -Z; Y = -Y; RM = -RM; } plane.origin = C; plane.xaxis = X; plane.yaxis = Y; plane.zaxis = Z; plane.UpdateEquation(); //m_point[0] = P; //m_point[1] = C + radius*RM/RM.Length(); //m_point[2] = Q; rc = IsValid(); } } } return rc; } bool ON_Circle::IsValid() const { bool rc = ( ON_IsValid(radius) && radius > 0.0 && plane.IsValid() ); return rc; } bool ON_Circle::IsInPlane( const ON_Plane& base_plane, double tolerance ) const { double d; int i; for ( i = 0; i < 8; i++ ) { d = base_plane.plane_equation.ValueAt( PointAt(0.25*i*ON_PI) ); if ( fabs(d) > tolerance ) return false; } return true; } ON_3dPoint ON_Circle::PointAt( double t ) const { return plane.PointAt( cos(t)*radius, sin(t)*radius ); } ON_3dVector ON_Circle::DerivativeAt( int d, // desired derivative ( >= 0 ) double t // parameter ) const { double r0 = radius; double r1 = radius; 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_Circle::TangentAt( double t ) const { ON_3dVector T = DerivativeAt(1,t); T.Unitize(); return T; } bool ON_Circle::ClosestPointTo( const ON_3dPoint& point, double* t ) const { bool rc = true; if ( t ) { double u, v; rc = plane.ClosestPointTo( point, &u, &v ); if ( u == 0.0 && v == 0.0 ) { *t = 0.0; } else { *t = atan2( v, u ); if ( *t < 0.0 ) *t += 2.0*ON_PI; } } return rc; } ON_3dPoint ON_Circle::ClosestPointTo( const ON_3dPoint& point ) const { ON_3dPoint P; ON_3dVector V = plane.ClosestPointTo( point ) - Center(); if ( V.Unitize() ) { V.Unitize(); P = Center() + Radius()*V; } else { P = PointAt(0.0); } return P; } double ON_Circle::EquationAt( const ON_2dPoint& p // coordinates in plane ) const { double e, x, y; if ( radius != 0.0 ) { x = p.x/radius; y = p.y/radius; e = x*x + y*y - 1.0; } else { e = 0.0; } return e; } ON_2dVector ON_Circle::GradientAt( const ON_2dPoint& p // coordinates in plane ) const { ON_2dVector g; if ( radius != 0.0 ) { const double rr = 2.0/(radius*radius); g.x = rr*p.x; g.y = rr*p.y; } else { g = ON_2dVector::ZeroVector; } return g; } bool ON_Circle::Rotate( double sin_angle, double cos_angle, const ON_3dVector& axis ) { return plane.Rotate( sin_angle, cos_angle, axis ); } bool ON_Circle::Rotate( double angle, const ON_3dVector& axis ) { return plane.Rotate( angle, axis ); } bool ON_Circle::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_Circle::Rotate( double angle, const ON_3dVector& axis, const ON_3dPoint& point ) { return plane.Rotate( angle, axis, point ); } bool ON_Circle::Translate( const ON_3dVector& delta ) { //m_point[0] += delta; //m_point[1] += delta; //m_point[2] += delta; return plane.Translate( delta ); } bool ON_Circle::Reverse() { //ON_3dPoint P = m_point[0]; //m_point[0] = m_point[2]; //m_point[2] = P; plane.yaxis = -plane.yaxis; plane.zaxis = -plane.zaxis; plane.UpdateEquation(); return true; } double ON_Circle::MaximumCoordinate() const { return plane.origin.MaximumCoordinate() + radius; } int ON_Circle::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); CV[1] = plane.PointAt( radius, radius); CV[2] = plane.PointAt( 0.0, radius); CV[3] = plane.PointAt(-radius, radius); CV[4] = plane.PointAt(-radius, 0.0); CV[5] = plane.PointAt(-radius, -radius); CV[6] = plane.PointAt( 0.0, -radius); CV[7] = plane.PointAt( radius, -radius); 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; } bool ON_Circle::GetRadianFromNurbFormParameter( double NurbParameter, double* RadianParameter ) const //returns false unless 0<= NurbParameter, <= 2*PI*Radius { if(!IsValid()) return false; ON_Arc arc(*this, 2*ON_PI); return arc.GetRadianFromNurbFormParameter( NurbParameter, RadianParameter); } bool ON_Circle::GetNurbFormParameterFromRadian( double RadianParameter, double* NurbParameter) const { if(!IsValid()) return false; ON_Arc arc(*this, 2*ON_PI); return arc.GetNurbFormParameterFromRadian( RadianParameter, NurbParameter); }