mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 03:26:09 +08:00
2416 lines
58 KiB
C++
2416 lines
58 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_OBJECT_IMPLEMENT( ON_RevSurface, ON_Surface, "A16220D3-163B-11d4-8000-0010830122F0");
|
|
|
|
|
|
void ON_RevSurface::DestroyRuntimeCache( bool bDelete )
|
|
{
|
|
ON_Surface::DestroyRuntimeCache(bDelete);
|
|
if ( 0 != m_curve )
|
|
m_curve->DestroyRuntimeCache(bDelete);
|
|
// 15 August 2003 Dale Lear
|
|
// Added the call to destroy m_bbox.
|
|
m_bbox.Destroy();
|
|
}
|
|
|
|
|
|
ON_RevSurface* ON_RevSurface::New()
|
|
{
|
|
return new ON_RevSurface();
|
|
}
|
|
|
|
ON_RevSurface* ON_RevSurface::New( const ON_RevSurface& rev_surface )
|
|
{
|
|
return new ON_RevSurface(rev_surface);
|
|
}
|
|
|
|
ON_RevSurface::ON_RevSurface() : m_curve(0),
|
|
m_axis( ON_3dPoint::Origin, ON_3dVector::ZAxis ),
|
|
m_angle( 0.0, 2.0*ON_PI ),
|
|
m_t( 0.0, 2.0*ON_PI ),
|
|
m_bTransposed(0)
|
|
{
|
|
ON__SET__THIS__PTR(m_s_ON_RevSurface_ptr);
|
|
}
|
|
|
|
ON_RevSurface::~ON_RevSurface()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
void ON_RevSurface::Destroy()
|
|
{
|
|
DestroySurfaceTree();
|
|
if ( m_curve)
|
|
{
|
|
delete m_curve;
|
|
m_curve = 0;
|
|
}
|
|
m_axis.Create( ON_3dPoint::Origin, ON_3dVector::ZAxis );
|
|
m_angle.Set(0.0,2.0*ON_PI);
|
|
m_t = m_angle;
|
|
m_bTransposed = false;
|
|
m_bbox.Destroy();
|
|
}
|
|
|
|
ON_RevSurface::ON_RevSurface( const ON_RevSurface& src ) : ON_Surface(src)
|
|
{
|
|
ON__SET__THIS__PTR(m_s_ON_RevSurface_ptr);
|
|
m_curve = src.m_curve ? src.m_curve->Duplicate() : nullptr;
|
|
m_axis = src.m_axis;
|
|
m_angle = src.m_angle;
|
|
m_t = src.m_t;
|
|
m_bTransposed = src.m_bTransposed;
|
|
m_bbox = src.m_bbox;
|
|
}
|
|
|
|
|
|
unsigned int ON_RevSurface::SizeOf() const
|
|
{
|
|
unsigned int sz = ON_Surface::SizeOf();
|
|
if ( m_curve )
|
|
sz += m_curve->SizeOf();
|
|
return sz;
|
|
}
|
|
|
|
ON__UINT32 ON_RevSurface::DataCRC(ON__UINT32 current_remainder) const
|
|
{
|
|
if ( m_curve )
|
|
current_remainder = m_curve->DataCRC(current_remainder);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_axis),&m_axis);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_angle),&m_angle);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_t),&m_t);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_bTransposed),&m_bTransposed);
|
|
return current_remainder;
|
|
}
|
|
|
|
|
|
ON_RevSurface& ON_RevSurface::operator=( const ON_RevSurface& src )
|
|
{
|
|
if ( this != &src )
|
|
{
|
|
Destroy();
|
|
ON_Surface::operator=(src);
|
|
if ( src.m_curve )
|
|
m_curve = src.m_curve->Duplicate();
|
|
m_axis = src.m_axis;
|
|
m_angle = src.m_angle;
|
|
m_t = src.m_t;
|
|
m_bTransposed = src.m_bTransposed;
|
|
m_bbox = src.m_bbox;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
bool ON_RevSurface::SetAngleRadians(
|
|
double start_angle_radians,
|
|
double end_angle_radians
|
|
)
|
|
{
|
|
bool rc = false;
|
|
double d = end_angle_radians-start_angle_radians;
|
|
if ( d >= 0.0 )
|
|
{
|
|
if ( d <= ON_ZERO_TOLERANCE || d > 2.0*ON_PI )
|
|
{
|
|
end_angle_radians = start_angle_radians + 2.0*ON_PI;
|
|
}
|
|
m_angle.Set( start_angle_radians, end_angle_radians );
|
|
rc = true;
|
|
DestroySurfaceTree();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_RevSurface::SetAngleDegrees (
|
|
double start_angle_degrees,
|
|
double end_angle_degrees
|
|
)
|
|
{
|
|
return SetAngleRadians(
|
|
start_angle_degrees*ON_PI/180.0,
|
|
end_angle_degrees*ON_PI/180.0
|
|
);
|
|
}
|
|
|
|
|
|
bool ON_RevSurface::IsValid( ON_TextLog* text_log ) const
|
|
{
|
|
if ( !m_curve )
|
|
{
|
|
if ( text_log )
|
|
text_log->Print( "ON_RevSurface.m_curve is nullptr.\n");
|
|
return false;
|
|
}
|
|
if ( !m_curve->IsValid(text_log) )
|
|
{
|
|
if ( text_log )
|
|
text_log->Print( "ON_RevSurface.m_curve is not valid.\n");
|
|
return false;
|
|
}
|
|
int dim = m_curve->Dimension();
|
|
if ( dim != 3 )
|
|
{
|
|
if ( text_log )
|
|
text_log->Print( "ON_RevSurface.m_curve->Dimension()=%d (should be 3).\n",dim);
|
|
return false;
|
|
}
|
|
if ( !m_axis.IsValid() )
|
|
{
|
|
if ( text_log )
|
|
text_log->Print( "ON_RevSurface.m_axis is not valid.\n");
|
|
return false;
|
|
}
|
|
if ( !m_angle.IsIncreasing() )
|
|
{
|
|
if ( text_log )
|
|
text_log->Print( "ON_RevSurface.m_angle = (%g,%g) (should be an increasing interval)\n",
|
|
m_angle[0],m_angle[1]);
|
|
return false;
|
|
}
|
|
double length = m_angle.Length();
|
|
if ( length > 2.0*ON_PI + ON_ZERO_TOLERANCE )
|
|
{
|
|
if ( text_log )
|
|
text_log->Print( "ON_RevSurface.m_angle.Length() = %g (should be <= 2*pi radians).\n",length);
|
|
return false;
|
|
}
|
|
if ( m_angle.Length() <= ON_ZERO_TOLERANCE )
|
|
{
|
|
if ( text_log )
|
|
text_log->Print( "ON_RevSurface.m_angle.Length() = %g (should be > ON_ZERO_TOLERANCE).\n",length);
|
|
return false;
|
|
}
|
|
if ( !m_t.IsIncreasing() )
|
|
{
|
|
if ( text_log )
|
|
text_log->Print( "ON_RevSurface.m_t = (%g,%g) (should be an increasing interval)\n",
|
|
m_t[0],m_t[1]);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ON_RevSurface::Dump( ON_TextLog& dump ) const
|
|
{
|
|
ON_Object::Dump(dump); // print class id
|
|
dump.PushIndent();
|
|
if ( m_bTransposed )
|
|
dump.Print("Paramerization: (curve,angle)\n");
|
|
else
|
|
dump.Print("Paramerization: (angle,curve)\n");
|
|
dump.Print("Axis: ");
|
|
dump.Print(m_axis.from);
|
|
dump.Print(" to ");
|
|
dump.Print(m_axis.to);
|
|
dump.Print("\n");
|
|
dump.Print("Rotation angle: %g to %g radians.\n",m_angle[0],m_angle[1]);
|
|
dump.Print("Angle evaluation parameter interval: [%g,%g].\n",m_t[0],m_t[1]);
|
|
if ( m_curve ) {
|
|
dump.Print("Revolute: \n");
|
|
dump.PushIndent();
|
|
m_curve->Dump(dump);
|
|
dump.PopIndent();
|
|
}
|
|
dump.PopIndent();
|
|
}
|
|
|
|
bool ON_RevSurface::Write( ON_BinaryArchive& file ) const
|
|
{
|
|
bool rc = file.Write3dmChunkVersion(2,0);
|
|
if (rc)
|
|
{
|
|
rc = file.WriteLine( m_axis );
|
|
rc = file.WriteInterval( m_angle );
|
|
rc = file.WriteInterval( m_t );
|
|
rc = file.WriteBoundingBox( m_bbox );
|
|
rc = file.WriteInt( m_bTransposed?1:0 );
|
|
if ( m_curve )
|
|
{
|
|
rc = file.WriteChar((char)1);
|
|
if (rc) rc = file.WriteObject(*m_curve);
|
|
}
|
|
else
|
|
{
|
|
rc = file.WriteChar((char)0);
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_RevSurface::Read( ON_BinaryArchive& file )
|
|
{
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
char bHaveCurve = 0;
|
|
bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
|
|
if (rc && major_version == 1)
|
|
{
|
|
rc = file.ReadLine( m_axis );
|
|
rc = file.ReadInterval( m_angle );
|
|
rc = file.ReadBoundingBox( m_bbox );
|
|
int bTransposedAsInt = m_bTransposed ? 1 : 0;
|
|
rc = file.ReadInt( &bTransposedAsInt );
|
|
if (rc)
|
|
m_bTransposed = bTransposedAsInt ? true : false;
|
|
rc = file.ReadChar( &bHaveCurve );
|
|
if ( bHaveCurve )
|
|
{
|
|
ON_Object* obj = 0;
|
|
rc = file.ReadObject(&obj);
|
|
if ( obj )
|
|
{
|
|
m_curve = ON_Curve::Cast(obj);
|
|
if ( !m_curve )
|
|
delete obj;
|
|
}
|
|
}
|
|
m_t[0] = m_angle.Min();
|
|
m_t[1] = m_angle.Max();
|
|
}
|
|
else if (rc && major_version == 2)
|
|
{
|
|
rc = file.ReadLine( m_axis );
|
|
rc = file.ReadInterval( m_angle );
|
|
rc = file.ReadInterval( m_t );
|
|
rc = file.ReadBoundingBox( m_bbox );
|
|
int bTransposedAsInt = m_bTransposed ? 1 : 0;
|
|
rc = file.ReadInt( &bTransposedAsInt );
|
|
if (rc)
|
|
m_bTransposed = bTransposedAsInt ? true : false;
|
|
rc = file.ReadChar( &bHaveCurve );
|
|
if ( bHaveCurve )
|
|
{
|
|
ON_Object* obj = 0;
|
|
rc = file.ReadObject(&obj);
|
|
if ( obj )
|
|
{
|
|
m_curve = ON_Curve::Cast(obj);
|
|
if ( !m_curve )
|
|
delete obj;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int ON_RevSurface::Dimension() const
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
bool ON_RevSurface::Transform( const ON_Xform& xform )
|
|
{
|
|
DestroyRuntimeCache();
|
|
TransformUserData(xform);
|
|
bool rc = (m_curve) ? m_curve->Transform(xform) : false;
|
|
ON_3dVector X, Y, Z;
|
|
Z = m_axis.Tangent();
|
|
X.PerpendicularTo( Z );
|
|
X.Unitize();
|
|
Y = ON_CrossProduct( Z, X );
|
|
if ( !m_axis.Transform(xform) )
|
|
rc = false;
|
|
ON_3dVector transZ = m_axis.Tangent();
|
|
|
|
if ( transZ.Length() == 0.0 )
|
|
{
|
|
// transformation collapsed axis
|
|
m_axis.to = m_axis.from + Z;
|
|
}
|
|
else
|
|
{
|
|
// see if axis needs to be reversed.
|
|
// (Happens with transformations that
|
|
// have negative determinant - like mirroring.)
|
|
ON_3dVector transX = xform*X;
|
|
ON_3dVector transY = xform*Y;
|
|
ON_3dVector transXxY = ON_CrossProduct( transX, transY );
|
|
double d = transXxY*transZ;
|
|
if ( d < 0.0 )
|
|
m_axis.to = m_axis.from - m_axis.Direction();
|
|
}
|
|
|
|
m_bbox.Destroy();
|
|
m_bbox = BoundingBox();
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::Evaluate( // returns false if unable to evaluate
|
|
double s, // angle
|
|
double t, // curve_parameter
|
|
// evaluation parameters
|
|
int der_count, // number of derivatives (>=0)
|
|
int v_stride, // array stride (>=Dimension())
|
|
double* v, // array of length stride*(ndir+1)*(ndir+2)/2
|
|
int side, // optional - determines which quadrant to evaluate from
|
|
// 0 = default
|
|
// 1 from NE quadrant
|
|
// 2 from NW quadrant
|
|
// 3 from SW quadrant
|
|
// 4 from SE quadrant
|
|
int* hint // optional - evaluation hint (int[2]) used to speed
|
|
// repeated evaluations
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
|
|
double ds = 1.0;
|
|
double x,y,z;
|
|
int i, j, k, src_i, dst_i;
|
|
ON_3dPoint pt;
|
|
|
|
if ( m_bTransposed )
|
|
{
|
|
x = s; s = t; t = x;
|
|
if ( side == 2 ) side = 4; else if ( side == 4 ) side = 2;
|
|
}
|
|
|
|
if ( m_t != m_angle )
|
|
{
|
|
if ( m_t.m_t[1] != m_t.m_t[0] )
|
|
{
|
|
ds = (m_angle.m_t[1] - m_angle.m_t[0])/(m_t.m_t[1] - m_t.m_t[0]);
|
|
x = m_t.NormalizedParameterAt(s);
|
|
y = m_angle.ParameterAt(x);
|
|
s = y;
|
|
}
|
|
}
|
|
|
|
|
|
double a = cos(s);
|
|
double b = sin(s);
|
|
const double ca[4] = {a, -b, -a, b}; // cosine derivatives
|
|
const double sa[4] = {b, a, -b, -a}; // sine derivatives
|
|
|
|
const int curve_dim = m_curve ? m_curve->Dimension() : 0;
|
|
if ( curve_dim == 2 || curve_dim == 3 )
|
|
{
|
|
int curve_side = 0;
|
|
switch(side)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
curve_side = 1;
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
curve_side = -1;
|
|
break;
|
|
}
|
|
rc = m_curve->Evaluate( t, der_count, v_stride, v, curve_side, hint )?true:false;
|
|
if ( rc )
|
|
{
|
|
ON_3dVector zaxis = m_axis.Tangent();
|
|
ON_3dVector xaxis, yaxis;
|
|
xaxis.PerpendicularTo(zaxis);
|
|
xaxis.Unitize();
|
|
yaxis = ON_CrossProduct(zaxis,xaxis);
|
|
|
|
// move curve derivatives to pure t partial spaces in v[]
|
|
if ( curve_dim == 2 )
|
|
{
|
|
for ( i = der_count; i >= 1; i-- )
|
|
{
|
|
// move curve derivative to proper spots
|
|
src_i = v_stride*i;
|
|
dst_i = v_stride*((i+1)*(i+2)/2 - 1);
|
|
v[dst_i++] = v[src_i++];
|
|
v[dst_i++] = 0.0;
|
|
v[dst_i] = v[src_i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( i = der_count; i >= 1; i-- )
|
|
{
|
|
// move curve derivative to proper spots
|
|
src_i = v_stride*i;
|
|
dst_i = v_stride*((i+1)*(i+2)/2 - 1);
|
|
v[dst_i++] = v[src_i++];
|
|
v[dst_i++] = v[src_i++];
|
|
v[dst_i] = v[src_i];
|
|
}
|
|
}
|
|
|
|
// convert location coordinates to local frame with origin at m_axis.from
|
|
{
|
|
pt = ON_3dPoint(v)-m_axis.from;
|
|
v[0] = pt*xaxis;
|
|
v[1] = pt*yaxis;
|
|
v[2] = pt*zaxis;
|
|
}
|
|
|
|
// convert curve derivative coordinates to local frame
|
|
for ( i = 1; i <= der_count; i++ )
|
|
{
|
|
dst_i = v_stride*((i+1)*(i+2)/2 - 1);
|
|
pt = ON_3dPoint(v+dst_i); // pt = curve derivative in world coords
|
|
v[dst_i++] = pt*xaxis;
|
|
v[dst_i++] = pt*yaxis;
|
|
v[dst_i] = pt*zaxis;
|
|
}
|
|
|
|
for ( i = der_count; i >= 0; i-- )
|
|
{
|
|
// i = total order of derivative
|
|
double f = 1.0; // f = chain rule scale factor
|
|
for ( j = i; j >= 0; j-- )
|
|
{
|
|
// j = number of partials w.r.t curve parameter
|
|
// i-j = number of partials w.r.t angular parameter
|
|
dst_i = v_stride*(i*(i+1)/2 + j); //
|
|
src_i = v_stride*((j+1)*(j+2)/2 - 1); // curve derivative
|
|
k=(i-j)%4;
|
|
a = f*ca[k];
|
|
b = f*sa[k];
|
|
f *= ds;
|
|
|
|
// calculate derivative in local frame
|
|
x = a*v[src_i] - b*v[src_i+1];
|
|
y = b*v[src_i] + a*v[src_i+1];
|
|
z = (j<i) ? 0.0 : v[src_i+2];
|
|
// store answer in world coordinates
|
|
pt = x*xaxis + y*yaxis + z*zaxis;
|
|
v[dst_i++] = pt.x;
|
|
v[dst_i++] = pt.y;
|
|
v[dst_i] = pt.z;
|
|
}
|
|
}
|
|
|
|
// translate location
|
|
v[0] += m_axis.from.x;
|
|
v[1] += m_axis.from.y;
|
|
v[2] += m_axis.from.z;
|
|
|
|
if ( m_bTransposed )
|
|
{
|
|
for ( i = 1; i <= der_count; i++ )
|
|
{
|
|
for ( j = 0, k = i; j < k; j++, k-- )
|
|
{
|
|
dst_i = i*(i+1)/2;
|
|
src_i = dst_i + k;
|
|
dst_i += j;
|
|
src_i *= v_stride;
|
|
dst_i *= v_stride;
|
|
x = v[src_i]; v[src_i++] = v[dst_i]; v[dst_i++] = x;
|
|
x = v[src_i]; v[src_i++] = v[dst_i]; v[dst_i++] = x;
|
|
x = v[src_i]; v[src_i] = v[dst_i]; v[dst_i] = x;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
class ON_RevolutionTensor : public ON_TensorProduct
|
|
{
|
|
public:
|
|
ON_3dPoint O;
|
|
ON_3dVector X;
|
|
ON_3dVector Y;
|
|
ON_3dVector Z;
|
|
int DimensionA() const;
|
|
int DimensionB() const;
|
|
int DimensionC() const;
|
|
bool Evaluate( double, // a
|
|
const double*, // A
|
|
double, // b
|
|
const double*, // B
|
|
double* // C
|
|
);
|
|
};
|
|
|
|
int ON_RevolutionTensor::DimensionA() const
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
int ON_RevolutionTensor::DimensionB() const
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
int ON_RevolutionTensor::DimensionC() const
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
bool ON_RevolutionTensor::Evaluate( double a, const double* ArcPoint, double b, const double* ShapePoint, double* SrfPoint )
|
|
{
|
|
double x, y, z, c, s, rx, ry, A[2], B[3];
|
|
|
|
if (a != 1.0) {
|
|
A[0] = a*ArcPoint[0];
|
|
A[1] = a*ArcPoint[1];
|
|
ArcPoint = A;
|
|
}
|
|
|
|
if (b != 1.0) {
|
|
B[0] = b*ShapePoint[0];
|
|
B[1] = b*ShapePoint[1];
|
|
B[2] = b*ShapePoint[2];
|
|
ShapePoint = B;
|
|
}
|
|
|
|
|
|
x = (ShapePoint[0] - O.x)*X.x + (ShapePoint[1] - O.y)*X.y + (ShapePoint[2] - O.z)*X.z;
|
|
y = (ShapePoint[0] - O.x)*Y.x + (ShapePoint[1] - O.y)*Y.y + (ShapePoint[2] - O.z)*Y.z;
|
|
z = (ShapePoint[0] - O.x)*Z.x + (ShapePoint[1] - O.y)*Z.y + (ShapePoint[2] - O.z)*Z.z;
|
|
|
|
c = ArcPoint[0];
|
|
s = ArcPoint[1];
|
|
|
|
rx = c*x - s*y;
|
|
ry = s*x + c*y;
|
|
|
|
SrfPoint[0] = O.x + rx*X.x + ry*Y.x + z*Z.x;
|
|
SrfPoint[1] = O.y + rx*X.y + ry*Y.y + z*Z.y;
|
|
SrfPoint[2] = O.z + rx*X.z + ry*Y.z + z*Z.z;
|
|
|
|
return true;
|
|
}
|
|
|
|
int ON_RevSurface::GetNurbForm(class ON_NurbsSurface& srf , double tolerance ) const
|
|
{
|
|
int rc = 0;
|
|
if ( 0 != m_curve )
|
|
{
|
|
ON_NurbsCurve a, c;
|
|
ON_Arc arc;
|
|
arc.plane.CreateFromNormal( ON_3dPoint::Origin, ON_3dVector::ZAxis );
|
|
arc.radius = 1.0;
|
|
arc.SetAngleRadians(m_angle[1]-m_angle[0]);
|
|
if ( arc.GetNurbForm(a) )
|
|
{
|
|
if ( m_t.IsIncreasing() )
|
|
a.SetDomain( m_t[0], m_t[1] );
|
|
rc = m_curve->GetNurbForm(c,tolerance);
|
|
if (rc)
|
|
{
|
|
if ( 2 == c.m_dim )
|
|
{
|
|
// Increasing the dimension of a 2d curve to 3d fixes
|
|
// was added to make the Scale1D operation in
|
|
// bug # 103845 work.
|
|
ON_WARNING("ON_RevSurface.m_curve is 2-dimensional.");
|
|
c.ChangeDimension(3);
|
|
}
|
|
if ( 3 != c.m_dim )
|
|
{
|
|
ON_ERROR("ON_RevSurface.m_curve is not valid.");
|
|
return 0;
|
|
}
|
|
|
|
if ( m_angle[0] != 0.0 )
|
|
{
|
|
c.Rotate( m_angle[0], m_axis.Direction(), m_axis.from );
|
|
}
|
|
ON_RevolutionTensor rho;
|
|
rho.O = m_axis.from;
|
|
rho.Z = m_axis.Direction();
|
|
rho.Z.Unitize();
|
|
rho.X.PerpendicularTo(rho.Z);
|
|
rho.X.Unitize();
|
|
rho.Y = ON_CrossProduct(rho.Z,rho.X);
|
|
rho.Y.Unitize();
|
|
if ( !srf.TensorProduct( a, c, rho ) )
|
|
{
|
|
// Testing for false here prevents crashes
|
|
// when and was added as part of investigating
|
|
// bug # 103845. A change a few lines up
|
|
// made it so that particular bug no longer
|
|
// fails to create a nurbs surface.
|
|
return 0;
|
|
}
|
|
|
|
// make singular points "spot on"
|
|
ON_3dPoint C0 = c.PointAtStart();
|
|
ON_3dPoint C1 = c.PointAtEnd();
|
|
ON_3dPoint A0, A1;
|
|
ON_4dPoint CV;
|
|
double t0 = ON_UNSET_VALUE;
|
|
double t1 = ON_UNSET_VALUE;
|
|
if (m_axis.ClosestPointTo(C0,&t0) && ON_IsValid(t0) )
|
|
{
|
|
A0 = m_axis.PointAt(t0);
|
|
if ( C0.DistanceTo(A0) <= ON_ZERO_TOLERANCE )
|
|
{
|
|
// SouthPole
|
|
int j = 0;
|
|
for ( int i = 0; i < srf.m_cv_count[0]; i++ )
|
|
{
|
|
CV.w = srf.Weight(i,j);
|
|
CV.x = CV.w*A0.x;
|
|
CV.y = CV.w*A0.y;
|
|
CV.z = CV.w*A0.z;
|
|
srf.SetCV(i,j,CV);
|
|
}
|
|
}
|
|
}
|
|
if (m_axis.ClosestPointTo(C1,&t1) && ON_IsValid(t1) )
|
|
{
|
|
A1 = m_axis.PointAt(t1);
|
|
if ( C1.DistanceTo(A1) <= ON_ZERO_TOLERANCE )
|
|
{
|
|
// NorthPole
|
|
int j = srf.m_cv_count[1]-1;
|
|
for ( int i = 0; i < srf.m_cv_count[0]; i++ )
|
|
{
|
|
CV.w = srf.Weight(i,j);
|
|
CV.x = CV.w*A1.x;
|
|
CV.y = CV.w*A1.y;
|
|
CV.z = CV.w*A1.z;
|
|
srf.SetCV(i,j,CV);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( m_bTransposed )
|
|
srf.Transpose();
|
|
}
|
|
}
|
|
}
|
|
return (rc > 0) ? 2 : 0;
|
|
}
|
|
|
|
int ON_RevSurface::HasNurbForm() const
|
|
|
|
{
|
|
if (!IsValid())
|
|
return 0;
|
|
return 2;
|
|
}
|
|
|
|
bool ON_RevSurface::GetSurfaceParameterFromNurbFormParameter(
|
|
double nurbs_s, double nurbs_t,
|
|
double* surface_s, double* surface_t
|
|
) const
|
|
{
|
|
// NOTE: overrides ON_Surface virtual function
|
|
bool rc = (0 != m_curve);
|
|
|
|
|
|
|
|
if ( m_bTransposed )
|
|
{
|
|
double* pTemp = surface_s; surface_s = surface_t; surface_t = pTemp;
|
|
double temp = nurbs_s; nurbs_s = nurbs_t; nurbs_t = temp;
|
|
}
|
|
|
|
*surface_s = nurbs_s;
|
|
*surface_t = nurbs_t;
|
|
|
|
ON_Circle circle( ON_xy_plane, 1.0 );
|
|
ON_Arc arc( circle, m_angle );
|
|
ON_ArcCurve arc_curve(arc, m_t[0], m_t[1]);
|
|
if ( !arc_curve.GetCurveParameterFromNurbFormParameter( nurbs_s, surface_s ) )
|
|
rc = false;
|
|
|
|
if ( m_curve )
|
|
{
|
|
if (!m_curve->GetCurveParameterFromNurbFormParameter( nurbs_t, surface_t ))
|
|
rc = false;
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::GetNurbFormParameterFromSurfaceParameter(
|
|
double surface_s, double surface_t,
|
|
double* nurbs_s, double* nurbs_t
|
|
) const
|
|
{
|
|
// NOTE: overrides ON_Surface virtual function
|
|
bool rc = (0 != m_curve);
|
|
|
|
if ( m_bTransposed )
|
|
{
|
|
double temp = surface_s; surface_s = surface_t; surface_t = temp;
|
|
double* pTemp = nurbs_s; nurbs_s = nurbs_t; nurbs_t = pTemp;
|
|
}
|
|
|
|
*nurbs_s = surface_s;
|
|
*nurbs_t = surface_t;
|
|
|
|
ON_Circle circle( ON_xy_plane, 1.0 );
|
|
ON_Arc arc( circle, m_angle );
|
|
ON_ArcCurve arc_curve(arc, m_t[0], m_t[1]);
|
|
if ( !arc_curve.GetNurbFormParameterFromCurveParameter( surface_s, nurbs_s ) )
|
|
rc = false;
|
|
|
|
if ( m_curve )
|
|
{
|
|
if (!m_curve->GetNurbFormParameterFromCurveParameter( surface_t, nurbs_t ))
|
|
rc = false;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
ON_Arc ON_RevSurface::IsoArc(
|
|
double curve_parameter
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == m_curve)
|
|
break;
|
|
// 8 December 2003 Chuck - fix iso extraction bug
|
|
// when m_angle[0] != 0.
|
|
ON_Circle circle;
|
|
ON_3dPoint P = m_curve->PointAt(curve_parameter);
|
|
if (false == P.IsValid())
|
|
break;
|
|
circle.plane.origin = m_axis.ClosestPointTo(P);
|
|
circle.plane.zaxis = m_axis.Tangent();
|
|
circle.plane.xaxis = P - circle.plane.origin;
|
|
circle.radius = circle.plane.xaxis.Length();
|
|
if (!circle.plane.xaxis.Unitize())
|
|
{
|
|
// 8 December 2003 Dale Lear - get valid zero radius
|
|
// arc/circle when revolute hits the axis.
|
|
// First: try middle of revolute for x-axis
|
|
P = m_curve->PointAt(m_curve->Domain().ParameterAt(0.5));
|
|
ON_3dPoint Q = m_axis.ClosestPointTo(P);
|
|
circle.plane.xaxis = P - Q;
|
|
if (!circle.plane.xaxis.Unitize())
|
|
{
|
|
// Then: just use a vector perp to zaxis
|
|
circle.plane.xaxis.PerpendicularTo(circle.plane.zaxis);
|
|
}
|
|
}
|
|
circle.plane.yaxis = ON_CrossProduct(circle.plane.zaxis, circle.plane.xaxis);
|
|
circle.plane.yaxis.Unitize();
|
|
circle.plane.UpdateEquation();
|
|
ON_Arc arc(circle, m_angle);
|
|
return arc;
|
|
}
|
|
ON_Arc arc;
|
|
arc.plane = ON_Plane::NanPlane;
|
|
arc.radius = ON_DBL_QNAN;
|
|
return arc;
|
|
}
|
|
|
|
|
|
ON_Curve* ON_RevSurface::IsoCurve( int dir, double c ) const
|
|
{
|
|
if ( dir < 0 || dir > 1 || !m_curve )
|
|
return nullptr;
|
|
|
|
ON_Curve* crv = 0;
|
|
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
|
|
if ( dir == 0 )
|
|
{
|
|
// 8 December 2003 Chuck - fix iso extraction bug
|
|
// when m_angle[0] != 0.
|
|
ON_Circle circle;
|
|
ON_3dPoint P = m_curve->PointAt(c);
|
|
circle.plane.origin = m_axis.ClosestPointTo(P);
|
|
circle.plane.zaxis = m_axis.Tangent();
|
|
circle.plane.xaxis = P - circle.plane.origin;
|
|
circle.radius = circle.plane.xaxis.Length();
|
|
if ( !circle.plane.xaxis.Unitize() )
|
|
{
|
|
// 8 December 2003 Dale Lear - get valid zero radius
|
|
// arc/circle when revolute hits the axis.
|
|
// First: try middle of revolute for x-axis
|
|
P = m_curve->PointAt(m_curve->Domain().ParameterAt(0.5));
|
|
ON_3dPoint Q = m_axis.ClosestPointTo(P);
|
|
circle.plane.xaxis = P-Q;
|
|
if ( !circle.plane.xaxis.Unitize() )
|
|
{
|
|
// Then: just use a vector perp to zaxis
|
|
circle.plane.xaxis.PerpendicularTo(circle.plane.zaxis);
|
|
}
|
|
}
|
|
circle.plane.yaxis = ON_CrossProduct( circle.plane.zaxis, circle.plane.xaxis );
|
|
circle.plane.yaxis.Unitize();
|
|
circle.plane.UpdateEquation();
|
|
ON_Arc arc( circle, m_angle );
|
|
crv = new ON_ArcCurve(arc, m_t[0], m_t[1]);
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
{
|
|
crv = m_curve->DuplicateCurve();
|
|
if ( crv )
|
|
{
|
|
double a = c;
|
|
if ( m_t != m_angle )
|
|
{
|
|
double t = m_t.NormalizedParameterAt(c);
|
|
a = m_angle.ParameterAt(t);
|
|
}
|
|
if ( a != 0.0 )
|
|
{
|
|
crv->Rotate( a, m_axis.Direction(), m_axis.from );
|
|
}
|
|
}
|
|
}
|
|
return crv;
|
|
}
|
|
|
|
bool ON_RevSurface::Trim( int dir, const ON_Interval& domain )
|
|
{
|
|
bool rc = false;
|
|
if ( dir != 0 && dir != 1 )
|
|
return false;
|
|
if ( !domain.IsIncreasing() )
|
|
return false;
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
if ( dir == 0 )
|
|
{
|
|
ON_Interval dom;
|
|
dom.Intersection(domain,m_t);
|
|
if ( !dom.IsIncreasing() || !m_t.IsIncreasing() || !m_angle.IsIncreasing() )
|
|
return false;
|
|
double t0 = m_t.NormalizedParameterAt(dom[0]);
|
|
double t1 = m_t.NormalizedParameterAt(dom[1]);
|
|
ON_Interval a;
|
|
a[0] = m_angle.ParameterAt(t0);
|
|
a[1] = m_angle.ParameterAt(t1);
|
|
double d = a.Length();
|
|
if ( fabs(d) > ON_ZERO_TOLERANCE && fabs(d) <= 2.0*ON_PI+ON_ZERO_TOLERANCE )
|
|
{
|
|
m_angle = a;
|
|
m_t = domain;
|
|
rc = true;
|
|
}
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
{
|
|
rc = m_curve->Trim( domain );
|
|
}
|
|
if ( rc )
|
|
{
|
|
// update bounding box
|
|
ON_BoundingBox bbox0 = m_bbox;
|
|
m_bbox.Destroy();
|
|
BoundingBox();
|
|
if ( m_bbox.IsValid() && bbox0.IsValid() )
|
|
m_bbox.Intersection(bbox0);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::Extend(
|
|
int dir,
|
|
const ON_Interval& domain
|
|
)
|
|
{
|
|
if ( dir != 0 && dir != 1 )
|
|
return false;
|
|
if (IsClosed(dir))
|
|
return false;
|
|
bool do_it = false;
|
|
ON_Interval dom = Domain(dir);
|
|
if (domain[0] < dom[0])
|
|
{
|
|
dom[0] = domain[0];
|
|
do_it = true;
|
|
}
|
|
|
|
if (domain[1] > dom[1])
|
|
{
|
|
dom[1] = domain[1];
|
|
do_it = true;
|
|
}
|
|
|
|
if (!do_it)
|
|
return false;
|
|
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
|
|
bool rc = false;
|
|
|
|
if ( dir == 0 )
|
|
{
|
|
double t0 = m_t.NormalizedParameterAt(dom[0]);
|
|
double t1 = m_t.NormalizedParameterAt(dom[1]);
|
|
ON_Interval a;
|
|
a[0] = m_angle.ParameterAt(t0);
|
|
a[1] = m_angle.ParameterAt(t1);
|
|
if (a.Length() > 2.0*ON_PI+ON_ZERO_TOLERANCE) a[1] = a[0]+2.0*ON_PI;
|
|
m_angle = a;
|
|
m_t = dom;
|
|
rc = true;
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
{
|
|
rc = m_curve->Extend(dom);
|
|
}
|
|
|
|
if ( rc )
|
|
{
|
|
DestroySurfaceTree();
|
|
// update bounding box
|
|
//ON_BoundingBox bbox0 = m_bbox;
|
|
m_bbox.Destroy();
|
|
BoundingBox();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::Split(
|
|
int dir,
|
|
double c,
|
|
ON_Surface*& west_or_south_side,
|
|
ON_Surface*& east_or_north_side
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
|
|
ON_RevSurface* srf_ws=ON_RevSurface::Cast(west_or_south_side);
|
|
ON_RevSurface* srf_en=ON_RevSurface::Cast(east_or_north_side);
|
|
if ( srf_ws && srf_ws == srf_en )
|
|
return false;
|
|
if ( west_or_south_side && !srf_ws )
|
|
return false;
|
|
if ( east_or_north_side && !srf_en )
|
|
return false;
|
|
|
|
if ( dir != 0 && dir != 1 )
|
|
return false;
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
|
|
ON_Curve* left_side = 0;
|
|
ON_Curve* right_side = 0;
|
|
ON_Interval left_angle, right_angle;
|
|
ON_Interval left_t, right_t;
|
|
left_angle = m_angle;
|
|
right_angle = m_angle;
|
|
left_t = m_t;
|
|
right_t = m_t;
|
|
|
|
if ( dir == 0 )
|
|
{
|
|
double t = m_t.NormalizedParameterAt(c);
|
|
if ( m_t.Includes(c,true) && 0.0 < t && t < 1.0 )
|
|
{
|
|
double a = m_angle.ParameterAt( t );
|
|
if ( m_angle.Includes(a,true) )
|
|
{
|
|
rc = true;
|
|
|
|
left_angle[1] = a;
|
|
right_angle[0] = a;
|
|
left_t[1] = c;
|
|
right_t[0] = c;
|
|
|
|
if ( srf_ws == this )
|
|
left_side = m_curve;
|
|
else
|
|
left_side = m_curve->Duplicate();
|
|
if ( srf_en == this )
|
|
right_side = m_curve;
|
|
else
|
|
right_side = m_curve->Duplicate();
|
|
}
|
|
}
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
{
|
|
rc = m_curve->Split( c, left_side, right_side );
|
|
if ( rc )
|
|
{
|
|
if ( this == srf_ws )
|
|
{
|
|
delete m_curve;
|
|
srf_ws->m_curve = left_side;
|
|
}
|
|
else if ( this == srf_en )
|
|
{
|
|
delete m_curve;
|
|
srf_en->m_curve = right_side;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( rc )
|
|
{
|
|
// save input bounding box
|
|
const ON_BoundingBox input_bbox(m_bbox);
|
|
|
|
if ( !srf_ws )
|
|
west_or_south_side = srf_ws = new ON_RevSurface();
|
|
else if ( srf_ws != this && srf_ws->m_curve )
|
|
{
|
|
delete srf_ws->m_curve;
|
|
srf_ws->m_curve = 0;
|
|
}
|
|
if ( !srf_en )
|
|
east_or_north_side = srf_en = new ON_RevSurface();
|
|
if ( srf_en != this && srf_en->m_curve )
|
|
{
|
|
delete srf_en->m_curve;
|
|
srf_en->m_curve = 0;
|
|
}
|
|
|
|
srf_ws->m_axis = m_axis;
|
|
srf_ws->m_angle = left_angle;
|
|
srf_ws->m_t = left_t;
|
|
srf_ws->m_bTransposed = m_bTransposed;
|
|
srf_ws->m_curve = left_side;
|
|
srf_ws->m_bbox.Destroy();
|
|
|
|
srf_en->m_axis = m_axis;
|
|
srf_en->m_angle = right_angle;
|
|
srf_en->m_t = right_t;
|
|
srf_en->m_bTransposed = m_bTransposed;
|
|
srf_en->m_curve = right_side;
|
|
srf_en->m_bbox.Destroy();
|
|
|
|
// update bounding boxes
|
|
// if input box was valid, intersect calculated boxes with
|
|
// input box
|
|
srf_ws->BoundingBox(); // calculates srf_ws->m_bbox
|
|
if ( srf_ws->m_bbox.IsValid() && input_bbox.IsValid() )
|
|
srf_ws->m_bbox.Intersection(input_bbox);
|
|
|
|
srf_en->BoundingBox(); // calculates srf_en->m_bbox
|
|
if ( srf_en->m_bbox.IsValid() && input_bbox.IsValid() )
|
|
srf_en->m_bbox.Intersection(input_bbox);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_RevSurface::Transpose()
|
|
{
|
|
m_bTransposed = m_bTransposed ? false : true;
|
|
return true;
|
|
}
|
|
|
|
bool ON_RevSurface::IsContinuous(
|
|
ON::continuity desired_continuity,
|
|
double s,
|
|
double t,
|
|
int* hint, // default = nullptr,
|
|
double point_tolerance, // default=ON_ZERO_TOLERANCE
|
|
double d1_tolerance, // default==ON_ZERO_TOLERANCE
|
|
double d2_tolerance, // default==ON_ZERO_TOLERANCE
|
|
double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE
|
|
double curvature_tolerance // default==ON_SQRT_EPSILON
|
|
) const
|
|
{
|
|
bool rc = true;
|
|
if ( m_curve )
|
|
{
|
|
double curve_t = m_bTransposed ? s : t;
|
|
rc = m_curve->IsContinuous( desired_continuity, curve_t, hint,
|
|
point_tolerance, d1_tolerance, d2_tolerance,
|
|
cos_angle_tolerance, curvature_tolerance );
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::Reverse( int dir )
|
|
{
|
|
bool rc = false;
|
|
if ( m_bTransposed )
|
|
dir = dir ? 0 : 1;
|
|
if ( dir == 0 )
|
|
{
|
|
m_axis.Reverse();
|
|
double a0 = m_angle[0];
|
|
double a1 = m_angle[1];
|
|
m_angle.Set( 2.0*ON_PI - a1, 2.0*ON_PI - a0 );
|
|
m_t.Reverse();
|
|
rc = true;
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
{
|
|
rc = m_curve->Reverse();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::GetNextDiscontinuity(
|
|
int dir,
|
|
ON::continuity c,
|
|
double t0,
|
|
double t1,
|
|
double* t,
|
|
int* hint,
|
|
int* dtype,
|
|
double cos_angle_tolerance,
|
|
double curvature_tolerance
|
|
) const
|
|
{
|
|
// 28 Jan 2005 - untested code
|
|
bool rc = false;
|
|
if ( (m_bTransposed ? 1 : 0) == dir )
|
|
{
|
|
// angle direction
|
|
ON_Circle circle(ON_xy_plane,1.0);
|
|
ON_Arc arc( circle, m_angle );
|
|
ON_ArcCurve arc_curve(arc, m_t[0], m_t[1]);
|
|
rc = arc_curve.GetNextDiscontinuity(
|
|
c,
|
|
t0,t1,t,
|
|
(hint? &hint[dir] : 0),
|
|
dtype,cos_angle_tolerance,
|
|
curvature_tolerance);
|
|
}
|
|
else
|
|
{
|
|
rc = m_curve->GetNextDiscontinuity(
|
|
c,
|
|
t0,t1,t,
|
|
(hint? &hint[dir] : 0),
|
|
dtype,cos_angle_tolerance,
|
|
curvature_tolerance);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::IsSingular( int side ) const // 0 = south, 1 = east, 2 = north, 3 = west
|
|
{
|
|
bool rc = false;
|
|
ON_3dPoint P, Q;
|
|
//double d, tol;
|
|
if ( side < 0 || side > 3 )
|
|
return false;
|
|
|
|
if ( m_bTransposed )
|
|
{
|
|
switch(side)
|
|
{
|
|
case 0: side = 3; break;
|
|
case 1: side = 2; break;
|
|
case 2: side = 1; break;
|
|
case 3: side = 0; break;
|
|
}
|
|
}
|
|
|
|
if ( 0 == side || 2 == side )
|
|
{
|
|
P = (0 == side)
|
|
? m_curve->PointAtStart()
|
|
: m_curve->PointAtEnd();
|
|
Q = m_axis.ClosestPointTo(P);
|
|
|
|
// 26 Feb 2003 Dale Lear
|
|
//
|
|
// I changed the code to handle cases when the
|
|
// "Q" has some coordinates that are small and
|
|
// other coordinates that are very large. The
|
|
// fabs(Q.*)*ON_SQRT_EPSILON term is required
|
|
// in cases where the evaluations in PointAtStart()
|
|
// and/or ClosestPointTo() have more numerical
|
|
// error than ON_ZERO_TOLERANCE. The numerical
|
|
// tolerance term has to be calculated on a
|
|
// coordinate-by-coordinate basis. See RR 9683.
|
|
//
|
|
// old test:
|
|
//d = P.DistanceTo(Q);
|
|
//if ( d <= ON_ZERO_TOLERANCE )
|
|
// rc = true;
|
|
|
|
// 12 July 2012 Dale Lear
|
|
// Use ON_PointsAreCoincident() for all IsSingular queries
|
|
//
|
|
//d = fabs(P.x - Q.x);
|
|
//tol = ON_ZERO_TOLERANCE + fabs(Q.x)*ON_SQRT_EPSILON;
|
|
//if ( d <= tol )
|
|
//{
|
|
// d = fabs(P.y - Q.y);
|
|
// tol = ON_ZERO_TOLERANCE + fabs(Q.y)*ON_SQRT_EPSILON;
|
|
// if ( d <= tol )
|
|
// {
|
|
// d = fabs(P.z - Q.z);
|
|
// tol = ON_ZERO_TOLERANCE + fabs(Q.z)*ON_SQRT_EPSILON;
|
|
// if ( d <= tol )
|
|
// rc = true;
|
|
// }
|
|
//}
|
|
rc = ON_PointsAreCoincident(3,0,&P.x,&Q.x);
|
|
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::IsPeriodic( int dir ) const // dir 0 = "s", 1 = "t"
|
|
{
|
|
bool rc = false;
|
|
if ( m_bTransposed )
|
|
dir = dir ? 0 : 1;
|
|
if ( dir == 0 )
|
|
{
|
|
if (m_angle.Length() >= 2.0*ON_PI-ON_ZERO_TOLERANCE )
|
|
rc = true;
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
{
|
|
rc = m_curve->IsPeriodic();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::IsPlanar(
|
|
ON_Plane* plane,
|
|
double tolerance
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
if( IsValid()){
|
|
ON_Plane AxisNormal( m_curve->PointAtStart(), m_axis.Tangent() );
|
|
rc = m_curve->IsInPlane( AxisNormal, tolerance);
|
|
if(rc && plane)
|
|
*plane = AxisNormal;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::IsClosed( int dir ) const // dir 0 = "s", 1 = "t"
|
|
{
|
|
bool rc = false;
|
|
if ( m_bTransposed )
|
|
dir = dir ? 0 : 1;
|
|
if ( dir == 0 )
|
|
{
|
|
if (m_angle.Length() >= 2.0*ON_PI-ON_ZERO_TOLERANCE )
|
|
rc = true;
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
{
|
|
rc = m_curve->IsClosed();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus
|
|
int dir, // 0 gets first parameter, 1 gets second parameter
|
|
double t, // t = parameter in domain
|
|
double* tminus, // tminus
|
|
double* tplus // tplus
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
if ( m_bTransposed )
|
|
dir = dir ? 0 : 1;
|
|
if ( dir == 0 )
|
|
{
|
|
if ( m_t.IsIncreasing() )
|
|
rc = ON_GetParameterTolerance( m_t[0], m_t[1], t, tminus, tplus );
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
{
|
|
rc = m_curve->GetParameterTolerance( t, tminus, tplus );
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::SetDomain(
|
|
int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain
|
|
double t0,
|
|
double t1
|
|
)
|
|
{
|
|
bool rc = false;
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
if ( dir == 0 )
|
|
{
|
|
if ( t0 < t1 )
|
|
{
|
|
m_t.Set(t0,t1);
|
|
DestroyRuntimeCache();
|
|
rc = true;
|
|
}
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
{
|
|
rc = m_curve->SetDomain( t0, t1 ) ? true : false;
|
|
DestroyRuntimeCache();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
ON_Interval ON_RevSurface::Domain( int dir ) const
|
|
{
|
|
ON_Interval d;
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
if ( dir == 0 )
|
|
{
|
|
d = m_t;
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
{
|
|
d = m_curve->Domain();
|
|
}
|
|
return d;
|
|
}
|
|
|
|
bool ON_RevSurface::GetSurfaceSize(
|
|
double* width,
|
|
double* height
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
if ( m_bTransposed )
|
|
{
|
|
double* ptr = width;
|
|
width = height;
|
|
height = ptr;
|
|
}
|
|
if ( m_curve )
|
|
{
|
|
rc = true;
|
|
|
|
ON_Interval cdom = m_curve->Domain();
|
|
int i, hint = 0;
|
|
int imax = 64;
|
|
double d = 1.0/((double)imax);
|
|
ON_3dPoint pt0 = ON_3dPoint::UnsetPoint;
|
|
ON_3dPoint pt;
|
|
double length_estimate = 0.0;
|
|
|
|
if ( width != nullptr || height != nullptr )
|
|
{
|
|
double radius_estimate = 0.0;
|
|
double r;
|
|
for ( i = 0; i <= imax; i++ )
|
|
{
|
|
if ( m_curve->EvPoint( cdom.ParameterAt(i*d), pt, 0, &hint ) )
|
|
{
|
|
r = m_axis.DistanceTo(pt);
|
|
if ( r > radius_estimate )
|
|
radius_estimate = r;
|
|
if ( pt0 != ON_3dPoint::UnsetPoint )
|
|
length_estimate += pt0.DistanceTo(pt);
|
|
pt0 = pt;
|
|
}
|
|
}
|
|
if ( width != nullptr )
|
|
*width = m_angle.Length()*radius_estimate;
|
|
}
|
|
|
|
if ( height != nullptr )
|
|
{
|
|
*height = length_estimate;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( width )
|
|
*width = 0.0;
|
|
if ( height )
|
|
*height = 0.0;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int ON_RevSurface::SpanCount( int dir ) const
|
|
{
|
|
int span_count = 0;
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
if ( dir==0 && m_t.IsIncreasing() )
|
|
{
|
|
double a = (0.5 + ON_SQRT_EPSILON)*ON_PI;
|
|
double da = fabs(m_angle.Length());
|
|
if (da <= a)
|
|
span_count = 1;
|
|
else if (da <= 2.0*a)
|
|
span_count = 2;
|
|
else
|
|
span_count = 4;
|
|
}
|
|
else if ( dir == 1 && m_curve )
|
|
span_count = m_curve->SpanCount();
|
|
return span_count;
|
|
}
|
|
|
|
|
|
bool ON_RevSurface::GetSpanVector( int dir, double* s ) const
|
|
{
|
|
bool rc = false;
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
if ( dir==0 && m_t.IsIncreasing() ) {
|
|
int span_count = SpanCount(m_bTransposed?1-dir:dir);
|
|
if ( span_count > 0 )
|
|
{
|
|
double d = 1.0/span_count;
|
|
s[0] = m_t[0];
|
|
for ( int i = 1; i < span_count; i++ )
|
|
s[i] = m_t.ParameterAt( i*d );
|
|
s[span_count] = m_t[1];
|
|
rc = true;
|
|
}
|
|
}
|
|
else if ( dir==1 && m_curve ) {
|
|
rc = m_curve->GetSpanVector(s);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
int ON_RevSurface::Degree( int dir ) const
|
|
{
|
|
int span_degree = 0;
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
if ( dir == 0 )
|
|
span_degree = 2;
|
|
else if ( dir == 1 && m_curve )
|
|
span_degree = m_curve->Degree();
|
|
return span_degree;
|
|
}
|
|
|
|
void ON_RevSurface::ClearBoundingBox()
|
|
{
|
|
m_bbox.Destroy();
|
|
}
|
|
|
|
bool ON_RevSurface::GetBBox( // returns true if successful
|
|
double* boxmin, // boxmin[dim]
|
|
double* boxmax, // boxmax[dim]
|
|
bool bGrowBox // true means grow box
|
|
) const
|
|
{
|
|
bool rc = m_bbox.IsValid();
|
|
|
|
if ( !rc )
|
|
{
|
|
ON_BoundingBox bbox, cbox, abox;
|
|
rc = m_curve->GetBoundingBox( cbox );
|
|
if (rc)
|
|
{
|
|
//Dec 16 2010 - Chuck - if the angle range does not include 0, m_curve is not part of the surface.
|
|
//bbox = cbox;
|
|
|
|
ON_3dPointArray corners;
|
|
cbox.GetCorners(corners);
|
|
ON_3dPoint P;
|
|
ON_Arc arc;
|
|
arc.plane.zaxis = m_axis.Tangent();
|
|
arc.SetAngleRadians(m_angle[1] - m_angle[0]);
|
|
int i;
|
|
double t;
|
|
for ( i = 0; i < corners.Count(); i++ )
|
|
{
|
|
P = corners[i];
|
|
abox.Set(P,false);
|
|
while( m_axis.ClosestPointTo(P,&t) ) // not a loop - used for flow control
|
|
{
|
|
// If we cannot construct a valid arc, then P and the point on the axis
|
|
// are added to the bounding box. One case where this happens is when
|
|
// P is on the axis of revolution. See bug 84354.
|
|
|
|
arc.plane.origin = m_axis.PointAt(t);
|
|
arc.plane.xaxis = P-arc.plane.origin;
|
|
abox.Set(arc.plane.origin,true);
|
|
arc.radius = arc.plane.xaxis.Length();
|
|
if ( !arc.plane.xaxis.Unitize() )
|
|
break;
|
|
double dot = arc.plane.xaxis*arc.plane.zaxis;
|
|
if ( fabs(dot) > 0.0001 )
|
|
break;
|
|
arc.plane.yaxis = ON_CrossProduct(arc.plane.zaxis,arc.plane.xaxis);
|
|
if ( !arc.plane.yaxis.Unitize() )
|
|
break;
|
|
arc.plane.UpdateEquation();
|
|
arc.plane.Rotate( m_angle[0], arc.plane.zaxis );
|
|
if ( !arc.IsValid() )
|
|
break;
|
|
abox = arc.BoundingBox();
|
|
break;
|
|
}
|
|
bbox.Union(abox);
|
|
}
|
|
|
|
if ( bbox.IsValid() )
|
|
{
|
|
ON_RevSurface* ptr = const_cast<ON_RevSurface*>(this);
|
|
ptr->m_bbox = bbox;
|
|
rc = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( rc )
|
|
{
|
|
if ( boxmin )
|
|
{
|
|
if (bGrowBox){
|
|
if (m_bbox.m_min.x < boxmin[0]) boxmin[0] = m_bbox.m_min.x;
|
|
if (m_bbox.m_min.y < boxmin[1]) boxmin[1] = m_bbox.m_min.y;
|
|
if (m_bbox.m_min.z < boxmin[2]) boxmin[2] = m_bbox.m_min.z;
|
|
}
|
|
else {
|
|
boxmin[0] = m_bbox.m_min.x;
|
|
boxmin[1] = m_bbox.m_min.y;
|
|
boxmin[2] = m_bbox.m_min.z;
|
|
}
|
|
}
|
|
if ( boxmax )
|
|
{
|
|
if (bGrowBox){
|
|
if (m_bbox.m_max.x > boxmax[0]) boxmax[0] = m_bbox.m_max.x;
|
|
if (m_bbox.m_max.y > boxmax[1]) boxmax[1] = m_bbox.m_max.y;
|
|
if (m_bbox.m_max.z > boxmax[2]) boxmax[2] = m_bbox.m_max.z;
|
|
}
|
|
else {
|
|
boxmax[0] = m_bbox.m_max.x;
|
|
boxmax[1] = m_bbox.m_max.y;
|
|
boxmax[2] = m_bbox.m_max.z;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_RevSurface::IsSpherical(ON_Sphere* sphere, double tolerance ) const
|
|
{
|
|
bool rc = false;
|
|
if ( m_curve )
|
|
{
|
|
ON_Plane plane;
|
|
ON_Arc arc;
|
|
ON_3dPoint P = m_curve->PointAt( m_curve->Domain().Mid() );
|
|
plane.origin = m_axis.from;
|
|
plane.yaxis = m_axis.Tangent();
|
|
plane.zaxis = ON_CrossProduct( P-plane.origin, plane.yaxis );
|
|
plane.zaxis.Unitize();
|
|
plane.xaxis = ON_CrossProduct( plane.yaxis, plane.zaxis );
|
|
plane.UpdateEquation();
|
|
if ( plane.IsValid() )
|
|
{
|
|
if ( m_curve->IsArc( &plane, &arc, tolerance ) )
|
|
{
|
|
P = m_axis.ClosestPointTo( arc.Center() );
|
|
if ( P.DistanceTo(arc.Center()) <= tolerance )
|
|
{
|
|
rc = true;
|
|
if ( sphere )
|
|
{
|
|
sphere->plane.origin = arc.Center();
|
|
sphere->plane.zaxis = m_axis.Tangent();
|
|
sphere->plane.yaxis = arc.plane.zaxis;
|
|
sphere->plane.xaxis = ON_CrossProduct( sphere->plane.zaxis, sphere->plane.yaxis );
|
|
sphere->plane.UpdateEquation();
|
|
sphere->radius = arc.radius;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_Surface::IsSphere( ON_Sphere* sphere, double tolerance ) const
|
|
{
|
|
if ( !ON_IsValid(tolerance) || tolerance <= 0.0 )
|
|
tolerance = ON_ZERO_TOLERANCE;
|
|
const ON_RevSurface* rs = ON_RevSurface::Cast(this);
|
|
if (rs)
|
|
{
|
|
return rs->IsSpherical(sphere,tolerance) ? true : false;
|
|
}
|
|
|
|
ON_Curve* crv = IsoCurve(0,Domain(1).Mid());
|
|
if ( !crv )
|
|
return false;
|
|
|
|
ON_Arc arc0;
|
|
int bIsArc0 = crv->IsArc(0,&arc0,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
delete crv;
|
|
crv = 0;
|
|
if ( !bIsArc0 )
|
|
return false;
|
|
|
|
crv = IsoCurve(1,Domain(0).Mid());
|
|
if ( !crv )
|
|
return false;
|
|
ON_Arc arc1;
|
|
int bIsArc1 = crv->IsArc(0,&arc1,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
delete crv;
|
|
crv = 0;
|
|
if ( !bIsArc1 )
|
|
return false;
|
|
|
|
// Determine if one of these arcs is a a portion of a
|
|
// great circle.
|
|
|
|
ON_Sphere sph0;
|
|
sph0.plane = arc0.plane;
|
|
sph0.radius = arc0.radius;
|
|
bool bTestSphere0 = sph0.IsValid();
|
|
|
|
ON_Sphere sph1;
|
|
sph1.plane = arc1.plane;
|
|
sph1.radius = arc1.radius;
|
|
bool bTestSphere1 = sph1.IsValid();
|
|
|
|
if ( !bTestSphere0 && !bTestSphere1 )
|
|
return false;
|
|
|
|
double sph0tol = 0.0;
|
|
double sph1tol = 0.0;
|
|
|
|
double tol = 0.5*ON_SQRT_EPSILON*(arc0.radius+arc1.radius);
|
|
|
|
ON_3dPoint P, S;
|
|
double a, d;
|
|
for ( a = 0.0; a < 1.0; a += 0.25 )
|
|
{
|
|
P = arc0.PointAt(a*2.0*ON_PI);
|
|
if ( bTestSphere0 )
|
|
{
|
|
S = sph0.ClosestPointTo(P);
|
|
d = S.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestSphere0 = false;
|
|
if ( !bTestSphere1 )
|
|
return false;
|
|
}
|
|
else if ( d > sph0tol )
|
|
sph0tol = d;
|
|
}
|
|
if ( bTestSphere1 )
|
|
{
|
|
S = sph1.ClosestPointTo(P);
|
|
d = S.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestSphere1 = false;
|
|
if ( !bTestSphere0 )
|
|
return false;
|
|
}
|
|
else if ( d > sph1tol )
|
|
sph1tol = d;
|
|
}
|
|
|
|
P = arc1.PointAt(a*2.0*ON_PI);
|
|
if ( bTestSphere0 )
|
|
{
|
|
S = sph0.ClosestPointTo(P);
|
|
d = S.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestSphere0 = false;
|
|
if ( !bTestSphere1 )
|
|
return false;
|
|
}
|
|
else if ( d > sph0tol )
|
|
sph0tol = d;
|
|
}
|
|
if ( bTestSphere1 )
|
|
{
|
|
S = sph1.ClosestPointTo(P);
|
|
d = S.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestSphere1 = false;
|
|
if ( !bTestSphere0 )
|
|
return false;
|
|
}
|
|
else if ( d > sph1tol )
|
|
sph1tol = d;
|
|
}
|
|
}
|
|
// If the arc's are both great circles, then
|
|
// both will be true unless we have a bug or
|
|
// numerical issues.
|
|
if (!bTestSphere0 && !bTestSphere1)
|
|
return false;
|
|
|
|
if ( tol < tolerance )
|
|
tol = tolerance;
|
|
|
|
double u, v;
|
|
int sc0 = SpanCount(0);
|
|
int sc1 = SpanCount(1);
|
|
double* s = (double*)onmalloc( (sc0+sc1+2)*sizeof(s[0]) );
|
|
double* t = s + (sc0+1);
|
|
GetSpanVector(0,s);
|
|
GetSpanVector(1,t);
|
|
for ( int i = 0; i < sc0; i++ )
|
|
{
|
|
for ( int ii = i?1:0; ii <= 4; ii++ )
|
|
{
|
|
u = 0.25*((4-ii)*s[i] + ii*s[i+1]);
|
|
for ( int j = 0; j <= sc1; j++ )
|
|
{
|
|
for ( int jj = j?1:0; jj <= 4; jj++ )
|
|
{
|
|
v = 0.25*((4-jj)*t[j] + jj*t[j+1]);
|
|
P = PointAt(u,v);
|
|
if ( bTestSphere0 )
|
|
{
|
|
S = sph0.ClosestPointTo(P);
|
|
d = S.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestSphere0 = false;
|
|
if ( !bTestSphere1 )
|
|
{
|
|
onfree(s);
|
|
return false;
|
|
}
|
|
}
|
|
else if ( d > sph0tol )
|
|
sph0tol = d;
|
|
}
|
|
if ( bTestSphere1 )
|
|
{
|
|
S = sph1.ClosestPointTo(P);
|
|
d = S.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestSphere1 = false;
|
|
if ( !bTestSphere0 )
|
|
{
|
|
onfree(s);
|
|
return false;
|
|
}
|
|
}
|
|
else if ( d > sph1tol )
|
|
sph1tol = d;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
onfree(s);
|
|
|
|
bool rc = (bTestSphere0 || bTestSphere1);
|
|
if ( rc && sphere )
|
|
{
|
|
if (!bTestSphere0)
|
|
*sphere = sph1;
|
|
else if (!bTestSphere1)
|
|
*sphere = sph0;
|
|
else if (sph0tol <= sph1tol)
|
|
*sphere = sph0;
|
|
else
|
|
*sphere = sph1;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Surface::IsCylinder( ON_Cylinder* cylinder, double tolerance ) const
|
|
{
|
|
if ( !ON_IsValid(tolerance) || tolerance <= 0.0 )
|
|
tolerance = ON_ZERO_TOLERANCE;
|
|
const ON_RevSurface* rs = ON_RevSurface::Cast(this);
|
|
bool rc = rs && rs->IsCylindrical(cylinder,tolerance);
|
|
|
|
if ( !rc && !rs )
|
|
{
|
|
ON_Curve* crv = IsoCurve(0,Domain(1).Mid());
|
|
if ( !crv )
|
|
return false;
|
|
|
|
ON_Arc arc;
|
|
ON_Line line;
|
|
int bIsLine = 0;
|
|
int bIsArc = crv->IsArc(0,&arc,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
if ( !bIsArc )
|
|
{
|
|
bIsLine = crv->IsLinear(tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
if ( bIsLine )
|
|
{
|
|
line.from = crv->PointAtStart();
|
|
line.to = crv->PointAtEnd();
|
|
}
|
|
}
|
|
delete crv;
|
|
crv = 0;
|
|
if ( !bIsArc && !bIsLine )
|
|
return false;
|
|
|
|
crv = IsoCurve(1,Domain(0).Mid());
|
|
if ( !crv )
|
|
return false;
|
|
if ( !bIsArc )
|
|
bIsArc = crv->IsArc(0,&arc,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
else if ( !bIsLine )
|
|
{
|
|
bIsLine = crv->IsLinear(tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
if ( bIsLine )
|
|
{
|
|
line.from = crv->PointAtStart();
|
|
line.to = crv->PointAtEnd();
|
|
}
|
|
}
|
|
delete crv;
|
|
crv = 0;
|
|
if ( !bIsArc || !bIsLine )
|
|
return false;
|
|
|
|
double tol = 0.5*ON_SQRT_EPSILON*(arc.radius);
|
|
if ( tol < tolerance )
|
|
tol = tolerance;
|
|
double r = arc.plane.origin.DistanceTo(arc.plane.ClosestPointTo(line.from));
|
|
if ( fabs(arc.radius - r) > tol )
|
|
return false;
|
|
r = arc.plane.origin.DistanceTo(arc.plane.ClosestPointTo(line.to));
|
|
if ( fabs(arc.radius - r) > tol )
|
|
return false;
|
|
|
|
ON_3dPoint P;
|
|
double u, v;
|
|
int sc0 = SpanCount(0);
|
|
int sc1 = SpanCount(1);
|
|
double* s = (double*)onmalloc( (sc0+sc1+2)*sizeof(s[0]) );
|
|
double* t = s + (sc0+1);
|
|
GetSpanVector(0,s);
|
|
GetSpanVector(1,t);
|
|
for ( int i = 0; i < sc0; i++ )
|
|
{
|
|
for ( int ii = i?1:0; ii <= 4; ii++ )
|
|
{
|
|
u = 0.25*((4-ii)*s[i] + ii*s[i+1]);
|
|
for ( int j = 0; j < sc1; j++ )
|
|
{
|
|
for ( int jj = j?1:0; jj <= 4; jj++ )
|
|
{
|
|
v = 0.25*((4-jj)*t[j] + jj*t[j+1]);
|
|
P = PointAt(u,v);
|
|
r = arc.plane.origin.DistanceTo(arc.plane.ClosestPointTo(P));
|
|
if ( fabs(arc.radius - r) > tol )
|
|
{
|
|
onfree(s);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
onfree(s);
|
|
|
|
|
|
rc = true;
|
|
if ( cylinder )
|
|
{
|
|
cylinder->Create(arc);
|
|
rc = cylinder->IsValid();
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_Surface::IsCone( ON_Cone* cone, double tolerance ) const
|
|
{
|
|
if ( !ON_IsValid(tolerance) || tolerance <= 0.0 )
|
|
tolerance = ON_ZERO_TOLERANCE;
|
|
|
|
const ON_RevSurface* rs = ON_RevSurface::Cast(this);
|
|
if (rs)
|
|
{
|
|
return rs->IsConical(cone,tolerance) ? true : false;
|
|
}
|
|
|
|
|
|
ON_Curve* crv = IsoCurve(0,Domain(1).Mid());
|
|
if ( !crv )
|
|
return false;
|
|
|
|
ON_Arc arc;
|
|
ON_Line line;
|
|
int bIsLine = 0;
|
|
int bIsArc = crv->IsArc(0,&arc,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
if ( !bIsArc )
|
|
{
|
|
bIsLine = crv->IsLinear(tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
if ( bIsLine )
|
|
{
|
|
line.from = crv->PointAtStart();
|
|
line.to = crv->PointAtEnd();
|
|
}
|
|
}
|
|
delete crv;
|
|
crv = 0;
|
|
if ( !bIsArc && !bIsLine )
|
|
return false;
|
|
|
|
crv = IsoCurve(1,Domain(0).Mid());
|
|
if ( !crv )
|
|
return false;
|
|
if ( !bIsArc )
|
|
bIsArc = crv->IsArc(0,&arc,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
else if ( !bIsLine )
|
|
{
|
|
bIsLine = crv->IsLinear(tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
if ( bIsLine )
|
|
{
|
|
line.from = crv->PointAtStart();
|
|
line.to = crv->PointAtEnd();
|
|
}
|
|
}
|
|
delete crv;
|
|
crv = 0;
|
|
if ( !bIsArc || !bIsLine )
|
|
return false;
|
|
|
|
double r0 = arc.plane.origin.DistanceTo(arc.plane.ClosestPointTo(line.from));
|
|
double r1 = arc.plane.origin.DistanceTo(arc.plane.ClosestPointTo(line.to));
|
|
if ( fabs(r0-r1) <= ON_ZERO_TOLERANCE )
|
|
return false;
|
|
double h0 = arc.plane.plane_equation.ValueAt(line.from);
|
|
double h1 = arc.plane.plane_equation.ValueAt(line.to);
|
|
if ( fabs(h0-h1) <= ON_ZERO_TOLERANCE )
|
|
return false;
|
|
double tol = 0.5*ON_SQRT_EPSILON*(r0+r1);
|
|
|
|
ON_Cone cn;
|
|
cn.height = (h0*r1 - r0*h1)/(r1-r0);
|
|
if ( !ON_IsValid(cn.height) || fabs(cn.height) <= ON_ZERO_TOLERANCE )
|
|
return false;
|
|
cn.plane = arc.plane;
|
|
cn.plane.origin = cn.plane.origin + cn.height*cn.plane.zaxis;
|
|
cn.plane.UpdateEquation();
|
|
if ( r0 >= r1 )
|
|
{
|
|
cn.radius = r0;
|
|
cn.height = h0-cn.height;
|
|
}
|
|
else
|
|
{
|
|
cn.radius = r1;
|
|
cn.height = h1-cn.height;
|
|
}
|
|
if ( !cn.IsValid() )
|
|
return false;
|
|
tol *= fabs(cn.height);
|
|
|
|
// if (r - h*cn.radius/cn.height > tolerance) return false;
|
|
double h = cn.plane.plane_equation.ValueAt(line.from);
|
|
if ( fabs(r0*cn.height - h*cn.radius) > tol )
|
|
return false;
|
|
h = cn.plane.plane_equation.ValueAt(line.to);
|
|
if ( fabs(r1*cn.height - h*cn.radius) > tol )
|
|
return false;
|
|
|
|
ON_3dPoint P;
|
|
double u, v, r;
|
|
int sc0 = SpanCount(0);
|
|
int sc1 = SpanCount(1);
|
|
double* s = (double*)onmalloc( (sc0+sc1+2)*sizeof(s[0]) );
|
|
double* t = s + (sc0+1);
|
|
GetSpanVector(0,s);
|
|
GetSpanVector(1,t);
|
|
tol = 0.5*ON_SQRT_EPSILON*(r0+r1);
|
|
if ( tol < tolerance )
|
|
tol = tolerance;
|
|
tol *= fabs(cn.height);
|
|
for ( int i = 0; i < sc0; i++ )
|
|
{
|
|
for ( int ii = i?1:0; ii <= 4; ii++ )
|
|
{
|
|
u = 0.25*((4-ii)*s[i] + ii*s[i+1]);
|
|
for ( int j = 0; j < sc1; j++ )
|
|
{
|
|
for ( int jj = j?1:0; jj <= 4; jj++ )
|
|
{
|
|
v = 0.25*((4-jj)*t[j] + jj*t[j+1]);
|
|
P = PointAt(u,v);
|
|
h = cn.plane.plane_equation.ValueAt(P);
|
|
r = cn.plane.origin.DistanceTo(cn.plane.ClosestPointTo(P));
|
|
// if (r - h*cn.radius/cn.height > tolerance) return false;
|
|
if ( fabs(r*cn.height - h*cn.radius) > tol )
|
|
{
|
|
onfree(s);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
onfree(s);
|
|
|
|
if ( cone )
|
|
{
|
|
*cone = cn;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ON_Surface::IsTorus( ON_Torus* torus, double tolerance ) const
|
|
{
|
|
if ( !ON_IsValid(tolerance) || tolerance <= 0.0 )
|
|
tolerance = ON_ZERO_TOLERANCE;
|
|
|
|
ON_Curve* crv = IsoCurve(0,Domain(1).Mid());
|
|
if ( !crv )
|
|
return false;
|
|
|
|
ON_Arc arc0;
|
|
int bIsArc0 = crv->IsArc(0,&arc0,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
delete crv;
|
|
crv = 0;
|
|
if ( !bIsArc0 )
|
|
return false;
|
|
|
|
crv = IsoCurve(1,Domain(0).Mid());
|
|
if ( !crv )
|
|
return false;
|
|
ON_Arc arc1;
|
|
int bIsArc1 = crv->IsArc(0,&arc1,tolerance > ON_ZERO_TOLERANCE ? tolerance : 0.0);
|
|
delete crv;
|
|
crv = 0;
|
|
if ( !bIsArc1 )
|
|
return false;
|
|
|
|
double tol = 0.5*ON_SQRT_EPSILON*(arc0.radius+arc1.radius);
|
|
|
|
ON_Torus tr0;
|
|
tr0.plane = arc0.plane;
|
|
double h = tr0.plane.plane_equation.ValueAt(arc1.plane.origin);
|
|
tr0.plane.origin = tr0.plane.origin + h*tr0.plane.zaxis;
|
|
tr0.plane.UpdateEquation();
|
|
tr0.major_radius = tr0.plane.origin.DistanceTo(arc1.plane.origin);
|
|
tr0.minor_radius = arc1.radius;
|
|
|
|
ON_Torus tr1;
|
|
tr1.plane = arc1.plane;
|
|
h = tr1.plane.plane_equation.ValueAt(arc0.plane.origin);
|
|
tr1.plane.origin = tr1.plane.origin + h*tr1.plane.zaxis;
|
|
tr1.plane.UpdateEquation();
|
|
tr1.major_radius = tr1.plane.origin.DistanceTo(arc0.plane.origin);
|
|
tr1.minor_radius = arc0.radius;
|
|
|
|
bool bTestTorus0 = tr0.IsValid()?true:false;
|
|
bool bTestTorus1 = tr1.IsValid()?true:false;
|
|
if ( !bTestTorus0 && !bTestTorus1 )
|
|
return false;
|
|
double tr0tol = 0.0;
|
|
double tr1tol = 0.0;
|
|
|
|
ON_3dPoint P, T;
|
|
double a, d;
|
|
for ( a = 0.0; a < 1.0; a += 0.25 )
|
|
{
|
|
P = arc0.PointAt(a*2.0*ON_PI);
|
|
if ( bTestTorus0 )
|
|
{
|
|
T = tr0.ClosestPointTo(P);
|
|
d = T.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestTorus0 = false;
|
|
if ( !bTestTorus1 )
|
|
return false;
|
|
}
|
|
else if ( d > tr0tol )
|
|
tr0tol = d;
|
|
}
|
|
if ( bTestTorus1 )
|
|
{
|
|
T = tr1.ClosestPointTo(P);
|
|
d = T.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestTorus1 = false;
|
|
if ( !bTestTorus0 )
|
|
return false;
|
|
}
|
|
else if ( d > tr1tol )
|
|
tr1tol = d;
|
|
}
|
|
|
|
P = arc1.PointAt(a*2.0*ON_PI);
|
|
if ( bTestTorus0 )
|
|
{
|
|
T = tr0.ClosestPointTo(P);
|
|
d = T.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestTorus0 = false;
|
|
if ( !bTestTorus1 )
|
|
return false;
|
|
}
|
|
else if ( d > tr0tol )
|
|
tr0tol = d;
|
|
}
|
|
if ( bTestTorus1 )
|
|
{
|
|
T = tr1.ClosestPointTo(P);
|
|
d = T.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestTorus1 = false;
|
|
if ( !bTestTorus0 )
|
|
return false;
|
|
}
|
|
else if ( d > tr1tol )
|
|
tr1tol = d;
|
|
}
|
|
}
|
|
// If the arc's planes are perpendicular, then
|
|
// both will be true unless we have a bug or
|
|
// numerical issues.
|
|
if (!bTestTorus0 && !bTestTorus1)
|
|
return false;
|
|
|
|
if ( tol < tolerance )
|
|
tol = tolerance;
|
|
|
|
double u, v;
|
|
int sc0 = SpanCount(0);
|
|
int sc1 = SpanCount(1);
|
|
double* s = (double*)onmalloc( (sc0+sc1+2)*sizeof(s[0]) );
|
|
double* t = s + (sc0+1);
|
|
GetSpanVector(0,s);
|
|
GetSpanVector(1,t);
|
|
for ( int i = 0; i < sc0; i++ )
|
|
{
|
|
for ( int ii = i?1:0; ii <= 4; ii++ )
|
|
{
|
|
u = 0.25*((4-ii)*s[i] + ii*s[i+1]);
|
|
for ( int j = 0; j < sc1; j++ )
|
|
{
|
|
for ( int jj = j?1:0; jj <= 4; jj++ )
|
|
{
|
|
v = 0.25*((4-jj)*t[j] + jj*t[j+1]);
|
|
P = PointAt(u,v);
|
|
if ( bTestTorus0 )
|
|
{
|
|
T = tr0.ClosestPointTo(P);
|
|
d = T.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestTorus0 = false;
|
|
if ( !bTestTorus1 )
|
|
{
|
|
onfree(s);
|
|
return false;
|
|
}
|
|
}
|
|
else if ( d > tr0tol )
|
|
tr0tol = d;
|
|
}
|
|
if ( bTestTorus1 )
|
|
{
|
|
T = tr1.ClosestPointTo(P);
|
|
d = T.DistanceTo(P);
|
|
if ( d > tol )
|
|
{
|
|
bTestTorus1 = false;
|
|
if ( !bTestTorus0 )
|
|
{
|
|
onfree(s);
|
|
return false;
|
|
}
|
|
}
|
|
else if ( d > tr1tol )
|
|
tr1tol = d;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
onfree(s);
|
|
|
|
bool rc = (bTestTorus0 || bTestTorus1);
|
|
if ( rc && torus )
|
|
{
|
|
if (!bTestTorus0)
|
|
*torus = tr1;
|
|
else if (!bTestTorus1)
|
|
*torus = tr0;
|
|
else if (tr0tol <= tr1tol)
|
|
*torus = tr0;
|
|
else
|
|
*torus = tr1;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static
|
|
bool ON__IsCylConeHelper(
|
|
const ON_Line& axis,
|
|
const ON_Curve* curve,
|
|
double tolerance,
|
|
ON_Plane& plane,
|
|
ON_Line& line,
|
|
double r[2],
|
|
double& h
|
|
)
|
|
{
|
|
if ( !axis.IsValid() )
|
|
return false;
|
|
if ( !curve )
|
|
return false;
|
|
line.from = curve->PointAtStart();
|
|
line.to = curve->PointAtEnd();
|
|
if ( !line.IsValid() )
|
|
return false;
|
|
if ( line.Length() <= ON_ZERO_TOLERANCE )
|
|
return false;
|
|
|
|
plane.zaxis = axis.Tangent();
|
|
h = plane.zaxis*line.Direction();
|
|
if ( !ON_IsValid(h) )
|
|
return false;
|
|
if ( h < 0.0 )
|
|
{
|
|
plane.zaxis = -plane.zaxis;
|
|
h = -h;
|
|
}
|
|
if ( h <= ON_ZERO_TOLERANCE )
|
|
return false;
|
|
|
|
double a0 = ON_UNSET_VALUE;
|
|
if ( !axis.ClosestPointTo(line.from,&a0) )
|
|
return false;
|
|
double a1 = ON_UNSET_VALUE;
|
|
if ( !axis.ClosestPointTo(line.to,&a1) )
|
|
return false;
|
|
if ( !ON_IsValid(a0) || !ON_IsValid(a1) )
|
|
return false;
|
|
|
|
|
|
ON_3dPoint A0 = axis.PointAt(a0);
|
|
ON_3dPoint A1 = axis.PointAt(a1);
|
|
plane.origin = A0;
|
|
ON_3dVector x0 = line.from - A0;
|
|
ON_3dVector x1 = line.to - A1;
|
|
r[0] = x0.Length();
|
|
r[1] = x1.Length();
|
|
if ( x0*x0 < 0.0 && r[0] > ON_ZERO_TOLERANCE && r[1] > ON_ZERO_TOLERANCE )
|
|
return false;
|
|
plane.xaxis = ( r[0] >= r[1] ) ? x0 : x1;
|
|
if (fabs(plane.xaxis.Length()) <= ON_ZERO_TOLERANCE)
|
|
return false;
|
|
if ( !plane.xaxis.Unitize() )
|
|
return false;
|
|
plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
|
|
if ( !plane.yaxis.Unitize() )
|
|
return false;
|
|
plane.UpdateEquation();
|
|
if ( !plane.IsValid() )
|
|
return false;
|
|
|
|
x0 = line.Tangent();
|
|
if ( fabs(x0*plane.yaxis) > ON_ZERO_TOLERANCE )
|
|
return false;
|
|
|
|
return (curve->IsLinear( tolerance )?true:false);
|
|
}
|
|
|
|
bool ON_RevSurface::IsCylindrical(
|
|
ON_Cylinder* cylinder,
|
|
double tolerance
|
|
) const
|
|
{
|
|
ON_Cylinder c;
|
|
ON_Line line;
|
|
double r[2] = {0.0,0.0};
|
|
double h = 0.0;
|
|
if ( !ON_IsValid(tolerance) || tolerance <= 0.0 )
|
|
tolerance = ON_ZERO_TOLERANCE;
|
|
if ( !ON__IsCylConeHelper(m_axis,m_curve,tolerance,c.circle.plane,line,r,h) )
|
|
return false;
|
|
if ( fabs(r[0]-r[1]) > tolerance )
|
|
return false;
|
|
if ( fabs(line.Tangent()*c.circle.plane.xaxis) > ON_ZERO_TOLERANCE )
|
|
return false;
|
|
c.circle.radius = (r[0]==r[1]) ? r[0] : (0.5*(r[0]+r[1]));
|
|
c.height[0] = 0.0;
|
|
c.height[1] = h;
|
|
if ( cylinder )
|
|
*cylinder = c;
|
|
return c.IsValid();
|
|
}
|
|
|
|
bool ON_RevSurface::IsConical(
|
|
ON_Cone* cone,
|
|
double tolerance
|
|
) const
|
|
{
|
|
ON_Cone c;
|
|
ON_Line line;
|
|
double r[2] = {0.0,0.0};
|
|
double h = 0.0;
|
|
if ( !ON_IsValid(tolerance) || tolerance <= 0.0 )
|
|
tolerance = ON_ZERO_TOLERANCE;
|
|
if ( !ON__IsCylConeHelper(m_axis,m_curve,tolerance,c.plane,line,r,h) )
|
|
return false;
|
|
double dr = r[0]-r[1];
|
|
if ( fabs(dr) <= ON_ZERO_TOLERANCE )
|
|
return false;
|
|
if ( 0.0 == r[0] )
|
|
{
|
|
c.radius = r[1];
|
|
c.height = h;
|
|
}
|
|
else if ( 0.0 == r[1] )
|
|
{
|
|
c.plane.origin += h*c.plane.zaxis;
|
|
c.plane.UpdateEquation();
|
|
c.radius = r[0];
|
|
c.height = -h;
|
|
}
|
|
else if ( dr > 0.0 )
|
|
{
|
|
h *= (r[0]/dr);
|
|
c.plane.origin += h*c.plane.zaxis;
|
|
c.plane.UpdateEquation();
|
|
c.radius = r[0];
|
|
c.height = -h;
|
|
}
|
|
else
|
|
{
|
|
double d = h*r[0]/dr;
|
|
c.plane.origin += d*c.plane.zaxis;
|
|
c.plane.UpdateEquation();
|
|
c.radius = r[1];
|
|
c.height = h-d;
|
|
}
|
|
|
|
if ( cone )
|
|
*cone = c;
|
|
return c.IsValid();
|
|
}
|
|
|