mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 03:26:09 +08:00
4956 lines
134 KiB
C++
4956 lines
134 KiB
C++
#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
|
|
|
|
static bool ON_ExtrusionPolyCurveProfileIsNotValid()
|
|
{
|
|
return false; // good place for a breakpoint
|
|
}
|
|
|
|
bool ON_Extrusion::IsValidPolyCurveProfile( const ON_PolyCurve& polycurve, ON_TextLog* text_log )
|
|
{
|
|
const bool bAllowGaps = true;
|
|
bool rc = polycurve.IsValid(bAllowGaps,text_log) ? true : ON_ExtrusionPolyCurveProfileIsNotValid();
|
|
if (!rc)
|
|
return ON_ExtrusionPolyCurveProfileIsNotValid();
|
|
|
|
const int profile_count = polycurve.Count();
|
|
|
|
if ( profile_count < 1 )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("polycurve has < 1 segments.\n");
|
|
}
|
|
return ON_ExtrusionPolyCurveProfileIsNotValid();
|
|
}
|
|
|
|
if ( 2 != polycurve.Dimension() )
|
|
{
|
|
if ( 3 != polycurve.Dimension() )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("polycurve dimension = %d (should be 2).\n",polycurve.Dimension());
|
|
}
|
|
return ON_ExtrusionPolyCurveProfileIsNotValid();
|
|
}
|
|
ON_BoundingBox bbox = polycurve.BoundingBox();
|
|
if ( !bbox.IsValid() )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("polycurve.BoundingBox() is not valid.\n");
|
|
}
|
|
return ON_ExtrusionPolyCurveProfileIsNotValid();
|
|
}
|
|
if ( !( 0.0 == bbox.m_min.z) || !(0.0 == bbox.m_max.z) )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("polycurve.BoundingBox() z values are not both 0.0.\n");
|
|
}
|
|
return ON_ExtrusionPolyCurveProfileIsNotValid();
|
|
}
|
|
}
|
|
|
|
if ( 1 == profile_count )
|
|
return true;
|
|
|
|
if ( profile_count > 1 )
|
|
{
|
|
for ( int i = 0; i < profile_count; i++ )
|
|
{
|
|
const ON_Curve* segment = polycurve.SegmentCurve(i);
|
|
if ( 0 == segment )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("polycurve.SegmentCurve(%d) is null.\n",i);
|
|
}
|
|
return ON_ExtrusionPolyCurveProfileIsNotValid();
|
|
}
|
|
if ( !segment->IsClosed() )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("polycurve.SegmentCurve(%d) is not closed.\n",i);
|
|
}
|
|
return ON_ExtrusionPolyCurveProfileIsNotValid();
|
|
}
|
|
if ( segment->Domain() != polycurve.SegmentDomain(i) )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("polycurve.Segment(%d).Domain() does not match polycurve.SegmentDomain(%d).\n",i,i);
|
|
}
|
|
return ON_ExtrusionPolyCurveProfileIsNotValid();
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_Extrusion::CleanupPolyCurveProfile( ON_PolyCurve& polycurve )
|
|
{
|
|
if ( !ON_Extrusion::IsValidPolyCurveProfile(polycurve) )
|
|
{
|
|
// see if we can fix it
|
|
int i;
|
|
const int old_count = polycurve.Count();
|
|
if ( old_count <= 1 )
|
|
return false;
|
|
|
|
// make segments 2d
|
|
for ( i = 0; i < old_count; i++ )
|
|
{
|
|
ON_Curve* old_segment = polycurve.SegmentCurve(i);
|
|
if ( 0 == old_segment )
|
|
return false;
|
|
if ( 2 != old_segment->Dimension() && !old_segment->ChangeDimension(2) )
|
|
return false;
|
|
}
|
|
|
|
// make segment domains match the polycurve's m_t[] array
|
|
polycurve.SynchronizeSegmentDomains();
|
|
|
|
// make each segment a closed curve
|
|
ON_SimpleArray<ON_PolyCurve*> new_polycurves(old_count);
|
|
ON_SimpleArray<ON_Curve*> new_segments(old_count);
|
|
ON_PolyCurve* new_segment = 0;
|
|
bool rc = true;
|
|
for ( i = 0; i < old_count && rc; i++ )
|
|
{
|
|
ON_Curve* old_segment = polycurve.SegmentCurve(i);
|
|
if ( old_segment->IsClosed() )
|
|
{
|
|
if ( 0 != new_segment )
|
|
{
|
|
rc = false;
|
|
break;
|
|
}
|
|
new_segments.Append(old_segment);
|
|
}
|
|
else if ( 0 == new_segment )
|
|
{
|
|
new_segment = new ON_PolyCurve();
|
|
new_polycurves.Append(new_segment);
|
|
new_segment->Append(old_segment);
|
|
}
|
|
else
|
|
{
|
|
new_segment->Append(old_segment);
|
|
if ( new_segment->FindNextGap(0) )
|
|
{
|
|
rc = false;
|
|
break;
|
|
}
|
|
if ( new_segment->IsClosed() )
|
|
{
|
|
new_segments.Append(new_segment);
|
|
new_segment = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( 0 != new_segment )
|
|
{
|
|
rc = false;
|
|
}
|
|
|
|
if ( !rc )
|
|
{
|
|
// unable to fix polycurve. Delete the new stuff we allocated.
|
|
for ( i = 0; i < new_polycurves.Count(); i++ )
|
|
{
|
|
new_segment = new_polycurves[i];
|
|
if ( new_segment )
|
|
{
|
|
for ( int j = new_segment->Count()-1; j >= 0; j-- )
|
|
{
|
|
new_segment->HarvestSegment(j); // do not delete parts of input polycurve
|
|
}
|
|
delete new_segment;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
for ( i = 0; i < new_polycurves.Count(); i++ )
|
|
{
|
|
new_polycurves[i]->RemoveNesting();
|
|
}
|
|
|
|
for ( i = old_count-1; i >= 0; i-- )
|
|
{
|
|
polycurve.HarvestSegment(i);
|
|
polycurve.Remove(i);
|
|
}
|
|
for ( i = 0; i < new_segments.Count(); i++ )
|
|
{
|
|
polycurve.Append(new_segments[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
polycurve.ChangeDimension(2);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ON_GetEndCapTransformation(ON_3dPoint P, ON_3dVector T, ON_3dVector U,
|
|
const ON_3dVector* Normal,
|
|
ON_Xform& xform,
|
|
ON_Xform* scale2d,
|
|
ON_Xform* rot3d
|
|
)
|
|
{
|
|
if ( scale2d )
|
|
*scale2d = ON_Xform::IdentityTransformation;
|
|
if ( rot3d )
|
|
*rot3d = ON_Xform::IdentityTransformation;
|
|
if ( !T.IsUnitVector() && !T.Unitize() )
|
|
return false;
|
|
if ( !U.IsUnitVector() && !U.Unitize() )
|
|
return false;
|
|
ON_3dVector N(0.0,0.0,0.0);
|
|
if ( Normal )
|
|
{
|
|
N = *Normal;
|
|
if ( !N.IsUnitVector() && !N.Unitize() )
|
|
N = ON_3dVector::ZeroVector;
|
|
}
|
|
|
|
ON_Plane p0;
|
|
p0.origin = P;
|
|
p0.zaxis = T;
|
|
p0.yaxis = U;
|
|
p0.xaxis = ON_CrossProduct(U,T);
|
|
if ( !p0.xaxis.IsUnitVector() )
|
|
p0.xaxis.Unitize();
|
|
p0.UpdateEquation();
|
|
xform.Rotation(ON_xy_plane,p0);
|
|
if ( rot3d )
|
|
*rot3d = xform;
|
|
if ( N.z > ON_Extrusion::m_Nz_min && N.IsUnitVector() )
|
|
{
|
|
//double cosa = T.x*N.x + T.y*N.y + T.z*N.z; // N is relative to T
|
|
double cosa = N.z; // N is relative to xy plane.
|
|
for(;;)
|
|
{
|
|
ON_3dVector A(-N.y,N.x,0.0); // N is relative to xy plane.
|
|
if ( !A.IsValid() )
|
|
break;
|
|
double sina = A.Length();
|
|
if ( !ON_IsValid(sina) )
|
|
break;
|
|
if ( !A.Unitize() )
|
|
break;
|
|
|
|
// S is a non-uniform scale that maps A to A and perpA to 1/cosa*perpA.
|
|
// The scale distorts the profile so that after it is rotated
|
|
// into the miter plane, the projection of the rotated profile
|
|
// onto the xy-plane matches the original profile.
|
|
ON_Xform S(ON_Xform::ZeroTransformation);
|
|
const double c = 1.0 - 1.0/cosa;
|
|
S.m_xform[0][0] = 1.0 - c*A.y*A.y;
|
|
S.m_xform[0][1] = c*A.x*A.y;
|
|
|
|
S.m_xform[1][0] = S.m_xform[0][1];
|
|
S.m_xform[1][1] = 1.0 - c*A.x*A.x;
|
|
|
|
S.m_xform[2][2] = 1.0;
|
|
|
|
S.m_xform[3][3] = 1.0;
|
|
if (scale2d)
|
|
*scale2d = S;
|
|
|
|
// R rotates the profile plane so its normal is equal to N
|
|
ON_Xform R;
|
|
R.Rotation(sina,cosa,A,ON_3dPoint::Origin);
|
|
if ( rot3d )
|
|
*rot3d = xform*R;
|
|
|
|
xform = xform*R*S;
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void ON_ExtrusionInitializeHelper(ON_Extrusion& extrusion)
|
|
{
|
|
extrusion.m_path.from = ON_3dPoint::Origin;
|
|
extrusion.m_path.to = ON_3dPoint::Origin;
|
|
extrusion.m_t.m_t[0] = 0.0;
|
|
extrusion.m_t.m_t[1] = 1.0;
|
|
extrusion.m_up = ON_3dVector::ZeroVector;
|
|
extrusion.m_profile_count = 0;
|
|
extrusion.m_profile = 0;
|
|
extrusion.m_bCap[0] = false;
|
|
extrusion.m_bCap[1] = false;
|
|
extrusion.m_bHaveN[0] = false;
|
|
extrusion.m_bHaveN[1] = false;
|
|
extrusion.m_N[0] = ON_3dVector::ZeroVector;
|
|
extrusion.m_N[1] = ON_3dVector::ZeroVector;
|
|
extrusion.m_path_domain.m_t[0] = 0.0;
|
|
extrusion.m_path_domain.m_t[1] = 1.0;
|
|
extrusion.m_bTransposed = false;
|
|
}
|
|
|
|
static void ON_ExtrusionCopyHelper(const ON_Extrusion& src,ON_Extrusion& dst)
|
|
{
|
|
if ( &src != &dst )
|
|
{
|
|
if ( dst.m_profile )
|
|
{
|
|
delete dst.m_profile;
|
|
dst.m_profile = 0;
|
|
}
|
|
dst.m_path = src.m_path;
|
|
dst.m_t = src.m_t;
|
|
dst.m_up = src.m_up;
|
|
dst.m_profile_count = src.m_profile_count;
|
|
dst.m_profile = src.m_profile
|
|
? src.m_profile->DuplicateCurve()
|
|
: 0;
|
|
dst.m_bCap[0] = src.m_bCap[0];
|
|
dst.m_bCap[1] = src.m_bCap[1];
|
|
dst.m_bHaveN[0] = src.m_bHaveN[0];
|
|
dst.m_bHaveN[1] = src.m_bHaveN[1];
|
|
dst.m_N[0] = src.m_N[0];
|
|
dst.m_N[1] = src.m_N[1];
|
|
dst.m_path_domain = src.m_path_domain;
|
|
dst.m_bTransposed = src.m_bTransposed;
|
|
dst.m_mesh_cache = src.m_mesh_cache;
|
|
}
|
|
}
|
|
|
|
bool ON_Extrusion::SetPath(ON_3dPoint A, ON_3dPoint B)
|
|
{
|
|
double distAB = 0.0;
|
|
bool rc = A.IsValid() && B.IsValid()
|
|
&& (distAB = A.DistanceTo(B)) > ON_ZERO_TOLERANCE;
|
|
if (rc)
|
|
{
|
|
m_path.from = A;
|
|
m_path.to = B;
|
|
m_t.Set(0.0,1.0);
|
|
m_path_domain.Set(0.0,distAB);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Extrusion::SetPathAndUp( ON_3dPoint A, ON_3dPoint B, ON_3dVector up )
|
|
{
|
|
double distAB = 0.0;
|
|
|
|
bool rc = up.IsValid()
|
|
&& up.Length() > ON_ZERO_TOLERANCE
|
|
&& A.IsValid()
|
|
&& B.IsValid()
|
|
&& (distAB = A.DistanceTo(B)) > ON_ZERO_TOLERANCE;
|
|
|
|
if (rc)
|
|
{
|
|
ON_3dVector D = A-B;
|
|
D.Unitize();
|
|
double d = up*D;
|
|
if ( !up.IsUnitVector() || fabs(d) > distAB*ON_SQRT_EPSILON*0.015625 )
|
|
{
|
|
// need to make up perpendicular to the line segment
|
|
// and unitize.
|
|
D.Unitize();
|
|
up = up - d*D;
|
|
up.Unitize();
|
|
// validate up
|
|
d = up*D;
|
|
rc = ( up.IsUnitVector() && fabs(d) <= ON_SQRT_EPSILON );
|
|
}
|
|
|
|
if (rc)
|
|
{
|
|
m_path.from = A;
|
|
m_path.to = B;
|
|
m_t.Set(0.0,1.0);
|
|
m_path_domain.Set(0.0,distAB);
|
|
m_up = up;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int ON_Extrusion::PathParameter() const
|
|
{
|
|
return m_bTransposed ? 0 : 1;
|
|
}
|
|
|
|
int ON_Extrusion::ProfileParameter() const
|
|
{
|
|
return m_bTransposed ? 1 : 0;
|
|
}
|
|
|
|
ON_3dPoint ON_Extrusion::PathStart() const
|
|
{
|
|
ON_3dPoint P(ON_3dPoint::UnsetPoint);
|
|
const double t = m_t.m_t[0];
|
|
if ( 0.0 <= t && t <= 1.0 && m_path.IsValid() )
|
|
P = m_path.PointAt(t);
|
|
return P;
|
|
}
|
|
|
|
ON_3dPoint ON_Extrusion::PathEnd() const
|
|
{
|
|
ON_3dPoint P(ON_3dPoint::UnsetPoint);
|
|
const double t = m_t.m_t[1];
|
|
if ( 0.0 <= t && t <= 1.0 && m_path.IsValid() )
|
|
P = m_path.PointAt(t);
|
|
return P;
|
|
}
|
|
|
|
ON_3dVector ON_Extrusion::PathTangent() const
|
|
{
|
|
ON_3dVector T(ON_3dVector::UnsetVector);
|
|
if ( m_path.IsValid() )
|
|
T = m_path.Tangent();
|
|
return T;
|
|
}
|
|
|
|
void ON_Extrusion::Destroy()
|
|
{
|
|
if ( m_profile)
|
|
{
|
|
delete m_profile;
|
|
m_profile = 0;
|
|
}
|
|
ON_ExtrusionInitializeHelper(*this);
|
|
DestroyRuntimeCache();
|
|
PurgeUserData();
|
|
}
|
|
|
|
bool ON_Extrusion::SetMiterPlaneNormal(ON_3dVector N, int end)
|
|
{
|
|
bool rc = false;
|
|
if ( end >= 0 && end <= 1 )
|
|
{
|
|
if ( N.IsValid()
|
|
&& N.z > ON_Extrusion::m_Nz_min
|
|
&& (N.IsUnitVector() || N.Unitize())
|
|
)
|
|
{
|
|
if (fabs(N.x) <= ON_SQRT_EPSILON && fabs(N.y) <= ON_SQRT_EPSILON)
|
|
N.Set(0.0,0.0,1.0);
|
|
m_N[end] = N;
|
|
m_bHaveN[end] = (N.z != 1.0);
|
|
rc = true;
|
|
}
|
|
else if ( N.IsZero() || ON_3dVector::UnsetVector == N )
|
|
{
|
|
m_bHaveN[end] = false;
|
|
rc = true;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void ON_Extrusion::GetMiterPlaneNormal(int end, ON_3dVector& N) const
|
|
{
|
|
if ( end >= 0 && end <= 1 && m_bHaveN[end] )
|
|
N = m_N[end];
|
|
else
|
|
N.Set(0.0,0.0,1.0);
|
|
}
|
|
|
|
int ON_Extrusion::IsMitered() const
|
|
{
|
|
int rc = 0;
|
|
if ( m_bHaveN[0] && m_N[0].IsUnitVector() && m_N[0].z > m_Nz_min && (m_N[0].x != 0.0 || m_N[0].y != 0.0) )
|
|
rc += 1;
|
|
if ( m_bHaveN[1] && m_N[1].IsUnitVector() && m_N[1].z > m_Nz_min && (m_N[1].x != 0.0 || m_N[1].y != 0.0) )
|
|
rc += 2;
|
|
return rc;
|
|
}
|
|
|
|
int ON_Extrusion::CapCount() const
|
|
{
|
|
// Returns number of end caps.
|
|
switch (IsCapped())
|
|
{
|
|
case 1:
|
|
case 2:
|
|
return 1;
|
|
case 3:
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ON_Extrusion::IsCapped() const
|
|
{
|
|
// 0 = no caps, 1 = bottom cap, 2 = top cap, 3 = both caps
|
|
|
|
if ( !m_bCap[0] && !m_bCap[1] )
|
|
return 0;
|
|
|
|
if ( m_profile_count < 1 || 0 == m_profile )
|
|
return 0;
|
|
|
|
if ( 1 == m_profile_count )
|
|
{
|
|
if ( !m_profile->IsClosed() )
|
|
return 0;
|
|
}
|
|
else if ( m_profile_count > 1 )
|
|
{
|
|
const ON_PolyCurve* p = ON_PolyCurve::Cast(m_profile);
|
|
if ( 0 == p )
|
|
return 0;
|
|
const ON_Curve* outer_profile = p->SegmentCurve(0);
|
|
if ( 0 == outer_profile )
|
|
return 0;
|
|
if ( !outer_profile->IsClosed() )
|
|
return 0;
|
|
}
|
|
|
|
return (m_bCap[0] ? (m_bCap[1] ? 3 : 1) : 2);
|
|
}
|
|
|
|
int ON_Extrusion::FaceCount() const
|
|
{
|
|
int face_count = 0;
|
|
const ON_Curve* profile0 = Profile(0);
|
|
|
|
if ( m_profile_count > 0 && 0 != profile0 )
|
|
{
|
|
int is_capped = IsCapped();
|
|
if ( is_capped != 0 && !profile0->IsClosed() )
|
|
{
|
|
is_capped = 0;
|
|
}
|
|
|
|
switch(is_capped)
|
|
{
|
|
case 1: // single bottom cap + sides
|
|
case 2: // single top cap + sides
|
|
face_count = m_profile_count + 1;
|
|
break;
|
|
|
|
case 3: // bottom and top cap + sides
|
|
face_count = m_profile_count + 2;
|
|
break;
|
|
|
|
default: // no caps
|
|
face_count = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return face_count;
|
|
}
|
|
|
|
|
|
bool ON_Extrusion::IsSolid() const
|
|
{
|
|
if ( !m_bCap[0] || !m_bCap[1] )
|
|
return false;
|
|
return 3 == IsCapped();
|
|
}
|
|
|
|
bool ON_Extrusion::GetPathPlane( double s, ON_Plane& plane ) const
|
|
{
|
|
ON_Plane p;
|
|
p.origin = ON_3dPoint::Origin;
|
|
p.zaxis = PathTangent();
|
|
p.yaxis = m_up;
|
|
p.xaxis = ON_CrossProduct(p.yaxis,p.zaxis);
|
|
if ( !p.xaxis.Unitize() )
|
|
return false;
|
|
if ( !p.yaxis.Unitize() )
|
|
return false;
|
|
p.UpdateEquation();
|
|
if ( !p.IsValid() )
|
|
{
|
|
p.yaxis = ON_CrossProduct(p.zaxis,p.xaxis);
|
|
p.yaxis.Unitize();
|
|
if ( !p.IsValid() )
|
|
return false;
|
|
}
|
|
p.origin = m_path.PointAt(m_t.ParameterAt(s));
|
|
p.UpdateEquation();
|
|
plane = p;
|
|
return plane.IsValid();
|
|
}
|
|
|
|
bool ON_Extrusion::GetProfilePlane( double s, ON_Plane& plane ) const
|
|
{
|
|
ON_Plane p;
|
|
p.origin = ON_3dPoint::Origin;
|
|
p.zaxis = PathTangent();
|
|
p.yaxis = m_up;
|
|
p.xaxis = ON_CrossProduct(p.yaxis,p.zaxis);
|
|
if ( !p.xaxis.Unitize() )
|
|
return false;
|
|
if ( !p.yaxis.Unitize() )
|
|
return false;
|
|
p.UpdateEquation();
|
|
if ( !p.IsValid() )
|
|
{
|
|
p.yaxis = ON_CrossProduct(p.zaxis,p.xaxis);
|
|
p.yaxis.Unitize();
|
|
if ( !p.IsValid() )
|
|
return false;
|
|
}
|
|
if ( (!m_bHaveN[0] || (0.0 == m_N[0].x && 0.0 == m_N[0].y))
|
|
&& (!m_bHaveN[1] || (0.0 == m_N[1].x && 0.0 == m_N[1].y))
|
|
)
|
|
{
|
|
p.origin = m_path.PointAt(m_t.ParameterAt(s));
|
|
p.UpdateEquation();
|
|
plane = p;
|
|
}
|
|
else
|
|
{
|
|
ON_Xform xform;
|
|
if ( !GetProfileTransformation(s,xform) )
|
|
return false;
|
|
if (!p.Transform(xform))
|
|
return false;
|
|
plane = p;
|
|
}
|
|
return plane.IsValid();
|
|
}
|
|
|
|
|
|
bool ON_Extrusion::GetProfileTransformation( double s, ON_Xform& xform ) const
|
|
{
|
|
//const ON_3dVector* N = (end?(m_bHaveN[1]?&m_N[1]:0):(m_bHaveN[0]?&m_N[0]:0));
|
|
const ON_3dVector T = m_path.Tangent();
|
|
if ( 0.0 == s )
|
|
{
|
|
return ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform,0,0);
|
|
}
|
|
if ( 1.0 == s )
|
|
{
|
|
return ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform,0,0);
|
|
}
|
|
ON_Xform xform0, xform1;
|
|
if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,0,0) )
|
|
return false;
|
|
if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,0,0) )
|
|
return false;
|
|
|
|
const double s0 = 1.0-s;
|
|
xform.m_xform[0][0] = s0*xform0.m_xform[0][0] + s*xform1.m_xform[0][0];
|
|
xform.m_xform[0][1] = s0*xform0.m_xform[0][1] + s*xform1.m_xform[0][1];
|
|
xform.m_xform[0][2] = s0*xform0.m_xform[0][2] + s*xform1.m_xform[0][2];
|
|
xform.m_xform[0][3] = s0*xform0.m_xform[0][3] + s*xform1.m_xform[0][3];
|
|
xform.m_xform[1][0] = s0*xform0.m_xform[1][0] + s*xform1.m_xform[1][0];
|
|
xform.m_xform[1][1] = s0*xform0.m_xform[1][1] + s*xform1.m_xform[1][1];
|
|
xform.m_xform[1][2] = s0*xform0.m_xform[1][2] + s*xform1.m_xform[1][2];
|
|
xform.m_xform[1][3] = s0*xform0.m_xform[1][3] + s*xform1.m_xform[1][3];
|
|
xform.m_xform[2][0] = s0*xform0.m_xform[2][0] + s*xform1.m_xform[2][0];
|
|
xform.m_xform[2][1] = s0*xform0.m_xform[2][1] + s*xform1.m_xform[2][1];
|
|
xform.m_xform[2][2] = s0*xform0.m_xform[2][2] + s*xform1.m_xform[2][2];
|
|
xform.m_xform[2][3] = s0*xform0.m_xform[2][3] + s*xform1.m_xform[2][3];
|
|
xform.m_xform[3][0] = s0*xform0.m_xform[3][0] + s*xform1.m_xform[3][0];
|
|
xform.m_xform[3][1] = s0*xform0.m_xform[3][1] + s*xform1.m_xform[3][1];
|
|
xform.m_xform[3][2] = s0*xform0.m_xform[3][2] + s*xform1.m_xform[3][2];
|
|
xform.m_xform[3][3] = s0*xform0.m_xform[3][3] + s*xform1.m_xform[3][3];
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool CleanProfileSegment( ON_Curve* curve )
|
|
{
|
|
ON_NurbsCurve* nurbs_curve = ON_NurbsCurve::Cast(curve);
|
|
if ( nurbs_curve )
|
|
{
|
|
nurbs_curve->RemoveSingularSpans();
|
|
return ( nurbs_curve->IsValid() && false == nurbs_curve->SpanIsSingular(0) );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool ProfileHelper( int desired_orientation, ON_Curve* profile )
|
|
{
|
|
// desired_orientation 0: outer profile that can be open or closed.
|
|
// desired_orientation 1: outer profile that must be closed
|
|
// desired_orientation -1: inner profile
|
|
|
|
if ( 0 == profile )
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - null input curve pointer.");
|
|
return false;
|
|
}
|
|
ON_BoundingBox bbox = profile->BoundingBox();
|
|
if ( !bbox.IsValid() )
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - profile->BoundingBox() failed.");
|
|
return false;
|
|
}
|
|
if ( fabs(bbox.m_min.z) > ON_ZERO_TOLERANCE || fabs(bbox.m_max.z) > ON_ZERO_TOLERANCE )
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - profile->BoundingBox() is not in the world xy plane.");
|
|
return false;
|
|
}
|
|
if ( !profile->ChangeDimension(2) )
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - profile->ChangeDimension(2) failed.");
|
|
return false;
|
|
}
|
|
|
|
if ( profile->IsClosed() )
|
|
{
|
|
int profile_orientation = ON_ClosedCurveOrientation(*profile,0);
|
|
switch(desired_orientation)
|
|
{
|
|
case 1:
|
|
case 0:
|
|
if ( -1 == profile_orientation )
|
|
{
|
|
if ( !profile->Reverse() )
|
|
{
|
|
ON_ERROR("ON_Extrusion::SetOuterProfile() - profile->Reverse() failed.");
|
|
return false;
|
|
}
|
|
profile_orientation = 1;
|
|
}
|
|
if ( 1 == desired_orientation && 1 != profile_orientation )
|
|
{
|
|
ON_ERROR("ON_Extrusion::SetOuterProfile() - profile has wrong orientation.");
|
|
return false;
|
|
}
|
|
break;
|
|
case -1:
|
|
if ( 1 == profile_orientation )
|
|
{
|
|
if ( !profile->Reverse() )
|
|
{
|
|
ON_ERROR("ON_Extrusion::AddInnerProfile() - profile->Reverse() failed.");
|
|
return false;
|
|
}
|
|
profile_orientation = -1;
|
|
}
|
|
if ( -1 != profile_orientation )
|
|
{
|
|
ON_ERROR("ON_Extrusion::AddInnerProfile() - profile has wrong orientation.");
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - invalid desired_orientation parameter.");
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if ( 0 != desired_orientation )
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - profile is an open curve.");
|
|
return false;
|
|
}
|
|
|
|
ON_PolyCurve* polycurve = ON_PolyCurve::Cast(profile);
|
|
if ( 0 != polycurve )
|
|
{
|
|
polycurve->RemoveNesting();
|
|
|
|
if ( polycurve->SegmentCurves().Count() < 1 )
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - ON_PolyCurve has no segments.");
|
|
return false;
|
|
}
|
|
|
|
if ( polycurve->SegmentCurves().Count() + 1 != polycurve->SegmentParameters().Count() )
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - ON_PolyCurve segment and parameter counts do not agree.");
|
|
return false;
|
|
}
|
|
|
|
for ( int i = polycurve->Count()-1; i >= 0; i-- )
|
|
{
|
|
ON_Curve* segment = polycurve->SegmentCurve(i);
|
|
if ( !CleanProfileSegment(segment) )
|
|
polycurve->Remove(i);
|
|
}
|
|
|
|
for ( int i = 0; i < polycurve->Count(); i++ )
|
|
{
|
|
ON_Curve* segment = polycurve->SegmentCurve(i);
|
|
if ( 0 == segment )
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - ON_PolyCurve has null segment.");
|
|
return false;
|
|
}
|
|
const ON_Interval segment_domain = polycurve->SegmentDomain(i);
|
|
if ( !segment_domain.IsIncreasing() )
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - segment has invalid domain.");
|
|
return false;
|
|
}
|
|
if ( !segment->SetDomain( segment_domain ) )
|
|
{
|
|
ON_ERROR("ON_Extrusion::Set/Add Profile - segment->SetDomain() failed.");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if ( !CleanProfileSegment(profile) )
|
|
{
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ON_Extrusion::SetOuterProfile( ON_Curve* outer_profile, bool bCap )
|
|
{
|
|
if ( 0 != m_profile )
|
|
{
|
|
ON_ERROR("ON_Extrusion::SetOuterProfile() called when m_profile is already not null.");
|
|
return false;
|
|
}
|
|
|
|
if ( !ProfileHelper( 0, outer_profile ) )
|
|
return false;
|
|
|
|
m_profile_count = 1;
|
|
m_profile = outer_profile;
|
|
|
|
if ( outer_profile->IsClosed() )
|
|
{
|
|
m_bCap[0] = m_bCap[1] = (bCap ? true : false);
|
|
}
|
|
else
|
|
{
|
|
m_bCap[0] = m_bCap[1] = false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ON_Extrusion::AddInnerProfile( ON_Curve* inner_profile )
|
|
{
|
|
if ( m_profile_count < 1 )
|
|
{
|
|
ON_ERROR("ON_Extrusion::AddInnerProfile() called when m_profile_count < 1.");
|
|
return false;
|
|
}
|
|
if ( 0 == m_profile )
|
|
{
|
|
ON_ERROR("ON_Extrusion::AddInnerProfile() called when m_profile is null.");
|
|
return false;
|
|
}
|
|
|
|
if ( 1 == m_profile_count && !m_profile->IsClosed() )
|
|
{
|
|
ON_ERROR("ON_Extrusion::AddInnerProfile() called when outer profile is not closed.");
|
|
return false;
|
|
}
|
|
|
|
ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile);
|
|
if ( m_profile_count > 1 && 0 == polycurve )
|
|
{
|
|
ON_ERROR("ON_Extrusion::AddInnerProfile() called when m_profile_count > 1 but m_profile is not an ON_PolyCurve.");
|
|
return false;
|
|
}
|
|
if ( m_profile_count > 1 && m_profile_count != polycurve->Count() )
|
|
{
|
|
ON_ERROR("ON_Extrusion::AddInnerProfile() called when m_profile_count > 1 but m_profile_count != m_profile->Count().");
|
|
return false;
|
|
}
|
|
|
|
if ( !ProfileHelper( -1, inner_profile ) )
|
|
return false;
|
|
|
|
if ( 1 == m_profile_count )
|
|
{
|
|
if ( 0 != polycurve )
|
|
{
|
|
polycurve->RemoveNesting();
|
|
}
|
|
|
|
if ( 0 == polycurve || 1 != polycurve->Count() )
|
|
{
|
|
polycurve = new ON_PolyCurve();
|
|
polycurve->Append(m_profile);
|
|
m_profile = polycurve;
|
|
}
|
|
}
|
|
|
|
polycurve->Append(inner_profile);
|
|
if ( polycurve->SegmentDomain(m_profile_count) != inner_profile->Domain() )
|
|
{
|
|
inner_profile->SetDomain( polycurve->SegmentDomain(m_profile_count) );
|
|
|
|
// If inner_profile is itself a polycurve, clean up segment domains
|
|
// so we don't get bugs caused by fuzz as we adjust parameters when evaluating.
|
|
polycurve = ON_PolyCurve::Cast(inner_profile);
|
|
if ( 0 != polycurve )
|
|
{
|
|
polycurve->SynchronizeSegmentDomains();
|
|
}
|
|
}
|
|
m_profile_count++;
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
const ON_PolyCurve* ON_Extrusion::PolyProfile() const
|
|
{
|
|
if ( m_profile_count <= 1 )
|
|
return 0;
|
|
const ON_PolyCurve* poly_profile = ON_PolyCurve::Cast(m_profile);
|
|
return (0 != poly_profile && m_profile_count == poly_profile->Count() ) ? poly_profile : 0;
|
|
}
|
|
|
|
const ON_Curve* ON_Extrusion::Profile(int profile_index) const
|
|
{
|
|
if ( 0 == profile_index && 1 == m_profile_count )
|
|
return m_profile;
|
|
if ( profile_index < 0 || profile_index > m_profile_count )
|
|
return 0;
|
|
const ON_PolyCurve* poly_profile = PolyProfile();
|
|
return ( 0 != poly_profile ? poly_profile->SegmentCurve(profile_index) : 0 );
|
|
}
|
|
|
|
ON_Curve* ON_Extrusion::Profile3d( ON_COMPONENT_INDEX ci ) const
|
|
{
|
|
double s = ON_UNSET_VALUE;
|
|
if ( ON_COMPONENT_INDEX::extrusion_bottom_profile == ci.m_type )
|
|
s = 0.0;
|
|
else if ( ON_COMPONENT_INDEX::extrusion_top_profile == ci.m_type )
|
|
s = 1.0;
|
|
else
|
|
return 0;
|
|
return Profile3d(ci.m_index,s);
|
|
}
|
|
|
|
ON_LineCurve* ON_Extrusion::PathLineCurve(ON_LineCurve* line_curve) const
|
|
{
|
|
if ( !m_path.IsValid() )
|
|
return 0;
|
|
|
|
ON_Interval path_domain = Domain(PathParameter());
|
|
if ( !path_domain.IsIncreasing() )
|
|
return 0;
|
|
|
|
if ( 0 == line_curve )
|
|
line_curve = new ON_LineCurve();
|
|
line_curve->m_line = m_path;
|
|
line_curve->SetDomain( path_domain[0], path_domain[1] );
|
|
|
|
return line_curve;
|
|
}
|
|
|
|
|
|
ON_Curve* ON_Extrusion::WallEdge( ON_COMPONENT_INDEX ci ) const
|
|
{
|
|
if ( ON_COMPONENT_INDEX::extrusion_wall_edge != ci.m_type )
|
|
return 0;
|
|
if ( ci.m_index < 0 )
|
|
return 0;
|
|
|
|
int profile_index = ci.m_index/2;
|
|
int profile_end = ci.m_index % 2;
|
|
const ON_Curve* profile2d = Profile(profile_index);
|
|
if ( 0 == profile2d )
|
|
return 0;
|
|
|
|
ON_3dPoint profileP = profile_end
|
|
? profile2d->PointAtEnd()
|
|
: profile2d->PointAtStart();
|
|
if ( !profileP.IsValid() )
|
|
return 0;
|
|
profileP.z = 0.0;
|
|
|
|
ON_Xform xform0, xform1;
|
|
if ( !GetProfileTransformation(0.0,xform0) )
|
|
return 0;
|
|
if ( !GetProfileTransformation(1.0,xform1) )
|
|
return 0;
|
|
|
|
ON_Line line;
|
|
line.from = xform0*profileP;
|
|
line.to = xform1*profileP;
|
|
if ( !line.IsValid() )
|
|
return 0;
|
|
|
|
ON_LineCurve* line_curve = new ON_LineCurve();
|
|
line_curve->m_line = line;
|
|
|
|
ON_Interval path_domain = Domain(PathParameter());
|
|
line_curve->SetDomain( path_domain[0], path_domain[1] );
|
|
|
|
return line_curve;
|
|
}
|
|
|
|
|
|
ON_Surface* ON_Extrusion::WallSurface( ON_COMPONENT_INDEX ci ) const
|
|
{
|
|
if ( ON_COMPONENT_INDEX::extrusion_wall_surface != ci.m_type )
|
|
return 0;
|
|
|
|
const ON_Curve* profile2d = Profile(ci.m_index);
|
|
if ( 0 == profile2d )
|
|
return 0;
|
|
|
|
ON_Interval wall_profile2d_domain = profile2d->Domain(); // 28 July, 2015 - Lowell - RH-29948
|
|
if ( m_profile_count > 1 )
|
|
{
|
|
const ON_PolyCurve* polyprofile2d = PolyProfile();
|
|
if ( 0 == polyprofile2d )
|
|
return 0;
|
|
if ( polyprofile2d->Count() != m_profile_count )
|
|
return 0;
|
|
wall_profile2d_domain = polyprofile2d->SegmentDomain(ci.m_index);
|
|
}
|
|
|
|
ON_Curve* wall_profile2d = profile2d->DuplicateCurve();
|
|
if ( 0 == wall_profile2d )
|
|
return 0;
|
|
wall_profile2d->SetDomain(wall_profile2d_domain);
|
|
wall_profile2d->ChangeDimension(2);
|
|
|
|
ON_Extrusion* wall = new ON_Extrusion();
|
|
wall->SetOuterProfile(wall_profile2d,false);
|
|
|
|
wall->m_path = m_path;
|
|
wall->m_t = m_t;
|
|
wall->m_path_domain = m_path_domain; // 28 July, 2015 - Lowell - RH-29948
|
|
wall->m_up = m_up;
|
|
wall->m_bHaveN[0] = m_bHaveN[0];
|
|
wall->m_bHaveN[1] = m_bHaveN[1];
|
|
wall->m_N[0] = m_N[0];
|
|
wall->m_N[1] = m_N[1];
|
|
wall->m_bTransposed = m_bTransposed;
|
|
|
|
return wall;
|
|
}
|
|
|
|
ON_Curve* ON_Extrusion::Profile3d( int profile_index, double s ) const
|
|
{
|
|
if ( profile_index < 0 || !(0.0 <= s && s <= 1.0) || 0 == m_profile )
|
|
return 0;
|
|
|
|
ON_Xform xform;
|
|
if ( !GetProfileTransformation(s,xform) )
|
|
return 0;
|
|
|
|
const ON_Curve* profile2d = Profile(profile_index);
|
|
if ( 0 == profile2d )
|
|
return 0;
|
|
|
|
ON_Curve* profile3d = profile2d->DuplicateCurve();
|
|
if ( 0 == profile3d )
|
|
return 0;
|
|
|
|
if ( !profile3d->ChangeDimension(3)
|
|
|| !profile3d->Transform(xform)
|
|
)
|
|
{
|
|
delete profile3d;
|
|
return 0;
|
|
}
|
|
|
|
return profile3d;
|
|
}
|
|
|
|
int ON_Extrusion::ProfileIndex( double profile_parameter ) const
|
|
{
|
|
if ( !m_profile )
|
|
return -1;
|
|
|
|
if ( m_profile_count < 1 )
|
|
return -1;
|
|
|
|
if ( 1 == m_profile_count )
|
|
{
|
|
return m_profile->Domain().Includes(profile_parameter,false) ? 0 : -1;
|
|
}
|
|
|
|
const ON_PolyCurve* polycurve = PolyProfile();
|
|
if ( 0 == polycurve )
|
|
return -1;
|
|
|
|
const ON_SimpleArray<double>& polycurve_t = polycurve->SegmentParameters();
|
|
if ( polycurve_t.Count() != m_profile_count+1 )
|
|
return -1;
|
|
|
|
int profile_index = ON_SearchMonotoneArray( polycurve_t.Array(), polycurve_t.Count(), profile_parameter );
|
|
if ( profile_index == m_profile_count )
|
|
profile_index = m_profile_count-1;
|
|
else if ( profile_index < 0 || profile_index > m_profile_count )
|
|
profile_index = -1;
|
|
return profile_index;
|
|
}
|
|
|
|
int ON_Extrusion::ProfileCount() const
|
|
{
|
|
if ( !m_profile )
|
|
return 0;
|
|
|
|
if ( m_profile_count < 1 )
|
|
return 0;
|
|
|
|
if ( m_profile_count > 1 )
|
|
{
|
|
const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile);
|
|
if ( 0 == polycurve )
|
|
return 0;
|
|
if ( polycurve->Count() != m_profile_count )
|
|
return 0;
|
|
}
|
|
|
|
return m_profile_count;
|
|
}
|
|
|
|
|
|
int ON_Extrusion::GetProfileCurves( ON_SimpleArray< const ON_Curve* >& profile_curves ) const
|
|
{
|
|
if ( !m_profile )
|
|
return 0;
|
|
|
|
if ( m_profile_count < 1 )
|
|
return 0;
|
|
|
|
if ( 1 == m_profile_count )
|
|
{
|
|
profile_curves.Reserve(profile_curves.Count()+1);
|
|
profile_curves.Append(m_profile);
|
|
}
|
|
else
|
|
{
|
|
const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile);
|
|
if ( 0 == polycurve )
|
|
return 0;
|
|
if ( polycurve->Count() != m_profile_count )
|
|
return 0;
|
|
const int count0 = profile_curves.Count();
|
|
profile_curves.Reserve(count0+m_profile_count);
|
|
for ( int i = 0; i < m_profile_count; i++ )
|
|
{
|
|
const ON_Curve* segment = polycurve->SegmentCurve(i);
|
|
if ( 0 == segment )
|
|
{
|
|
profile_curves.SetCount(count0);
|
|
return 0;
|
|
}
|
|
profile_curves.Append(segment);
|
|
}
|
|
}
|
|
|
|
return m_profile_count;
|
|
}
|
|
|
|
|
|
const double ON_Extrusion::m_Nz_min = 1.0/64.0;
|
|
|
|
const double ON_Extrusion::m_path_length_min = ON_ZERO_TOLERANCE;
|
|
|
|
ON_OBJECT_IMPLEMENT(ON_Extrusion,ON_Surface,"36F53175-72B8-4d47-BF1F-B4E6FC24F4B9");
|
|
|
|
ON_Extrusion::ON_Extrusion()
|
|
{
|
|
ON_ExtrusionInitializeHelper(*this);
|
|
}
|
|
|
|
ON_Extrusion::ON_Extrusion(const ON_Extrusion& src) : ON_Surface(src), m_profile(0)
|
|
{
|
|
ON_ExtrusionCopyHelper(src,*this);
|
|
}
|
|
|
|
ON_Extrusion::~ON_Extrusion()
|
|
{
|
|
if ( m_profile)
|
|
{
|
|
delete m_profile;
|
|
}
|
|
}
|
|
|
|
ON_Extrusion& ON_Extrusion::operator=(const ON_Extrusion& src)
|
|
{
|
|
if ( this != &src )
|
|
{
|
|
Destroy();
|
|
ON_Surface::operator=(src);
|
|
ON_ExtrusionCopyHelper(src,*this);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
static
|
|
void ON_Extrusion_IsNotValidMessage( ON_TextLog* text_log, const char* msg )
|
|
{
|
|
// this is a good place for a breakpoint
|
|
if ( text_log && msg && msg[0] )
|
|
text_log->Print("%s\n",msg);
|
|
}
|
|
|
|
static bool ON_ExtrusionIsNotValid()
|
|
{
|
|
return ON_IsNotValid(); // good place for a breakpoint
|
|
}
|
|
|
|
void ON_Extrusion::DestroyRuntimeCache( bool bDelete )
|
|
{
|
|
if ( 0 != m_profile )
|
|
{
|
|
// profile curve clean up
|
|
m_profile->DestroyRuntimeCache(bDelete);
|
|
}
|
|
|
|
// surface clean up
|
|
ON_Surface::DestroyRuntimeCache(bDelete);
|
|
}
|
|
|
|
bool ON_Extrusion::IsValid( ON_TextLog* text_log ) const
|
|
{
|
|
// check m_profile
|
|
if ( m_profile_count < 1 )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count < 1.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
if ( !m_profile )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_profile is nullptr.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
if ( m_profile_count > 1 )
|
|
{
|
|
const ON_PolyCurve* c = ON_PolyCurve::Cast(m_profile);
|
|
if ( 0 == c )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but m_profile is not an ON_PolyCurve.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
if ( m_profile_count != c->Count() )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but m_profile_count != m_profile->SegmentCount().");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
|
|
if ( !ON_Extrusion::IsValidPolyCurveProfile(*c,text_log) )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_profile is not a valid ON_PolyCurve.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
for ( int i = 0; i < m_profile_count; i++ )
|
|
{
|
|
const ON_Curve* segment = c->SegmentCurve(i);
|
|
if ( 0 == segment )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but a m_profile_count->SegmentCurve() is null.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
if ( !segment->IsClosed() )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but a m_profile_count->SegmentCurve() is not closed.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
}
|
|
}
|
|
else if ( !m_profile->IsValid(text_log) )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_profile is not valid.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
|
|
// check m_path
|
|
if ( !m_path.IsValid() )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_path is not valid.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
ON_3dVector D = m_path.to - m_path.from;
|
|
double len = D.Length();
|
|
if ( !ON_IsValid(len) || len <= 0.0 )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_path has zero length.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
if ( !ON_IsValid(len) || len <= ON_Extrusion::m_path_length_min )
|
|
{
|
|
if ( text_log )
|
|
text_log->Print("m_path has zero length <= ON_Extrusion::m_path_length_min.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
if ( !D.Unitize() || !D.IsUnitVector() )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_path has zero direction.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
|
|
// check m_t
|
|
if ( !(0.0 <= m_t.m_t[0] && m_t.m_t[0] < m_t.m_t[1] && m_t.m_t[1] <= 1.0) )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_t does not satisfy 0<=m_t[0]<m_t[1]<=1");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
|
|
// check m_up
|
|
if ( !m_up.IsUnitVector() )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_up is not a unit vector.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
len = m_up*D;
|
|
if ( fabs(len) > ON_SQRT_EPSILON )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_up is not perpendicular to m_path.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
|
|
// validate ends
|
|
if ( m_bHaveN[0] )
|
|
{
|
|
if ( !m_N[0].IsUnitVector() )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_N[0] is not a unit vector.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
if ( !(m_N[0].z > ON_Extrusion::m_Nz_min) )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_N[0].z is too small (<=ON_Extrusion::m_Nz_min) or negative");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
}
|
|
if ( m_bHaveN[1] )
|
|
{
|
|
if ( !m_N[1].IsUnitVector() )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_N[1] is not a unit vector.");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
if ( !(m_N[1].z > ON_Extrusion::m_Nz_min) )
|
|
{
|
|
ON_Extrusion_IsNotValidMessage(text_log,"m_N[1].z is too small (<=ON_Extrusion::m_Nz_min) or negative");
|
|
return ON_ExtrusionIsNotValid();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ON_Extrusion::Dump( ON_TextLog& text_log ) const
|
|
{
|
|
text_log.Print("ON_Extrusion: \n");
|
|
{
|
|
text_log.PushIndent();
|
|
text_log.Print("Path: ");
|
|
text_log.Print(m_path.PointAt(m_t[0]));
|
|
text_log.Print(" ");
|
|
text_log.Print(m_path.PointAt(m_t[1]));
|
|
text_log.Print("\n");
|
|
text_log.Print("Up: ");
|
|
text_log.Print(m_up);
|
|
text_log.Print("\n");
|
|
text_log.Print("m_bCap[] = (%d, %d)\n",m_bCap[0],m_bCap[1]);
|
|
text_log.Print("m_bHaveN[] = (%d, %d)\n",m_bHaveN[0],m_bHaveN[1]);
|
|
text_log.Print("m_N[] = (");
|
|
text_log.Print(m_N[0]);
|
|
text_log.Print(", ");
|
|
text_log.Print(m_N[1]);
|
|
text_log.Print("\n");
|
|
text_log.Print("m_path_domain = (%.17g, %.17g)\n",m_path_domain[0],m_path_domain[1]);
|
|
text_log.Print("m_bTransposed = %d\n",m_bTransposed);
|
|
text_log.Print("Profile Count: %d\n",m_profile_count);
|
|
text_log.Print("Profile:\n");
|
|
{
|
|
text_log.PushIndent();
|
|
if ( !m_profile )
|
|
text_log.Print("nullptr");
|
|
else
|
|
m_profile->Dump(text_log);
|
|
text_log.PopIndent();
|
|
}
|
|
|
|
m_mesh_cache.Dump(text_log);
|
|
|
|
text_log.PopIndent();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
unsigned int ON_Extrusion::SizeOf() const
|
|
{
|
|
unsigned int sz = sizeof(*this) - sizeof(ON_Surface);
|
|
if ( m_profile )
|
|
sz += m_profile->SizeOf();
|
|
return sz;
|
|
}
|
|
|
|
ON__UINT32 ON_Extrusion::DataCRC(ON__UINT32 current_remainder) const
|
|
{
|
|
if ( m_profile )
|
|
current_remainder = m_profile->DataCRC(current_remainder);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_path),&m_path);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_t),&m_t);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_up),&m_up);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_bHaveN[0]), &m_bHaveN[0]);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_bHaveN[1]), &m_bHaveN[1]);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_N[0]), &m_N[0]);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_N[1]), &m_N[1]);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_path_domain), &m_path_domain);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_bTransposed), &m_bTransposed);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_profile_count), &m_profile_count);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_bCap[0]), &m_bCap[0]);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_bCap[1]), &m_bCap[1]);
|
|
if ( m_profile )
|
|
current_remainder = m_profile->DataCRC(current_remainder);
|
|
return current_remainder;
|
|
}
|
|
|
|
// OBSOLETE - USED TO READ/WRITE V5 files
|
|
class ON_V5ExtrusionDisplayMeshCache : public ON_UserData
|
|
{
|
|
ON_OBJECT_DECLARE(ON_V5ExtrusionDisplayMeshCache);
|
|
public:
|
|
ON_V5ExtrusionDisplayMeshCache();
|
|
~ON_V5ExtrusionDisplayMeshCache();
|
|
ON_V5ExtrusionDisplayMeshCache(const ON_V5ExtrusionDisplayMeshCache&);
|
|
ON_V5ExtrusionDisplayMeshCache& operator=(const ON_V5ExtrusionDisplayMeshCache&);
|
|
|
|
static ON_V5ExtrusionDisplayMeshCache* CreateMeshCache(
|
|
const ON_Extrusion*
|
|
);
|
|
|
|
// virtual ON_Object::Write function
|
|
bool Write(ON_BinaryArchive& binary_archive) const override;
|
|
|
|
// virtual ON_Object::Read function
|
|
bool Read(ON_BinaryArchive& binary_archive) override;
|
|
|
|
// virtual ON_UserData::GetDescription
|
|
bool GetDescription( ON_wString& description ) override;
|
|
|
|
// virtual ON_UserData::Archive
|
|
bool Archive() const override;
|
|
|
|
// virtual ON_UserData::DeleteAfterWrite
|
|
bool DeleteAfterWrite(
|
|
const class ON_BinaryArchive& archive,
|
|
const class ON_Object* parent_object
|
|
) const override;
|
|
|
|
// virtual ON_UserData::DeleteAfterRead
|
|
bool DeleteAfterRead(
|
|
const class ON_BinaryArchive& archive,
|
|
class ON_Object* parent_object
|
|
) const override;
|
|
|
|
private:
|
|
void DestroyHelper();
|
|
void CopyHelper(const ON_V5ExtrusionDisplayMeshCache&);
|
|
|
|
// Only extrusion objects use the obsolete ON_V5ExtrusionDisplayMeshCache.
|
|
//const ON::object_type m_object_type = ON::object_type::extrusion_object;
|
|
std::shared_ptr<ON_Mesh> m_render_mesh;
|
|
std::shared_ptr<ON_Mesh> m_analysis_mesh;
|
|
};
|
|
|
|
|
|
bool ON_Extrusion::Write(
|
|
ON_BinaryArchive& binary_archive
|
|
) const
|
|
{
|
|
// 2015-June-03
|
|
// chunk version 1.3 - added m_mesh_cache
|
|
const int minor_version = binary_archive.Archive3dmVersion() >= 60 ? 3 : 2;
|
|
|
|
bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,minor_version);
|
|
if (!rc)
|
|
return false;
|
|
for (;;)
|
|
{
|
|
rc = binary_archive.WriteObject(m_profile);
|
|
if (!rc) break;
|
|
rc = binary_archive.WriteLine(m_path);
|
|
if (!rc) break;
|
|
rc = binary_archive.WriteInterval(m_t);
|
|
if (!rc) break;
|
|
rc = binary_archive.WriteVector(m_up);
|
|
if (!rc) break;
|
|
rc = binary_archive.WriteBool(m_bHaveN[0]);
|
|
if (!rc) break;
|
|
rc = binary_archive.WriteBool(m_bHaveN[1]);
|
|
if (!rc) break;
|
|
rc = binary_archive.WriteVector(m_N[0]);
|
|
if (!rc) break;
|
|
rc = binary_archive.WriteVector(m_N[1]);
|
|
if (!rc) break;
|
|
rc = binary_archive.WriteInterval(m_path_domain);
|
|
if (!rc) break;
|
|
rc = binary_archive.WriteBool(m_bTransposed);
|
|
if (!rc) break;
|
|
// 1.1
|
|
rc = binary_archive.WriteInt(m_profile_count);
|
|
if (!rc) break;
|
|
|
|
|
|
// 1.2
|
|
rc = binary_archive.WriteBool(m_bCap[0]);
|
|
if (!rc) break;
|
|
rc = binary_archive.WriteBool(m_bCap[1]);
|
|
if (!rc) break;
|
|
|
|
if (2 == minor_version && binary_archive.Save3dmRenderMesh(ObjectType()))
|
|
{
|
|
// add temporary V5 user data cache
|
|
ON_V5ExtrusionDisplayMeshCache::CreateMeshCache(this);
|
|
}
|
|
else if (minor_version >= 3)
|
|
{
|
|
if (binary_archive.Save3dmRenderMesh(ObjectType()))
|
|
rc = m_mesh_cache.Write(binary_archive);
|
|
else
|
|
rc = ON_MeshCache::Empty.Write(binary_archive);
|
|
if (!rc) break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
if ( !binary_archive.EndWrite3dmChunk() )
|
|
rc = false;
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_Extrusion::Read(
|
|
ON_BinaryArchive& binary_archive
|
|
)
|
|
{
|
|
Destroy();
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
|
|
if (!rc)
|
|
return false;
|
|
for (;;)
|
|
{
|
|
rc = (1 == major_version);
|
|
if (!rc) break;
|
|
ON_Object* obj = 0;
|
|
rc = (1==binary_archive.ReadObject(&obj));
|
|
if (!rc) break;
|
|
if ( obj )
|
|
{
|
|
m_profile = ON_Curve::Cast(obj);
|
|
if ( !m_profile )
|
|
{
|
|
delete obj;
|
|
rc = false;
|
|
break;
|
|
}
|
|
}
|
|
rc = binary_archive.ReadLine(m_path);
|
|
if (!rc) break;
|
|
rc = binary_archive.ReadInterval(m_t);
|
|
if (!rc) break;
|
|
rc = binary_archive.ReadVector(m_up);
|
|
if (!rc) break;
|
|
rc = binary_archive.ReadBool(&m_bHaveN[0]);
|
|
if (!rc) break;
|
|
rc = binary_archive.ReadBool(&m_bHaveN[1]);
|
|
if (!rc) break;
|
|
rc = binary_archive.ReadVector(m_N[0]);
|
|
if (!rc) break;
|
|
rc = binary_archive.ReadVector(m_N[1]);
|
|
if (!rc) break;
|
|
rc = binary_archive.ReadInterval(m_path_domain);
|
|
if (!rc) break;
|
|
rc = binary_archive.ReadBool(&m_bTransposed);
|
|
if (!rc) break;
|
|
|
|
// set profile count if extrusion was read from an old file.
|
|
m_profile_count = (0 != m_profile) ? 1 : 0;
|
|
|
|
if ( minor_version >= 1 )
|
|
{
|
|
// 1.1
|
|
rc = binary_archive.ReadInt(&m_profile_count);
|
|
if (!rc) break;
|
|
|
|
if ( minor_version >= 2 )
|
|
{
|
|
// 1.2
|
|
rc = binary_archive.ReadBool(&m_bCap[0]);
|
|
if (!rc) break;
|
|
rc = binary_archive.ReadBool(&m_bCap[1]);
|
|
if (!rc) break;
|
|
|
|
if (minor_version >= 3)
|
|
{
|
|
rc = m_mesh_cache.Read(binary_archive);
|
|
if (!rc) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( minor_version < 2 )
|
|
{
|
|
const ON_Curve* outer_profile = Profile(0);
|
|
if ( 0 != outer_profile && outer_profile->IsClosed() )
|
|
{
|
|
m_bCap[0] = m_bCap[1] = true;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ( !binary_archive.EndRead3dmChunk() )
|
|
rc = false;
|
|
|
|
return rc;
|
|
}
|
|
|
|
ON::object_type ON_Extrusion::ObjectType() const
|
|
{
|
|
return ON::extrusion_object;
|
|
}
|
|
|
|
|
|
int ON_Extrusion::Dimension() const
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
static
|
|
bool GetBoundingBoxHelper(const ON_Extrusion& extrusion,
|
|
ON_BoundingBox& bbox,
|
|
const ON_Xform* xform
|
|
)
|
|
{
|
|
// input bbox = profile curve bounding box.
|
|
ON_3dPoint corners[8];
|
|
bbox.m_min.z = 0.0;
|
|
bbox.m_max.z = 0.0;
|
|
corners[0] = corners[1] = bbox.m_min;
|
|
corners[1].x = bbox.m_max.x;
|
|
corners[2] = corners[3] = bbox.m_max;
|
|
corners[3].x = bbox.m_min.x;
|
|
corners[4] = corners[0];
|
|
corners[5] = corners[1];
|
|
corners[6] = corners[2];
|
|
corners[7] = corners[3];
|
|
|
|
ON_Xform xform0;
|
|
if ( !extrusion.GetProfileTransformation(0,xform0) )
|
|
return false;
|
|
ON_Xform xform1;
|
|
if ( !extrusion.GetProfileTransformation(1,xform1) )
|
|
return false;
|
|
if ( xform && !xform->IsIdentity() )
|
|
{
|
|
xform0 = (*xform)*xform0;
|
|
xform1 = (*xform)*xform1;
|
|
}
|
|
corners[0] = xform0*corners[0];
|
|
corners[1] = xform0*corners[1];
|
|
corners[2] = xform0*corners[2];
|
|
corners[3] = xform0*corners[3];
|
|
corners[4] = xform1*corners[4];
|
|
corners[5] = xform1*corners[5];
|
|
corners[6] = xform1*corners[6];
|
|
corners[7] = xform1*corners[7];
|
|
bbox.Set(3,0,8,3,&corners[0].x,false);
|
|
return true;
|
|
}
|
|
|
|
bool ON_Extrusion::GetBBox(double* boxmin,double* boxmax,bool bGrowBox) const
|
|
{
|
|
bool rc = false;
|
|
if ( m_path.IsValid() && m_profile )
|
|
{
|
|
ON_BoundingBox bbox;
|
|
if ( m_profile->GetTightBoundingBox(bbox) && GetBoundingBoxHelper(*this,bbox,0) )
|
|
{
|
|
rc = true;
|
|
if ( bGrowBox )
|
|
{
|
|
bGrowBox = ( boxmin[0] <= boxmax[0]
|
|
&& boxmin[1] <= boxmax[1]
|
|
&& boxmin[2] <= boxmax[2]
|
|
&& ON_IsValid(boxmax[0])
|
|
&& ON_IsValid(boxmax[1])
|
|
&& ON_IsValid(boxmax[2]));
|
|
}
|
|
if ( bGrowBox )
|
|
{
|
|
if ( boxmin[0] > bbox.m_min.x ) boxmin[0] = bbox.m_min.x;
|
|
if ( boxmin[1] > bbox.m_min.y ) boxmin[1] = bbox.m_min.y;
|
|
if ( boxmin[2] > bbox.m_min.z ) boxmin[2] = bbox.m_min.z;
|
|
if ( boxmax[0] < bbox.m_max.x ) boxmax[0] = bbox.m_max.x;
|
|
if ( boxmax[1] < bbox.m_max.y ) boxmax[1] = bbox.m_max.y;
|
|
if ( boxmax[2] < bbox.m_max.z ) boxmax[2] = bbox.m_max.z;
|
|
}
|
|
else
|
|
{
|
|
boxmin[0] = bbox.m_min.x;
|
|
boxmin[1] = bbox.m_min.y;
|
|
boxmin[2] = bbox.m_min.z;
|
|
boxmax[0] = bbox.m_max.x;
|
|
boxmax[1] = bbox.m_max.y;
|
|
boxmax[2] = bbox.m_max.z;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_Extrusion::GetTightBoundingBox(ON_BoundingBox& tight_bbox, bool bGrowBox, const ON_Xform* xform ) const
|
|
{
|
|
bool rc = false;
|
|
if ( m_path.IsValid() && m_profile )
|
|
{
|
|
ON_BoundingBox bbox;
|
|
if ( m_profile->GetTightBoundingBox(bbox) && GetBoundingBoxHelper(*this,bbox,xform) )
|
|
{
|
|
if ( bGrowBox )
|
|
tight_bbox.Union(bbox);
|
|
else
|
|
tight_bbox = bbox;
|
|
rc = true;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static bool ON_Extrusion_TransformFailed()
|
|
{
|
|
return false; // good place for a breakpoint
|
|
}
|
|
|
|
static bool Profile2dTransform( ON_Extrusion& e, const ON_Xform& profile_xform, bool bNeedReverse )
|
|
{
|
|
if ( profile_xform.IsIdentity() )
|
|
{
|
|
// profile curves are unchanged (translation, rotation, 1D scale along path, ... )
|
|
return true;
|
|
}
|
|
|
|
// transform 2d profiles
|
|
bool rc = false;
|
|
bool bNeedDeformable = ( fabs(profile_xform.m_xform[0][0]) != fabs(profile_xform.m_xform[1][1])
|
|
|| 0.0 != profile_xform.m_xform[1][0]
|
|
);
|
|
ON_PolyCurve* polyprofile = const_cast<ON_PolyCurve*>(e.PolyProfile());
|
|
if ( 0 != polyprofile )
|
|
{
|
|
rc = true;
|
|
if ( bNeedDeformable )
|
|
polyprofile->MakeDeformable();
|
|
for ( int i = 0; i < polyprofile->Count(); i++ )
|
|
{
|
|
ON_Curve* profile_segment = polyprofile->SegmentCurve(i);
|
|
if ( 0 == profile_segment )
|
|
{
|
|
continue;
|
|
}
|
|
if ( !profile_segment->Transform(profile_xform) )
|
|
{
|
|
rc = ON_Extrusion_TransformFailed();
|
|
continue;
|
|
}
|
|
if ( bNeedReverse )
|
|
{
|
|
double t0, t1;
|
|
if ( profile_segment->GetDomain(&t0,&t1) )
|
|
{
|
|
// reverse the segment so the correct clounterclockwise/clockwise
|
|
// orientation is maintained after reflection across the y axis.
|
|
profile_segment->Reverse();
|
|
// preserve the evaluation domain of each segment
|
|
profile_segment->SetDomain(t0,t1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( bNeedDeformable && !e.m_profile->IsDeformable() )
|
|
{
|
|
ON_NurbsCurve* c = e.m_profile->NurbsCurve();
|
|
if ( 0 != c )
|
|
{
|
|
c->CopyUserData(*e.m_profile);
|
|
if( c->Transform(profile_xform) )
|
|
{
|
|
rc = true;
|
|
delete e.m_profile;
|
|
e.m_profile = c;
|
|
}
|
|
else
|
|
{
|
|
delete c;
|
|
rc = ON_Extrusion_TransformFailed();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = ON_Extrusion_TransformFailed();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = e.m_profile->Transform(profile_xform)?true:ON_Extrusion_TransformFailed();
|
|
}
|
|
if ( rc && bNeedReverse )
|
|
{
|
|
double t0, t1;
|
|
if ( e.m_profile->GetDomain(&t0,&t1) )
|
|
{
|
|
// reverse the segment so the correct clounterclockwise/clockwise
|
|
// orientation is maintained after reflection across the y axis.
|
|
e.m_profile->Reverse();
|
|
// preserve the evaluation domain of each segment
|
|
e.m_profile->SetDomain(t0,t1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Extrusion::Transform( const ON_Xform& xform )
|
|
{
|
|
if ( !m_path.IsValid() || !m_up.IsUnitVector() || m_profile_count < 1 )
|
|
return ON_Extrusion_TransformFailed();
|
|
|
|
ON_3dVector T = m_path.Tangent();
|
|
ON_3dVector UxT = ON_CrossProduct(m_up,T);
|
|
if ( !UxT.IsUnitVector() && !UxT.Unitize() )
|
|
return ON_Extrusion_TransformFailed();
|
|
|
|
const bool bUseVectorXform = (0.0 == xform[3][0] && 0.0 == xform[3][1] && 0.0 == xform[3][2]);
|
|
|
|
ON_3dPoint E[2], QE[2];
|
|
E[0] = m_path.from;
|
|
E[1] = m_path.to;
|
|
QE[0] = xform*E[0];
|
|
QE[1] = xform*E[1];
|
|
if ( !QE[0].IsValid() )
|
|
return ON_Extrusion_TransformFailed();
|
|
if ( !QE[1].IsValid() )
|
|
return ON_Extrusion_TransformFailed();
|
|
ON_3dVector QT0 = bUseVectorXform ? (xform*(E[1] - E[0])) : (QE[1]-QE[0]);
|
|
if ( !QT0.Unitize() )
|
|
return ON_Extrusion_TransformFailed();
|
|
|
|
const int base_index = ( QE[1].DistanceTo(E[1]) < QE[0].DistanceTo(E[0]) ) ? 1 : 0;
|
|
const ON_3dPoint B(E[base_index]);
|
|
const ON_3dPoint QB(QE[base_index]);
|
|
|
|
const double QT0oT = QT0*T;
|
|
bool bSamePathDir = ( fabs(fabs(QT0oT) - 1.0) <= ON_SQRT_EPSILON );
|
|
ON_3dVector QT = bSamePathDir ? ((QT0oT < 0.0) ? -T : T) : QT0;
|
|
const double Qlen = QE[0].DistanceTo(QE[1]);
|
|
if ( bSamePathDir )
|
|
{
|
|
ON_3dPoint R = QB + (base_index ? -Qlen : Qlen)*QT;
|
|
if ( QE[1-base_index].DistanceTo( R ) <= ON_SQRT_EPSILON*Qlen )
|
|
{
|
|
QE[1-base_index] = R;
|
|
}
|
|
else
|
|
{
|
|
bSamePathDir = false;
|
|
QT = QT0;
|
|
}
|
|
}
|
|
|
|
const ON_3dPoint X0 = xform*(B + UxT);
|
|
const ON_3dPoint Y0 = xform*(B + m_up);
|
|
const ON_3dVector QDY = bUseVectorXform ? (xform*m_up) : (Y0-QB);
|
|
ON_3dVector QY = QDY;
|
|
if ( !QY.Unitize() )
|
|
return ON_Extrusion_TransformFailed();
|
|
ON_3dVector QU0 = QDY - (QDY*QT)*QT;
|
|
if ( !QU0.Unitize() )
|
|
return ON_Extrusion_TransformFailed();
|
|
|
|
const double QU0oU = QU0*m_up;
|
|
bool bSameUpDir = ( fabs(fabs(QU0oU) - 1.0) <= ON_SQRT_EPSILON );
|
|
ON_3dVector QU = bSameUpDir ? ((QU0oU < 0.0) ? -m_up : m_up) : QU0;
|
|
|
|
if (bSameUpDir && !bSamePathDir && fabs(QU*QT) > fabs(QU0*QT) )
|
|
{
|
|
// 12 July 2008 Dale Lear - this test fixes http://dev.mcneel.com/bugtrack/?q=88130
|
|
bSameUpDir = false;
|
|
QU = QU0;
|
|
}
|
|
|
|
ON_3dVector QUxQT = ON_CrossProduct(QU,QT);
|
|
if ( !QUxQT.Unitize() )
|
|
return ON_Extrusion_TransformFailed();
|
|
|
|
const double QUxQToUxT = QUxQT*UxT;
|
|
if ( (bSamePathDir && bSameUpDir) || fabs(fabs(QUxQToUxT) - 1.0) <= ON_SQRT_EPSILON )
|
|
{
|
|
if ( QUxQToUxT < 0.0 )
|
|
QUxQT = -UxT;
|
|
else
|
|
QUxQT = UxT;
|
|
}
|
|
|
|
const double QUoQY = QU*QY;
|
|
if ( fabs(QUoQY - 1.0) < ON_SQRT_EPSILON )
|
|
QY = QU;
|
|
else if ( fabs(QUoQY + 1.0) < ON_SQRT_EPSILON )
|
|
QY = -QU;
|
|
|
|
// profile_xform will be the transformation
|
|
// applied to the 2d profile curve.
|
|
const ON_3dVector QDX = bUseVectorXform ? (xform*UxT) : (X0 - QB);
|
|
ON_3dVector QX = QDX - (QDX*QY)*QY;
|
|
if ( !QX.Unitize() )
|
|
return ON_Extrusion_TransformFailed();
|
|
|
|
const double QUxQToQX = QUxQT*QX;
|
|
if ( fabs(QUxQToQX - 1.0) < ON_SQRT_EPSILON )
|
|
QX = QUxQT;
|
|
else if ( fabs(QUxQToQX + 1.0) < ON_SQRT_EPSILON )
|
|
QX = -QUxQT;
|
|
|
|
|
|
ON_3dVector QXxQY = ON_CrossProduct(QX,QY);
|
|
if ( !QXxQY.IsUnitVector() && !QXxQY.Unitize() )
|
|
return ON_Extrusion_TransformFailed();
|
|
if ( QXxQY*QT < 0.0 )
|
|
{
|
|
QX = -QX;
|
|
QXxQY = -QXxQY;
|
|
}
|
|
|
|
ON_3dVector path_shear(0.0,QU*QXxQY,QT*QXxQY);
|
|
bool bHavePathShear = path_shear.z > ON_Extrusion::m_Nz_min
|
|
&& path_shear.z < 1.0 - ON_SQRT_EPSILON
|
|
&& path_shear.y < 1.0 - ON_SQRT_EPSILON;
|
|
if ( bHavePathShear )
|
|
{
|
|
double x2 = path_shear.y*path_shear.y + path_shear.z*path_shear.z;
|
|
if ( x2 > ON_SQRT_EPSILON && x2 <= 1.0 )
|
|
{
|
|
double QUxQToQXxQY = QUxQT*QXxQY;
|
|
path_shear.x = sqrt(1.0 - x2);
|
|
if ( QUxQToQXxQY < 0.0 )
|
|
path_shear.x = -path_shear.x;
|
|
}
|
|
if ( fabs(path_shear.y) <= ON_SQRT_EPSILON )
|
|
path_shear.y = 0.0;
|
|
bHavePathShear = path_shear.IsUnitVector() && (path_shear.x != 0.0 || path_shear.y != 0.0);
|
|
}
|
|
|
|
if ( !bHavePathShear )
|
|
{
|
|
path_shear.Set(0.0,0.0,1.0);
|
|
QXxQY = QT;
|
|
}
|
|
|
|
// miter normals
|
|
ON_3dVector QN[2] = {ON_3dVector::ZeroVector,ON_3dVector::ZeroVector};
|
|
bool bHaveQN[2] = {false,false};
|
|
bool bUseQN[2] = {false,false};
|
|
for ( int i = 0; i < 2; i++ )
|
|
{
|
|
if ( m_bHaveN[i] )
|
|
{
|
|
QN[i] = m_N[i];
|
|
bHaveQN[i] = true;
|
|
bUseQN[i] = true;
|
|
|
|
// In order for this to work with transformations that
|
|
// have bHavePathShear = true, we have to see where the
|
|
// miter *plane* is mapped which, for a shear
|
|
// transformation, cannot be done by transforming
|
|
// m_N[i] = normal to the miter plane.
|
|
ON_3dVector QN3d;
|
|
if ( bHavePathShear )
|
|
{
|
|
// calculate a world 3d basis for the miter plane
|
|
ON_3dVector V1(m_N[i].y,-m_N[i].x,0.0);
|
|
V1.Unitize();
|
|
ON_3dVector V2 = ON_CrossProduct( m_N[i],V1);
|
|
V1 = V1.x*UxT + V1.y*m_up + V1.z*T;
|
|
V2 = V2.x*UxT + V2.y*m_up + V2.z*T;
|
|
|
|
// transform the basis to get a world 3d basis
|
|
// for the transformed miter plane.
|
|
ON_3dVector QV1 = xform*V1;
|
|
ON_3dVector QV2 = xform*V2;
|
|
double lenQV1 = QV1.Length();
|
|
double lenQV2 = QV2.Length();
|
|
if ( fabs(lenQV1 - lenQV2) > 0.125*(lenQV1 + lenQV2) )
|
|
{
|
|
// if lengths are "different", then normalize
|
|
// before taking the cross product. Otherwise,
|
|
// skip normalization to get slightly
|
|
// more precision in a calculation that is
|
|
// already pretty fuzzy.
|
|
QV1.Unitize();
|
|
QV2.Unitize();
|
|
}
|
|
|
|
// now get a world 3d normal to the miter plane
|
|
QN3d = ON_CrossProduct(QV1,QV2);
|
|
}
|
|
else
|
|
{
|
|
const ON_3dVector N3d = m_N[i].x*UxT + m_N[i].y*m_up + m_N[i].z*T;
|
|
ON_3dPoint QTip = xform*(E[i] + N3d);
|
|
QN3d = bUseVectorXform ? (xform*N3d) : (QTip - QE[i]);
|
|
}
|
|
if ( !QN3d.Unitize() )
|
|
continue;
|
|
|
|
// QN[i] = miter vector
|
|
QN[i].x = QUxQT*QN3d;
|
|
QN[i].y = QU*QN3d;
|
|
QN[i].z = QT*QN3d;
|
|
if (QN[i].z < 0.0)
|
|
QN[i] = -QN[i]; // necessary for some mirror transformations
|
|
if ( !QN[i].Unitize() )
|
|
{
|
|
bHaveQN[i] = false;
|
|
}
|
|
if ( fabs(QN[i].x) <= ON_SQRT_EPSILON )
|
|
QN[i].x = 0.0;
|
|
if ( fabs(QN[i].y) <= ON_SQRT_EPSILON )
|
|
QN[i].y = 0.0;
|
|
if ( QN[i].z < ON_Extrusion::m_Nz_min
|
|
|| QN[i].z >= 1.0 - ON_SQRT_EPSILON
|
|
|| (fabs(QN[i].x) <= ON_SQRT_EPSILON && fabs(QN[i].y) <= ON_SQRT_EPSILON )
|
|
)
|
|
{
|
|
bHaveQN[i] = false;
|
|
}
|
|
|
|
if ( !bHaveQN[i] )
|
|
QN[i] = ON_3dVector::ZeroVector;
|
|
else if ( fabs(QN[i]*m_N[i] - 1.0) <= 1.0e-6 ) // ON_SQRT_EPSILON
|
|
QN[i] = m_N[i];
|
|
}
|
|
}
|
|
|
|
// get 2d profile transformation
|
|
ON_Xform profile_xform(ON_Xform::IdentityTransformation);
|
|
// set 2d profile y scale
|
|
const double profile_y_scale = QDY.Length(); // = QYoQDY "mathematically"
|
|
|
|
if ( !bHavePathShear )
|
|
{
|
|
const double profile_x_scale = QX*QDX;
|
|
|
|
if ( !ON_IsValid(profile_y_scale) || 0.0 == profile_y_scale )
|
|
{
|
|
// cannot flatten profile
|
|
return ON_Extrusion_TransformFailed();
|
|
}
|
|
|
|
if ( !ON_IsValid(profile_x_scale) || 0.0 == profile_x_scale )
|
|
{
|
|
// cannot flatten profile
|
|
return ON_Extrusion_TransformFailed();
|
|
}
|
|
|
|
// NOTE:
|
|
// profile_xform.m_xform[1][1] must always be > 0.0 and
|
|
// is the most precise number number in the matrix.
|
|
const double profile_y_scale_tol = ON_SQRT_EPSILON;
|
|
const double profile_x_scale_tol = 10.0*profile_y_scale_tol;
|
|
|
|
if ( fabs(profile_y_scale - 1.0) <= profile_y_scale_tol )
|
|
profile_xform.m_xform[1][1] = 1.0;
|
|
else
|
|
profile_xform.m_xform[1][1] = profile_y_scale;
|
|
|
|
if ( fabs( profile_x_scale - 1.0 ) <= profile_x_scale_tol )
|
|
profile_xform.m_xform[0][0] = 1.0;
|
|
else if ( fabs( profile_x_scale + 1.0 ) <= profile_x_scale_tol )
|
|
profile_xform.m_xform[0][0] = -1.0;
|
|
else
|
|
profile_xform.m_xform[0][0] = profile_x_scale;
|
|
|
|
const double profile_xform_tol = profile_x_scale_tol*(fabs(profile_xform.m_xform[0][0]) + profile_xform.m_xform[1][1]);
|
|
if ( fabs(fabs(profile_xform.m_xform[0][0]) - profile_xform.m_xform[1][1]) <= profile_xform_tol )
|
|
{
|
|
profile_xform.m_xform[0][0] = ((profile_xform.m_xform[0][0] < 0.0)?-1.0:1.0)*profile_xform.m_xform[1][1];
|
|
}
|
|
else if ( fabs(profile_xform.m_xform[0][0]) <= profile_xform_tol )
|
|
{
|
|
// cannot flatten profile
|
|
return ON_Extrusion_TransformFailed();
|
|
}
|
|
|
|
double profile_det = profile_xform.m_xform[0][0]*profile_xform.m_xform[1][1];
|
|
if ( !ON_IsValid(profile_det) || 0.0 == profile_det )
|
|
return ON_Extrusion_TransformFailed();
|
|
|
|
// set 2d profile shear
|
|
const double profile_shear_tol = profile_xform_tol;
|
|
const double QYoQDX = QY*QDX;
|
|
if ( fabs( QYoQDX ) <= profile_shear_tol )
|
|
profile_xform.m_xform[1][0] = 0.0;
|
|
else
|
|
profile_xform.m_xform[1][0] = QYoQDX;
|
|
}
|
|
else
|
|
{
|
|
// plane0 is world 3d plane that provides the coordinate system
|
|
// for mapping the original 2d profile into world 3d.
|
|
ON_Plane plane0;
|
|
plane0.origin = B; plane0.xaxis = UxT; plane0.yaxis = m_up; plane0.zaxis = T;
|
|
plane0.UpdateEquation();
|
|
|
|
// plane1 is world 3d plane that provides the coordinate system
|
|
// for mapping the transformed 2d profile into world 3d.
|
|
ON_Plane plane1;
|
|
plane1.origin = QB; plane1.xaxis = QUxQT; plane1.yaxis = QU; plane1.zaxis = QT;
|
|
plane1.UpdateEquation();
|
|
|
|
ON_Xform xform1, xform3, xform4, xform5;
|
|
|
|
// xform 1 maps original 2d profile to world 3d
|
|
xform1.Rotation(ON_Plane::World_xy,plane0);
|
|
|
|
// xform maps plane1 to
|
|
|
|
// xform 3 maps the transformed 3d profile to the world 3d plane
|
|
// that is orthoganal to the transformed axis vector.
|
|
xform3.PlanarProjection(plane1);
|
|
|
|
// xform4 maps the transformed world 3d profile back to the xy plane
|
|
xform4.Rotation(plane1, ON_Plane::World_xy);
|
|
|
|
profile_xform = xform4*xform3*xform*xform1;
|
|
profile_xform.m_xform[0][2] = 0.0;
|
|
profile_xform.m_xform[1][2] = 0.0;
|
|
profile_xform.m_xform[2][0] = 0.0; profile_xform.m_xform[2][1] = 0.0; profile_xform.m_xform[2][2] = 1.0; profile_xform.m_xform[2][3] = 0.0;
|
|
profile_xform.m_xform[3][0] = 0.0; profile_xform.m_xform[3][1] = 0.0; profile_xform.m_xform[3][2] = 0.0; profile_xform.m_xform[3][3] = 1.0;
|
|
|
|
const double xform_tol = 10.0*ON_SQRT_EPSILON;
|
|
if ( profile_y_scale > xform_tol && fabs(profile_y_scale - 1.0) > xform_tol )
|
|
{
|
|
double profile_scale_tol = profile_y_scale*ON_SQRT_EPSILON;
|
|
if ( fabs(profile_xform.m_xform[0][0] - profile_y_scale) < profile_scale_tol )
|
|
{
|
|
profile_xform.m_xform[0][0] = profile_y_scale;
|
|
if ( fabs(profile_xform.m_xform[1][1] - profile_y_scale) < profile_scale_tol )
|
|
profile_xform.m_xform[1][1] = profile_y_scale;
|
|
else if ( fabs(profile_xform.m_xform[1][1] + profile_y_scale) < profile_scale_tol )
|
|
profile_xform.m_xform[1][1] = -profile_y_scale;
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < 2; i++ ) for ( int j = 0; j < 4; j++ )
|
|
{
|
|
if ( 2 == j )
|
|
continue;
|
|
double x = fabs(profile_xform.m_xform[i][j]);
|
|
if ( fabs(x) <= xform_tol )
|
|
profile_xform.m_xform[i][j] = 0.0;
|
|
else if ( fabs(1.0-x) <= xform_tol )
|
|
profile_xform.m_xform[i][j] = 1.0;
|
|
else if ( fabs(x-1.0) <= xform_tol )
|
|
profile_xform.m_xform[i][j] = -1.0;
|
|
}
|
|
}
|
|
|
|
// start transforming informtion here
|
|
TransformUserData(xform);
|
|
|
|
// transform path, up, and miter directions
|
|
m_path.from = QE[0];
|
|
m_path.to = QE[1];
|
|
m_up = QU;
|
|
for ( int i = 0; i < 2; i++ )
|
|
{
|
|
if ( bUseQN[i] )
|
|
{
|
|
m_N[i] = QN[i];
|
|
m_bHaveN[i] = bHaveQN[i];
|
|
}
|
|
else if ( bHavePathShear )
|
|
{
|
|
m_N[i] = path_shear;
|
|
m_bHaveN[i] = true;
|
|
}
|
|
else
|
|
{
|
|
m_N[i].Set(0.0,0.0,0.0);
|
|
m_bHaveN[i] = false;
|
|
}
|
|
}
|
|
|
|
//if ( m_bHaveN[0] && QN[0].IsValid() && QN[0].IsUnitVector() )
|
|
// m_N[0] = QN[0];
|
|
//if ( m_bHaveN[1] && QN[1].IsValid() && QN[1].IsUnitVector() )
|
|
// m_N[1] = QN[1];
|
|
//if ( bHavePathShear )
|
|
//{
|
|
// // TODO:
|
|
// // 1) combine with eisting mitering
|
|
// // 2) fix bug when eigenvector of the shear transform
|
|
// // is not parallel to a profile plane axis.
|
|
// m_bHaveN[0] = true;
|
|
// m_N[0] = path_shear;
|
|
// m_bHaveN[1] = true;
|
|
// m_N[1] = path_shear;
|
|
//}
|
|
|
|
|
|
double profile_det = profile_xform[0][0]*profile_xform[1][1] - profile_xform[1][0]*profile_xform[0][1];
|
|
bool bNeedReverse = (profile_det < 0.0);
|
|
|
|
if (bNeedReverse)
|
|
{
|
|
// Delete meshes. Flipping the mesh does not insure
|
|
// the correspondence between the mesh faces and the
|
|
// initial locations on the extrusion surface are preserved.
|
|
// A predictable way to convert a point on a mesh face to a
|
|
// point on the extrusion is required for robust sub-object
|
|
// selection.
|
|
// Generating meshes of extrusion objects is fast enough that
|
|
// recreating meshes used for rendering is generally
|
|
// not noticed by users.
|
|
m_mesh_cache.ClearAllMeshes();
|
|
}
|
|
else
|
|
{
|
|
m_mesh_cache.Transform(xform);
|
|
}
|
|
|
|
if ( profile_xform.IsIdentity() )
|
|
return true; // should happen on all rotations and translations.
|
|
|
|
return Profile2dTransform( *this, profile_xform, bNeedReverse );
|
|
}
|
|
|
|
class CMyBrepIsSolidSetter : public ON_Brep
|
|
{
|
|
public:
|
|
void SetIsSolid(int is_solid) {m_is_solid = is_solid;}
|
|
void SetBBox( const ON_Extrusion& extrusion)
|
|
{
|
|
ON_BoundingBox brep_bbox = BoundingBox();
|
|
ON_BoundingBox extr_bbox = extrusion.BoundingBox();
|
|
ON_BoundingBox bbox;
|
|
bbox.Intersection(brep_bbox,extr_bbox);
|
|
m_bbox = bbox;
|
|
}
|
|
};
|
|
|
|
class ON_Extrusion_BrepForm_FaceInfo
|
|
{
|
|
public:
|
|
ON_Extrusion_BrepForm_FaceInfo();
|
|
|
|
~ON_Extrusion_BrepForm_FaceInfo();
|
|
|
|
/*
|
|
Returns
|
|
false if m_face_index < 0. This happens when the 3d profile
|
|
for the extrusion is too short or bogus or the nurbs surface
|
|
generated from this profile is bogus. One way this happens
|
|
is when the transformation from 2d to 3d has an enormous
|
|
translation component.
|
|
*/
|
|
bool HaveBrepFaceFace() const;
|
|
|
|
void Init();
|
|
|
|
// Information about the entire profile
|
|
// If this profile has kinks and we are splitting up
|
|
// kinky faces, then the ON_Extrusion_BrepForm_FaceInfo
|
|
// will be face made by extruding a smooth sub-curve
|
|
// of the entire profile.
|
|
bool m_bClosedProfile;
|
|
int m_profile_orientation;
|
|
int m_profile_index;
|
|
|
|
// Information for this wall face
|
|
ON_Curve* m_extrusion_profile;
|
|
ON_Extrusion* m_extrusion_srf;
|
|
int m_face_index;
|
|
int m_vid[4];
|
|
int m_eid[4];
|
|
bool m_bRev3d[4];
|
|
|
|
// capping information (bottom,top)
|
|
int m_cap_trim_index[2]; // indices of wall trims
|
|
int m_cap_edge_index[2];
|
|
ON_NurbsCurve* m_cap_c2[2]; // 2d cap trim curves
|
|
};
|
|
|
|
ON_Extrusion_BrepForm_FaceInfo::ON_Extrusion_BrepForm_FaceInfo()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
ON_Extrusion_BrepForm_FaceInfo::~ON_Extrusion_BrepForm_FaceInfo()
|
|
{
|
|
// when m_extrusion_srf is not null, its destructor deletes m_extrusion_profile.
|
|
if ( 0 != m_extrusion_srf )
|
|
{
|
|
// When m_extrusion_srf is not null, it
|
|
// manages the m_extrusion_profile curve
|
|
m_extrusion_profile = 0;
|
|
delete m_extrusion_srf;
|
|
m_extrusion_srf = 0;
|
|
}
|
|
|
|
if ( 0 != m_extrusion_profile )
|
|
{
|
|
delete m_extrusion_profile;
|
|
m_extrusion_profile = 0;
|
|
}
|
|
|
|
if ( m_cap_c2[0] )
|
|
{
|
|
delete m_cap_c2[0];
|
|
m_cap_c2[0] = 0;
|
|
}
|
|
|
|
if ( m_cap_c2[1] )
|
|
{
|
|
delete m_cap_c2[1];
|
|
m_cap_c2[1] = 0;
|
|
}
|
|
|
|
memset(this,0,sizeof(*this));
|
|
}
|
|
|
|
bool ON_Extrusion_BrepForm_FaceInfo::HaveBrepFaceFace() const
|
|
{
|
|
if ( m_face_index < 0 )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void ON_Extrusion_BrepForm_FaceInfo::Init()
|
|
{
|
|
memset(this,0,sizeof(*this));
|
|
|
|
m_bClosedProfile = false;
|
|
m_profile_orientation = 0;
|
|
m_profile_index = -1;
|
|
|
|
m_extrusion_profile = 0;
|
|
m_extrusion_srf = 0;
|
|
m_face_index = -1;
|
|
m_vid[0] = m_vid[1] = m_vid[2] = m_vid[3] = -1;
|
|
m_eid[0] = m_eid[1] = m_eid[2] = m_eid[3] = -1;
|
|
// Set the m_bRev3d[] flags so the 3d edges orientation
|
|
// matches the surface iso-curve orientation.
|
|
m_bRev3d[0] = m_bRev3d[1] = 0;
|
|
m_bRev3d[2] = m_bRev3d[3] = 1;
|
|
|
|
m_cap_trim_index[0] = m_cap_trim_index[1] = -1;
|
|
m_cap_edge_index[0] = m_cap_edge_index[1] = -1;
|
|
m_cap_c2[0] = m_cap_c2[1] = 0;
|
|
}
|
|
|
|
static
|
|
ON_PlaneSurface* MakeCapPlaneHelper(
|
|
ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo >& finfo,
|
|
int cap_index, // 0 = bottom, 1 = top
|
|
const ON_Xform& rot
|
|
)
|
|
{
|
|
double d;
|
|
ON_BoundingBox bbox;
|
|
|
|
// get bounding box of trimming curves
|
|
int outer_loop_trim_count = 0;
|
|
int outer_loop_iso_trim_count = 0;
|
|
for ( int i = 0; i < finfo.Count(); i++ )
|
|
{
|
|
if ( false == finfo[i].HaveBrepFaceFace() )
|
|
continue; // profile curve segment was too short or bogus
|
|
|
|
if ( 0 != finfo[i].m_profile_index )
|
|
{
|
|
// this and the rest of the finfo[] elements attach
|
|
// attach to an inner boundary on the cap.
|
|
break;
|
|
}
|
|
const ON_NurbsCurve* c2 = finfo[i].m_cap_c2[cap_index];
|
|
if ( 0 == c2 )
|
|
return 0;
|
|
ON_BoundingBox c2bbox;
|
|
c2->GetTightBoundingBox( c2bbox, false );
|
|
if ( 0 == i )
|
|
bbox = c2bbox;
|
|
else
|
|
bbox.Union(c2bbox);
|
|
if ( outer_loop_iso_trim_count == outer_loop_trim_count )
|
|
{
|
|
// check for iso trims as long as all previous trims are iso trims.
|
|
ON_3dVector D = c2bbox.Diagonal();
|
|
double zero_tol = ON_ZERO_TOLERANCE;
|
|
double nonzero_tol = 1000.0*ON_ZERO_TOLERANCE;
|
|
if ( (D.x <= zero_tol && D.y > nonzero_tol)
|
|
|| (D.y <= zero_tol && D.x > nonzero_tol)
|
|
)
|
|
{
|
|
outer_loop_iso_trim_count++;
|
|
}
|
|
}
|
|
outer_loop_trim_count++;
|
|
}
|
|
|
|
if ( outer_loop_trim_count <= 0 )
|
|
{
|
|
// must have an outer boundary.
|
|
return 0;
|
|
}
|
|
|
|
ON_Interval u(bbox.m_min.x,bbox.m_max.x);
|
|
ON_Interval v(bbox.m_min.y,bbox.m_max.y);
|
|
|
|
if ( outer_loop_iso_trim_count < outer_loop_trim_count
|
|
|| !u.IsIncreasing()
|
|
|| !v.IsIncreasing()
|
|
)
|
|
{
|
|
// grow cap plane to extend a little beyond the trims
|
|
d = u.Length();
|
|
if ( 0.0 == d )
|
|
d = 1.0; // happens when profile is a line
|
|
d *= 0.125; u.m_t[0] -= d; u.m_t[1] += d;
|
|
|
|
d = v.Length();
|
|
if ( 0.0 == d )
|
|
d = 1.0; // happens when profile is a line
|
|
d *= 0.125; v.m_t[0] -= d; v.m_t[1] += d;
|
|
}
|
|
|
|
if ( !u.IsIncreasing() || !v.IsIncreasing() )
|
|
return 0;
|
|
|
|
ON_PlaneSurface* plane = new ON_PlaneSurface(ON_xy_plane);
|
|
plane->SetExtents(0,u,true);
|
|
plane->SetExtents(1,v,true);
|
|
if ( !rot.IsIdentity() )
|
|
plane->Transform(rot);
|
|
|
|
return plane;
|
|
}
|
|
|
|
static
|
|
int MakeCapLoopHelper(
|
|
ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo >& finfo,
|
|
int fi0,
|
|
int cap_index, // 0 = bottom, 1 = top
|
|
ON_BrepFace* capface,
|
|
ON_BrepLoop::TYPE loop_type,
|
|
bool* bTrimsWereModified
|
|
)
|
|
{
|
|
ON_BrepEdge* edge;
|
|
|
|
if ( 0 == capface )
|
|
return fi0; // happens when extrusion has an open end
|
|
|
|
ON_Brep* brep = capface->Brep();
|
|
if ( 0 == brep )
|
|
return fi0;
|
|
|
|
if ( fi0 < 0 || fi0 >= finfo.Count() )
|
|
return fi0;
|
|
|
|
ON_BrepLoop& loop = brep->NewLoop(loop_type,*capface);
|
|
|
|
bool bRev3d = false;
|
|
bool bCloseGaps = true;
|
|
|
|
int fi1;
|
|
for ( fi1 = fi0; fi1 < finfo.Count(); fi1++ )
|
|
{
|
|
if ( false == finfo[fi1].HaveBrepFaceFace() )
|
|
continue; // profile segment for this face was too short or bogus
|
|
|
|
if ( finfo[fi0].m_profile_index != finfo[fi1].m_profile_index )
|
|
break;
|
|
|
|
ON_NurbsCurve* c2 = finfo[fi1].m_cap_c2[cap_index];
|
|
if ( 0 == c2 )
|
|
{
|
|
bCloseGaps = false;
|
|
break;
|
|
}
|
|
int c2i = brep->AddTrimCurve(c2);
|
|
finfo[fi1].m_cap_c2[cap_index] = 0;
|
|
edge = brep->Edge(finfo[fi1].m_cap_edge_index[cap_index]);
|
|
if ( 0 == edge )
|
|
{
|
|
bCloseGaps = false;
|
|
break;
|
|
}
|
|
ON_BrepTrim& trim = brep->NewTrim(*edge, bRev3d, loop, c2i);
|
|
trim.m_tolerance[0] = trim.m_tolerance[1] = 0.0;
|
|
}
|
|
brep->SetTrimIsoFlags( loop );
|
|
|
|
// 6 June 2012 Dale Lear
|
|
// Fix bug 105311
|
|
// Sometimes there are gaps in ON_PolyCurve profiles that
|
|
// need to be closed to make a valid ON_Brep. The gaps
|
|
// need to be closed after the call to SetTrimIsoFlags().
|
|
if ( bCloseGaps ) for ( int lti = 0; lti < loop.m_ti.Count(); lti++ )
|
|
{
|
|
ON_BrepTrim& trim0 = brep->m_T[loop.m_ti[lti]];
|
|
ON_BrepTrim& trim1 = brep->m_T[loop.m_ti[(lti+1)%loop.m_ti.Count()]];
|
|
if ( trim0.PointAtEnd() != trim1.PointAtStart() )
|
|
{
|
|
if ( brep->CloseTrimGap(trim0,trim1) )
|
|
{
|
|
edge = trim0.Edge();
|
|
if ( edge )
|
|
edge->m_tolerance = ON_UNSET_VALUE;
|
|
edge = trim1.Edge();
|
|
if ( edge )
|
|
edge->m_tolerance = ON_UNSET_VALUE;
|
|
if ( 0 != bTrimsWereModified )
|
|
*bTrimsWereModified = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fi1;
|
|
}
|
|
|
|
static
|
|
bool MakeCap2dCurvesHelper(
|
|
ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo >& finfo,
|
|
int is_capped,
|
|
const ON_Xform& scale0,
|
|
const ON_Xform& scale1
|
|
)
|
|
{
|
|
switch(is_capped)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
for ( int i = 0; i < finfo.Count(); i++ )
|
|
{
|
|
ON_NurbsCurve* c20 = 0;
|
|
ON_NurbsCurve* c21 = 0;
|
|
|
|
if ( false == finfo[i].HaveBrepFaceFace() )
|
|
continue; // profile curve was too short or bogus
|
|
|
|
c20 = finfo[i].m_extrusion_srf->m_profile->NurbsCurve();
|
|
if ( 0 == c20 )
|
|
return false;
|
|
|
|
c20->ChangeDimension(2);
|
|
c20->MakePiecewiseBezier(true);
|
|
if ( 2 == is_capped )
|
|
{
|
|
c21 = c20;
|
|
c20 = 0;
|
|
}
|
|
else if ( 3 == is_capped )
|
|
{
|
|
c21 = c20->Duplicate();
|
|
}
|
|
|
|
if ( 0 != c20 && !scale0.IsIdentity() )
|
|
c20->Transform(scale0);
|
|
if ( 0 != c21 && !scale1.IsIdentity() )
|
|
c21->Transform(scale1);
|
|
|
|
finfo[i].m_cap_c2[0] = c20;
|
|
finfo[i].m_cap_c2[1] = c21;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool GetNextProfileSegmentDiscontinuity(
|
|
const ON_Curve* profile_segment,
|
|
double t0,
|
|
double t1,
|
|
double *t
|
|
)
|
|
{
|
|
// Do NOT change ON_DEFAULT_ANGLE_TOLERANCE_COSINE to another value.
|
|
// See comments in CRhinoDoc::AddObject() for details.
|
|
if ( 0 == profile_segment )
|
|
return false;
|
|
return profile_segment->GetNextDiscontinuity(
|
|
ON::continuity::Gsmooth_continuous,
|
|
t0,t1,t,
|
|
0,0,
|
|
ON_DEFAULT_ANGLE_TOLERANCE_COSINE,
|
|
ON_SQRT_EPSILON
|
|
);
|
|
}
|
|
|
|
|
|
bool ON_Extrusion::ProfileIsKinked( int profile_index ) const
|
|
{
|
|
const ON_Curve* profile = Profile(profile_index);
|
|
if ( 0 == profile )
|
|
return false;
|
|
double t0 = ON_UNSET_VALUE;
|
|
double t1 = ON_UNSET_VALUE;
|
|
double t;
|
|
if ( !profile->GetDomain(&t0,&t1) )
|
|
return 0;
|
|
if ( !ON_IsValid(t0) || !(t0 < t1) )
|
|
return 0;
|
|
t = t0;
|
|
return (GetNextProfileSegmentDiscontinuity( profile, t0,t1, &t) && t0 < t && t < t1);
|
|
}
|
|
|
|
int ON_Extrusion::ProfileSmoothSegmentCount( int profile_index ) const
|
|
{
|
|
if ( 0 == Profile(profile_index) )
|
|
return 0;
|
|
return (1 + GetProfileKinkParameters(profile_index,nullptr));
|
|
}
|
|
|
|
int ON_Extrusion::GetProfileKinkParameters( int profile_index, ON_SimpleArray<double>& profile_kink_parameters ) const
|
|
{
|
|
return GetProfileKinkParameters(profile_index, &profile_kink_parameters);
|
|
}
|
|
|
|
int ON_Extrusion::GetProfileKinkParameters( int profile_index, ON_SimpleArray<double>* profile_kink_parameters_ptr ) const
|
|
{
|
|
const ON_Curve* profile2d = Profile(profile_index);
|
|
if ( 0 == profile2d )
|
|
return 0;
|
|
double t0 = ON_UNSET_VALUE;
|
|
double t1 = ON_UNSET_VALUE;
|
|
double t;
|
|
if ( !profile2d->GetDomain(&t0,&t1) )
|
|
return 0;
|
|
if ( !ON_IsValid(t0) || !(t0 < t1) )
|
|
return 0;
|
|
int count = 0;
|
|
while ( GetNextProfileSegmentDiscontinuity( profile2d, t0,t1, &t) )
|
|
{
|
|
if ( t0 < t && t < t1 )
|
|
{
|
|
if ( nullptr != profile_kink_parameters_ptr )
|
|
{
|
|
profile_kink_parameters_ptr->Append(t);
|
|
}
|
|
t0 = t;
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
ON_Brep* ON_Extrusion::BrepForm(ON_Brep* brep) const
|
|
{
|
|
return BrepForm(brep, true);
|
|
}
|
|
|
|
static int GetSmoothSideCount(const ON_Extrusion& E)
|
|
|
|
{
|
|
ON_SimpleArray<const ON_Curve*> profile_curves;
|
|
const int profile_count = E.GetProfileCurves(profile_curves );
|
|
if ( profile_count < 1 || profile_count != profile_curves.Count() )
|
|
return profile_count;
|
|
int side_count = 0;
|
|
for ( int profile_index = 0; profile_index < profile_count; profile_index++ ){
|
|
const ON_Curve* profile_segment = profile_curves[profile_index];
|
|
double t0 = ON_UNSET_VALUE;
|
|
double t1 = ON_UNSET_VALUE;
|
|
if ( !profile_segment->GetDomain(&t0,&t1)
|
|
|| !ON_IsValid(t0)
|
|
|| !ON_IsValid(t1)
|
|
|| !(t0 < t1)
|
|
)
|
|
return profile_count;
|
|
double t = t1;
|
|
while ( GetNextProfileSegmentDiscontinuity(profile_segment,t0,t1,&t) ){
|
|
if ( t0 < t && t < t1 ){
|
|
side_count++;
|
|
t0 = t;
|
|
t = t1;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
side_count++;
|
|
}
|
|
return side_count;
|
|
}
|
|
|
|
ON_Brep* ON_Extrusion::BrepForm( ON_Brep* brep, bool bSmoothFaces ) const
|
|
{
|
|
if ( brep )
|
|
brep->Destroy();
|
|
|
|
ON_SimpleArray<const ON_Curve*> profile_curves;
|
|
const int profile_count = GetProfileCurves(profile_curves );
|
|
if ( profile_count < 1 || profile_count != profile_curves.Count() )
|
|
{
|
|
ON_ERROR("extrusion has no profile curve.");
|
|
return 0;
|
|
}
|
|
|
|
// get end cap transformation information
|
|
|
|
const ON_3dVector T = m_path.Tangent();
|
|
if ( !T.IsUnitVector() )
|
|
{
|
|
ON_ERROR("extrusion direction is zero.");
|
|
return 0;
|
|
}
|
|
|
|
ON_Xform
|
|
xform0(ON_Xform::IdentityTransformation),
|
|
xform1(ON_Xform::IdentityTransformation),
|
|
scale0(ON_Xform::IdentityTransformation),
|
|
scale1(ON_Xform::IdentityTransformation),
|
|
rot0(ON_Xform::IdentityTransformation),
|
|
rot1(ON_Xform::IdentityTransformation);
|
|
if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,&scale0,&rot0) )
|
|
{
|
|
ON_ERROR("Unable to get bottom cap location.");
|
|
return 0;
|
|
}
|
|
|
|
if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,&scale1,&rot1) )
|
|
{
|
|
ON_ERROR("Unable to get top cap location.");
|
|
return 0;
|
|
}
|
|
|
|
ON_Brep* newbrep = brep ? brep : ON_Brep::New();
|
|
|
|
if ( 0 == newbrep )
|
|
{
|
|
ON_ERROR("Unable to get allocate brep.");
|
|
return 0;
|
|
}
|
|
|
|
int is_capped = IsCapped();
|
|
if ( is_capped < 0 || is_capped > 3 )
|
|
is_capped = 0; // don't let future changes or bugs in IsCapped() crash this code.
|
|
|
|
int cap_count = (3==is_capped) ? 2 : (0 != is_capped ? 1 : 0);
|
|
|
|
//27 Nov 2018 - Chuck - if a kinky profile gets split up,
|
|
//there will be more than profile_count side faces. See RH-49655.
|
|
int side_count = (bSmoothFaces ) ? GetSmoothSideCount(*this) : profile_count;
|
|
|
|
newbrep->m_S.Reserve(side_count + cap_count);
|
|
newbrep->m_F.Reserve(side_count + cap_count);
|
|
newbrep->m_L.Reserve((1 + cap_count)*side_count);
|
|
|
|
// Note:
|
|
// If the profiles have kinks and bSplitKinkyFaces
|
|
// is true, then the m_C2, m_T, m_C3 and m_E arrays will
|
|
// generally be longer than these initial reservations.
|
|
newbrep->m_C2.Reserve((4 + cap_count)*side_count);
|
|
newbrep->m_T.Reserve((4 + cap_count)*side_count);
|
|
newbrep->m_C3.Reserve(4*side_count);
|
|
newbrep->m_E.Reserve(4*side_count);
|
|
// 3 Feb 2023, Mikko, RH-72784:
|
|
// Initialize vertex array size to avoid slow reallocs
|
|
newbrep->m_V.Reserve(2 * side_count);
|
|
|
|
int vidmap[4] = {0,1,2,3};
|
|
int eidmap[4] = {0,1,2,3};
|
|
if ( m_bTransposed )
|
|
{
|
|
vidmap[1] = 3;
|
|
vidmap[3] = 1;
|
|
eidmap[0] = 3;
|
|
eidmap[1] = 2;
|
|
eidmap[2] = 1;
|
|
eidmap[3] = 0;
|
|
}
|
|
|
|
int fi0, fi1;
|
|
|
|
ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo > finfo(2*profile_count);
|
|
for ( int profile_index = 0; profile_index < profile_count; profile_index++ )
|
|
{
|
|
const ON_Curve* profile_segment = profile_curves[profile_index];
|
|
if ( 0 == profile_segment )
|
|
{
|
|
ON_ERROR("null profile segment.");
|
|
if (newbrep != brep )
|
|
delete newbrep;
|
|
return 0;
|
|
}
|
|
|
|
fi0 = finfo.Count();
|
|
ON_Extrusion_BrepForm_FaceInfo profile_fi;
|
|
profile_fi.Init();
|
|
{
|
|
ON_Curve* newprofile = profile_segment->DuplicateCurve();
|
|
if ( 0 == newprofile )
|
|
{
|
|
ON_ERROR("unable to duplicate profile segment.");
|
|
if (newbrep != brep )
|
|
delete newbrep;
|
|
return 0;
|
|
}
|
|
|
|
|
|
profile_fi.m_bClosedProfile = newprofile->IsClosed() ? true : false;
|
|
profile_fi.m_profile_orientation = ( profile_fi.m_bClosedProfile )
|
|
? ON_ClosedCurveOrientation(*newprofile,0)
|
|
: 0;
|
|
|
|
if ( is_capped && profile_fi.m_profile_orientation != ((0==profile_index) ? 1 : -1) )
|
|
{
|
|
// do not attempt to cap extrusions with incorrectly oriented profiles
|
|
is_capped = 0;
|
|
cap_count = 0;
|
|
}
|
|
|
|
if ( bSmoothFaces )
|
|
{
|
|
// It is important to use the original profile, "profile_segment"
|
|
// and not the trimmed copy in the GetNextDiscontinuity() loop
|
|
// so the code that creates this brep divides the extrusion profile
|
|
// exactly the same way as the code that calculates component indices.
|
|
double t0 = ON_UNSET_VALUE;
|
|
double t1 = ON_UNSET_VALUE;
|
|
if ( !profile_segment->GetDomain(&t0,&t1)
|
|
|| !ON_IsValid(t0)
|
|
|| !ON_IsValid(t1)
|
|
|| !(t0 < t1)
|
|
)
|
|
{
|
|
ON_ERROR("Corrupt profile domain.");
|
|
delete newprofile;
|
|
if (newbrep != brep )
|
|
delete newbrep;
|
|
return 0;
|
|
}
|
|
double t = t1;
|
|
while ( GetNextProfileSegmentDiscontinuity(profile_segment,t0,t1,&t) )
|
|
{
|
|
if ( t0 < t && t < t1 )
|
|
{
|
|
ON_Curve* left_side = 0;
|
|
ON_Curve* right_side = 0;
|
|
if ( newprofile->Split(t,left_side,right_side)
|
|
&& 0 != left_side
|
|
&& 0 != right_side
|
|
)
|
|
{
|
|
finfo.AppendNew().m_extrusion_profile = left_side;
|
|
left_side = 0;
|
|
delete newprofile;
|
|
newprofile = right_side;
|
|
right_side = 0;
|
|
t0 = t;
|
|
t = t1;
|
|
continue;
|
|
}
|
|
if ( 0 != left_side )
|
|
delete left_side;
|
|
if ( 0 != right_side )
|
|
delete right_side;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
finfo.AppendNew().m_extrusion_profile = newprofile;
|
|
}
|
|
|
|
fi1 = finfo.Count();
|
|
|
|
if ( fi1 <= fi0 )
|
|
{
|
|
ON_ERROR("Corrupt extrusion cannot produce brep form.");
|
|
if (newbrep != brep )
|
|
delete newbrep;
|
|
return 0;
|
|
}
|
|
|
|
for ( int finfo_index = fi0; finfo_index < fi1; finfo_index++ )
|
|
{
|
|
// create an extrusion that represents a single surface
|
|
// to store in the brep.
|
|
ON_Extrusion_BrepForm_FaceInfo& fi = finfo[finfo_index];
|
|
fi.m_face_index = -1;
|
|
|
|
fi.m_extrusion_srf = new ON_Extrusion();
|
|
fi.m_extrusion_srf->m_path = m_path;
|
|
fi.m_extrusion_srf->m_t = m_t;
|
|
fi.m_extrusion_srf->m_up = m_up;
|
|
fi.m_extrusion_srf->m_profile_count = 1;
|
|
fi.m_extrusion_srf->m_profile = fi.m_extrusion_profile;
|
|
fi.m_extrusion_srf->m_bCap[0] = false;
|
|
fi.m_extrusion_srf->m_bCap[1] = false;
|
|
fi.m_extrusion_srf->m_bHaveN[0] = m_bHaveN[0];
|
|
fi.m_extrusion_srf->m_bHaveN[1] = m_bHaveN[1];
|
|
fi.m_extrusion_srf->m_N[0] = m_N[0];
|
|
fi.m_extrusion_srf->m_N[1] = m_N[1];
|
|
fi.m_extrusion_srf->m_path_domain = m_path_domain;
|
|
fi.m_extrusion_srf->m_bTransposed = m_bTransposed;
|
|
|
|
fi.m_profile_index = profile_index;
|
|
fi.m_bClosedProfile = profile_fi.m_bClosedProfile;
|
|
fi.m_profile_orientation = profile_fi.m_profile_orientation;
|
|
|
|
// When a profile has multiple profile_list[] entries,
|
|
// the west side of the "next" face is joined to
|
|
// the east side of the "previous" face.
|
|
fi.m_vid[vidmap[0]] = profile_fi.m_vid[vidmap[1]];
|
|
fi.m_vid[vidmap[3]] = profile_fi.m_vid[vidmap[2]];
|
|
fi.m_eid[eidmap[3]] = profile_fi.m_eid[eidmap[1]];
|
|
if ( fi.m_eid[eidmap[3]] >= 0 )
|
|
fi.m_bRev3d[eidmap[3]] = (profile_fi.m_bRev3d[eidmap[1]]) ? false : true;
|
|
|
|
if ( profile_fi.m_bClosedProfile
|
|
&& finfo_index > fi0
|
|
&& finfo_index == fi1 - 1
|
|
)
|
|
{
|
|
// When a profile is closed and there are multiple
|
|
// profile_list[] entries, the east side of the
|
|
// last face is joined to the west side of the
|
|
// first face.
|
|
fi.m_vid[vidmap[1]] = profile_fi.m_vid[vidmap[0]];
|
|
fi.m_vid[vidmap[2]] = profile_fi.m_vid[vidmap[3]];
|
|
fi.m_eid[eidmap[1]] = profile_fi.m_eid[eidmap[3]];
|
|
if ( fi.m_eid[eidmap[1]] >= 0 )
|
|
fi.m_bRev3d[eidmap[1]] = (profile_fi.m_bRev3d[eidmap[3]]) ? false : true;
|
|
}
|
|
|
|
// Create a face topology around the surface fi.m_extrusion_srf
|
|
ON_NurbsSurface* face_srf = fi.m_extrusion_srf->NurbsSurface();
|
|
if ( 0 == face_srf )
|
|
{
|
|
continue;
|
|
////if (newbrep != brep )
|
|
//// delete newbrep;
|
|
////return 0;
|
|
}
|
|
|
|
const ON_BrepFace* face = newbrep->NewFace(face_srf,fi.m_vid,fi.m_eid,fi.m_bRev3d);
|
|
if ( 0 == face )
|
|
{
|
|
// When NewFace() returns null, face_srf is not reference in newbrep.
|
|
delete face_srf;
|
|
|
|
// 10 July 2012 Dale Lear
|
|
// Continue to attempt to get a brep form. This failure
|
|
// typically happens when face_srf has two singularities
|
|
// because a profile component is very short.
|
|
continue;
|
|
////if (newbrep != brep )
|
|
//// delete newbrep;
|
|
////return 0;
|
|
}
|
|
fi.m_face_index = face->m_face_index;
|
|
|
|
// Save vid[] and eid[] informtion so subsequent faces
|
|
// are properly joined.
|
|
if ( finfo_index == fi0 )
|
|
{
|
|
profile_fi.m_vid[vidmap[0]] = fi.m_vid[vidmap[0]];
|
|
profile_fi.m_vid[vidmap[3]] = fi.m_vid[vidmap[3]];
|
|
profile_fi.m_eid[eidmap[3]] = fi.m_eid[eidmap[3]];
|
|
profile_fi.m_bRev3d[eidmap[3]] = fi.m_bRev3d[eidmap[3]];
|
|
}
|
|
profile_fi.m_vid[vidmap[1]] = fi.m_vid[vidmap[1]];
|
|
profile_fi.m_vid[vidmap[2]] = fi.m_vid[vidmap[2]];
|
|
profile_fi.m_eid[eidmap[1]] = fi.m_eid[eidmap[1]];
|
|
profile_fi.m_bRev3d[eidmap[1]] = fi.m_bRev3d[eidmap[1]];
|
|
|
|
if ( 0 == is_capped )
|
|
continue;
|
|
|
|
const ON_BrepLoop* loop = (1 == face->LoopCount()) ? face->OuterLoop() : 0;
|
|
if ( 0 == loop || 4 != loop->TrimCount() )
|
|
{
|
|
is_capped = 0;
|
|
cap_count = 0;
|
|
continue;
|
|
}
|
|
|
|
const ON_BrepTrim* bottom_trim = loop->Trim(eidmap[0]);
|
|
if ( 0 == bottom_trim || ON_BrepTrim::boundary != bottom_trim->m_type )
|
|
{
|
|
is_capped = 0;
|
|
cap_count = 0;
|
|
continue;
|
|
}
|
|
|
|
const ON_BrepTrim* top_trim = loop->Trim(eidmap[2]);
|
|
if ( 0 == top_trim || ON_BrepTrim::boundary != top_trim->m_type )
|
|
{
|
|
is_capped = 0;
|
|
cap_count = 0;
|
|
continue;
|
|
}
|
|
|
|
const ON_BrepEdge* edge0 = bottom_trim->Edge();
|
|
const ON_BrepEdge* edge1 = top_trim->Edge();
|
|
if ( 0 == edge0 || 0 == edge1 )
|
|
{
|
|
is_capped = 0;
|
|
cap_count = 0;
|
|
continue;
|
|
}
|
|
fi.m_cap_trim_index[0] = bottom_trim->m_trim_index;
|
|
fi.m_cap_trim_index[1] = top_trim->m_trim_index;
|
|
fi.m_cap_edge_index[0] = edge0->m_edge_index;
|
|
fi.m_cap_edge_index[1] = edge1->m_edge_index;
|
|
}
|
|
}
|
|
|
|
// Add end caps
|
|
bool bSetEdgeTolerances = false;
|
|
|
|
while ( is_capped > 0 && cap_count == ((3 == is_capped) ? 2 : 1) )
|
|
{
|
|
// while(...) not a loop
|
|
// - break's are used for flow control and there is a break at the bottom of this scope.
|
|
|
|
// Add end caps.
|
|
if ( !MakeCap2dCurvesHelper(finfo,is_capped,scale0,scale1) )
|
|
break;
|
|
|
|
newbrep->m_S.Reserve(newbrep->m_S.Count()+cap_count);
|
|
newbrep->m_F.Reserve(newbrep->m_F.Count()+cap_count);
|
|
ON_BrepFace* capface0 = 0;
|
|
ON_BrepFace* capface1 = 0;
|
|
{
|
|
ON_PlaneSurface* plane = 0;
|
|
int si;
|
|
if ( 3 == is_capped || 1 == is_capped )
|
|
{
|
|
// create bottom face cap
|
|
plane = MakeCapPlaneHelper( finfo, 0, rot0 );
|
|
if ( plane )
|
|
{
|
|
si = newbrep->AddSurface(plane);
|
|
plane = 0;
|
|
capface0 = &newbrep->NewFace(si);
|
|
capface0->m_bRev = m_bTransposed ? false : true;
|
|
}
|
|
}
|
|
if ( 3 == is_capped || 2 == is_capped )
|
|
{
|
|
// create top face cap
|
|
plane = MakeCapPlaneHelper( finfo, 1, rot1 );
|
|
if ( plane )
|
|
{
|
|
si = newbrep->AddSurface(plane);
|
|
plane = 0;
|
|
capface1 = &newbrep->NewFace(si);
|
|
capface1->m_bRev = m_bTransposed ? true : false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( 0 == capface0 && 0 == capface1 )
|
|
break;
|
|
|
|
newbrep->m_C2.Reserve(newbrep->m_C2.Count()+cap_count*finfo.Count());
|
|
newbrep->m_L.Reserve(newbrep->m_L.Count()+cap_count*profile_count);
|
|
newbrep->m_T.Reserve(newbrep->m_T.Count()+cap_count*finfo.Count());
|
|
|
|
int cap_index = 0;
|
|
for ( cap_index = 0; cap_index < 2; cap_index++ )
|
|
{
|
|
// make all the loops and trims for the bottom cap
|
|
// before making them for the top cap. This makes
|
|
// it easier for the picking code to create ON_Brep
|
|
// component indices when picking the extrusion
|
|
// and the brep index is not present.
|
|
ON_BrepFace* capface = ( 0 == cap_index ) ? capface0 : capface1;
|
|
if ( 0 == capface )
|
|
continue;
|
|
|
|
fi0 = 0;
|
|
int profile_index;
|
|
for ( profile_index = 0; profile_index < profile_count && fi0 < finfo.Count(); profile_index++ )
|
|
{
|
|
if ( finfo[fi0].m_profile_index != profile_index )
|
|
break;
|
|
ON_BrepLoop::TYPE loop_type = ( 0 == profile_index ) ? ON_BrepLoop::outer : ON_BrepLoop::inner;
|
|
fi1 = MakeCapLoopHelper(finfo,fi0,cap_index,capface,loop_type,&bSetEdgeTolerances);
|
|
if ( fi1 <= fi0 )
|
|
break;
|
|
fi0 = fi1;
|
|
}
|
|
|
|
if ( fi0 != finfo.Count() || profile_index != profile_count )
|
|
{
|
|
ON_ERROR("Failed to add caps to extrusion brep form.");
|
|
if ( 0 != capface0 )
|
|
newbrep->DeleteFace(*capface0,false);
|
|
if ( 0 != capface1 )
|
|
newbrep->DeleteFace(*capface1,false);
|
|
newbrep->Compact();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( 2 == cap_index && 2 == cap_count && 3 == is_capped )
|
|
((CMyBrepIsSolidSetter*)newbrep)->SetIsSolid(m_bTransposed?2:1);
|
|
|
|
break;
|
|
}
|
|
|
|
if ( 0 != newbrep )
|
|
{
|
|
if (newbrep->m_F.Count() <= 0 || newbrep->m_E.Count() <= 2)
|
|
{
|
|
ON_ERROR("corrupt extrusion cannot produce a brep form.");
|
|
if (newbrep != brep )
|
|
delete newbrep;
|
|
return 0;
|
|
}
|
|
|
|
// set a tight bounding box
|
|
((CMyBrepIsSolidSetter*)newbrep)->SetBBox(*this);
|
|
newbrep->SetTrimBoundingBoxes(true);
|
|
|
|
if ( bSetEdgeTolerances )
|
|
newbrep->SetEdgeTolerances(true);
|
|
|
|
#if defined(ON_DEBUG)
|
|
if ( !newbrep->IsValid() )
|
|
{
|
|
newbrep->IsValid(); // breakpoint here
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return newbrep;
|
|
}
|
|
|
|
bool ON_Extrusion::GetBrepFormComponentIndex(
|
|
ON_COMPONENT_INDEX extrusion_ci,
|
|
ON_COMPONENT_INDEX& brep_ci
|
|
) const
|
|
{
|
|
return GetBrepFormComponentIndex(extrusion_ci,ON_UNSET_VALUE,nullptr,brep_ci);
|
|
}
|
|
|
|
static bool GetBrepFormFaceIndex(
|
|
const ON_Extrusion& extrusion,
|
|
int extrusion_profile_index,
|
|
double extrusion_profile_parameter,
|
|
bool bCountProfileDiscontinuities,
|
|
int* brep_form_face_index,
|
|
ON_Interval* profile_segment_domain
|
|
)
|
|
{
|
|
// When extrusion_profile_index = profile_count,
|
|
// it means the caller is looking for the index of
|
|
// the bottom cap.
|
|
int profile_segment_count = 0;
|
|
const int profile_count = extrusion.ProfileCount();
|
|
double t0 = ON_UNSET_VALUE;
|
|
double t1 = ON_UNSET_VALUE;
|
|
const ON_Curve* profile;
|
|
if ( ON_UNSET_VALUE == extrusion_profile_parameter )
|
|
{
|
|
if ( extrusion_profile_index != profile_count )
|
|
{
|
|
profile = extrusion.Profile(extrusion_profile_index);
|
|
if ( 0 == profile
|
|
|| !profile->GetDomain(&t0,&t1)
|
|
|| !ON_IsValid(t0)
|
|
|| !ON_IsValid(t1)
|
|
|| !(t0 < t1)
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
profile_segment_count = extrusion_profile_index;
|
|
}
|
|
else
|
|
{
|
|
for ( int i = 0; i < profile_count; i++ )
|
|
{
|
|
profile = extrusion.Profile(i);
|
|
if ( 0 == profile )
|
|
return false;
|
|
// It is important to use the original profile, "profile_segment"
|
|
// and not the trimmed copy in the GetNextDiscontinuity() loop
|
|
// so the code that creates this brep divides the extrusion profile
|
|
// exactly the same way as the code that calculates component indices.
|
|
if ( 0 == profile
|
|
|| !profile->GetDomain(&t0,&t1)
|
|
|| !ON_IsValid(t0)
|
|
|| !ON_IsValid(t1)
|
|
|| !(t0 < t1)
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
double t = t1;
|
|
if ( bCountProfileDiscontinuities )
|
|
{
|
|
while ( GetNextProfileSegmentDiscontinuity(profile,t0,t1,&t) )
|
|
{
|
|
if ( t0 < t && t < t1 )
|
|
{
|
|
if ( i == extrusion_profile_index
|
|
&& t0 <= extrusion_profile_parameter
|
|
&& extrusion_profile_parameter < t
|
|
)
|
|
{
|
|
t1 = t;
|
|
break;
|
|
}
|
|
t0 = t;
|
|
t = t1;
|
|
profile_segment_count++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if ( i == extrusion_profile_index )
|
|
break;
|
|
profile_segment_count++;
|
|
}
|
|
}
|
|
|
|
if ( 0 != brep_form_face_index )
|
|
*brep_form_face_index = profile_segment_count;
|
|
|
|
if ( 0 != profile_segment_domain && extrusion_profile_index < profile_count )
|
|
profile_segment_domain->Set(t0,t1);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ON_Extrusion::GetBrepFormComponentIndex(
|
|
ON_COMPONENT_INDEX extrusion_ci,
|
|
double extrusion_profile_parameter,
|
|
const ON_Brep& brep_form,
|
|
ON_COMPONENT_INDEX& brep_ci
|
|
) const
|
|
{
|
|
return GetBrepFormComponentIndex(
|
|
extrusion_ci,
|
|
extrusion_profile_parameter,
|
|
&brep_form,
|
|
brep_ci
|
|
);
|
|
}
|
|
|
|
bool ON_Extrusion::GetBrepFormComponentIndex(
|
|
ON_COMPONENT_INDEX extrusion_ci,
|
|
double extrusion_profile_parameter,
|
|
const ON_Brep* brep,
|
|
ON_COMPONENT_INDEX& brep_ci
|
|
) const
|
|
{
|
|
return GetBrepFormComponentIndex(
|
|
extrusion_ci,
|
|
extrusion_profile_parameter,
|
|
brep,
|
|
false,
|
|
brep_ci,
|
|
nullptr
|
|
);
|
|
}
|
|
|
|
bool ON_Extrusion::GetBrepFormComponentIndex(
|
|
ON_COMPONENT_INDEX extrusion_ci,
|
|
double extrusion_profile_parameter,
|
|
const ON_Brep* brep,
|
|
bool bSmoothFaces,
|
|
ON_COMPONENT_INDEX& brep_ci,
|
|
ON_Interval* profile_subdomain
|
|
) const
|
|
{
|
|
brep_ci.UnSet();
|
|
int face_index = -1;
|
|
ON_Interval face_profile_domain(ON_UNSET_VALUE,ON_UNSET_VALUE);
|
|
|
|
const int is_capped = IsCapped();
|
|
if ( is_capped < 0 || is_capped > 3 )
|
|
return false;
|
|
const int profile_count = ProfileCount();
|
|
if ( profile_count < 1 )
|
|
return false;
|
|
const ON_Curve* profile0 = Profile(0);
|
|
if ( 0 == profile0 )
|
|
return false;
|
|
const bool bClosedProfile = profile0->IsClosed() ? true : false;
|
|
if ( profile_count > 1 && !bClosedProfile )
|
|
return false;
|
|
const int edges_per_wall_face = 4;
|
|
const int cap_count = (0 == is_capped || !bClosedProfile) ? 0 : ((3==is_capped)?2:1);
|
|
int brep_face_count = ( nullptr != brep ) ? brep->m_F.Count() : 0;
|
|
if ( nullptr != brep && brep_face_count < profile_count + cap_count )
|
|
{
|
|
ON_ERROR("brep_form parameter cannot be extrusion's BrepForm()");
|
|
return false;
|
|
}
|
|
bool bCountProfileDiscontinuities = bSmoothFaces || ( brep_face_count > profile_count + cap_count );
|
|
int side_count = (bCountProfileDiscontinuities) ? GetSmoothSideCount(*this) : profile_count;
|
|
|
|
switch(extrusion_ci.m_type)
|
|
{
|
|
case ON_COMPONENT_INDEX::extrusion_bottom_profile:
|
|
case ON_COMPONENT_INDEX::extrusion_top_profile:
|
|
if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= profile_count )
|
|
return false;
|
|
if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) )
|
|
return false;
|
|
|
|
// first face has edges_per_wall_face edges, the subsequent ones have one less
|
|
if (face_index > 0)
|
|
brep_ci.m_index = edges_per_wall_face + ((edges_per_wall_face-1) * (face_index-1));
|
|
else
|
|
brep_ci.m_index = 0;
|
|
|
|
if (ON_COMPONENT_INDEX::extrusion_top_profile == extrusion_ci.m_type)
|
|
{
|
|
if ( bClosedProfile && face_index == side_count - 1)
|
|
brep_ci.m_index += 1; // in closed case the last face has an already existing edge between bottom and top edge
|
|
else
|
|
brep_ci.m_index += 2;
|
|
}
|
|
brep_ci.m_type = ON_COMPONENT_INDEX::brep_edge;
|
|
if (nullptr != profile_subdomain)
|
|
*profile_subdomain = face_profile_domain;
|
|
break;
|
|
|
|
case ON_COMPONENT_INDEX::extrusion_wall_edge:
|
|
if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= 2*profile_count )
|
|
return false;
|
|
if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index/2, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) )
|
|
return false;
|
|
brep_ci.m_index = edges_per_wall_face*face_index+1;
|
|
if ( !bClosedProfile && 1 == (face_index % 1) )
|
|
brep_ci.m_index += 2;
|
|
brep_ci.m_type = ON_COMPONENT_INDEX::brep_edge;
|
|
break;
|
|
|
|
case ON_COMPONENT_INDEX::extrusion_wall_surface:
|
|
if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= side_count )
|
|
return false;
|
|
if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) )
|
|
return false;
|
|
brep_ci.m_index = face_index;
|
|
brep_ci.m_type = ON_COMPONENT_INDEX::brep_face;
|
|
if (nullptr != profile_subdomain)
|
|
*profile_subdomain = face_profile_domain;
|
|
break;
|
|
|
|
case ON_COMPONENT_INDEX::extrusion_cap_surface:
|
|
if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index > 2 )
|
|
return false;
|
|
if ( 1 == extrusion_ci.m_index && (is_capped != 1 && is_capped != 3) )
|
|
return false;
|
|
if ( 2 == extrusion_ci.m_index && (is_capped != 2 && is_capped != 3) )
|
|
return false;
|
|
if ( 0 != brep )
|
|
{
|
|
face_index = brep->m_F.Count()-cap_count;
|
|
}
|
|
else if ( !GetBrepFormFaceIndex( *this, profile_count, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
brep_ci.m_index = face_index+extrusion_ci.m_index-1;
|
|
brep_ci.m_type = ON_COMPONENT_INDEX::brep_face;
|
|
break;
|
|
|
|
case ON_COMPONENT_INDEX::extrusion_path:
|
|
// There is no corresponding brep component index
|
|
break;
|
|
|
|
default:
|
|
// Other ON_COMPONENT_INDEX::TYPE values intentionally ignored
|
|
break;
|
|
}
|
|
|
|
if ( !brep_ci.IsBrepComponentIndex() )
|
|
{
|
|
brep_ci.UnSet();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ON_Extrusion::SetDomain(
|
|
int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain
|
|
double t0,
|
|
double t1
|
|
)
|
|
{
|
|
bool rc = false;
|
|
if ( ON_IsValid(t0) && ON_IsValid(t1) && t0 < t1 )
|
|
{
|
|
const int path_dir = PathParameter();
|
|
if ( path_dir == dir )
|
|
{
|
|
m_path_domain.Set(t0,t1);
|
|
rc = true;
|
|
}
|
|
else if ( 1-path_dir == dir )
|
|
{
|
|
rc = m_profile->SetDomain(t0,t1)?true:false;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
ON_Interval ON_Extrusion::Domain(
|
|
int dir // 0 gets first parameter's domain, 1 gets second parameter's domain
|
|
) const
|
|
{
|
|
const int path_dir = PathParameter();
|
|
return (path_dir == dir )
|
|
? m_path_domain
|
|
: ((1-path_dir == dir && m_profile) ? m_profile->Domain() : ON_Interval());
|
|
}
|
|
|
|
bool ON_Extrusion::GetSurfaceSize(
|
|
double* width,
|
|
double* height
|
|
) const
|
|
{
|
|
bool rc = true;
|
|
//int path_dir = PathParameter();
|
|
if ( PathParameter() )
|
|
{
|
|
double* p = width;
|
|
width = height;
|
|
height = p;
|
|
}
|
|
if ( width )
|
|
{
|
|
if ( m_path.IsValid() && m_t.IsIncreasing() )
|
|
*width = m_path.Length()*m_t.Length();
|
|
else
|
|
{
|
|
*width = 0.0;
|
|
rc = false;
|
|
}
|
|
}
|
|
if (height)
|
|
{
|
|
if ( m_profile )
|
|
{
|
|
// A crude over estimate is good enough for file IO
|
|
// needs in the public souce code version. When there
|
|
// are multiple profile components, the nurbs_profile_curve
|
|
// will have a whacky control polygon, but it's length will
|
|
// be ok as an estimate.
|
|
ON_NurbsCurve nurbs_profile_curve;
|
|
if ( m_profile->GetNurbForm(nurbs_profile_curve) <= 0 )
|
|
{
|
|
*height = 0.0;
|
|
rc = false;
|
|
}
|
|
else
|
|
{
|
|
*height = nurbs_profile_curve.ControlPolygonLength();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = false;
|
|
*height = 0.0;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int ON_Extrusion::SpanCount(
|
|
int dir // 0 gets first parameter's domain, 1 gets second parameter's domain
|
|
) const // number of smooth nonempty spans in the parameter direction
|
|
{
|
|
const int path_dir = PathParameter();
|
|
if ( path_dir == dir )
|
|
return 1;
|
|
if ( 1-path_dir == dir && m_profile )
|
|
return m_profile->SpanCount();
|
|
return 0;
|
|
}
|
|
|
|
bool ON_Extrusion::GetSpanVector( // span "knots"
|
|
int dir, // 0 gets first parameter's domain, 1 gets second parameter's domain
|
|
double* span_vector // array of length SpanCount() + 1
|
|
) const //
|
|
{
|
|
if ( span_vector )
|
|
{
|
|
const int path_dir = PathParameter();
|
|
if ( path_dir == dir )
|
|
{
|
|
span_vector[0] = m_path_domain[0];
|
|
span_vector[1] = m_path_domain[1];
|
|
return true;
|
|
}
|
|
if ( 1-path_dir == dir && m_profile )
|
|
{
|
|
return m_profile->GetSpanVector(span_vector);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ON_Extrusion::GetSpanVectorIndex(
|
|
int dir , // 0 gets first parameter's domain, 1 gets second parameter's domain
|
|
double t, // [IN] t = evaluation parameter
|
|
int side, // [IN] side 0 = default, -1 = from below, +1 = from above
|
|
int* span_vector_index, // [OUT] span vector index
|
|
ON_Interval* span_interval // [OUT] domain of the span containing "t"
|
|
) const
|
|
{
|
|
const int path_dir = PathParameter();
|
|
if ( path_dir == dir )
|
|
{
|
|
if ( span_vector_index )
|
|
*span_vector_index = 0;
|
|
if ( span_interval )
|
|
*span_interval = m_path_domain;
|
|
return true;
|
|
}
|
|
if ( 1-path_dir == dir && m_profile )
|
|
{
|
|
return m_profile->GetSpanVectorIndex(t,side,span_vector_index,span_interval);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int ON_Extrusion::Degree( // returns maximum algebraic degree of any span
|
|
// ( or a good estimate if curve spans are not algebraic )
|
|
int dir // 0 gets first parameter's domain, 1 gets second parameter's domain
|
|
) const
|
|
{
|
|
const int path_dir = PathParameter();
|
|
if ( path_dir == dir )
|
|
return 1;
|
|
if ( 1-path_dir == dir && m_profile )
|
|
return m_profile->Degree();
|
|
return 0;
|
|
}
|
|
|
|
bool ON_Extrusion::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
|
|
{
|
|
const int path_dir = PathParameter();
|
|
if ( path_dir == dir )
|
|
return ON_Surface::GetParameterTolerance(dir,t,tminus,tplus);
|
|
if ( 1-path_dir==dir && m_profile)
|
|
return m_profile->GetParameterTolerance(t,tminus,tplus);
|
|
return false;
|
|
}
|
|
|
|
ON_Surface::ISO ON_Extrusion::IsIsoparametric(
|
|
const ON_Curve& curve,
|
|
const ON_Interval* curve_domain
|
|
) const
|
|
{
|
|
return ON_Surface::IsIsoparametric(curve,curve_domain);
|
|
}
|
|
|
|
bool ON_Extrusion::IsPlanar(
|
|
ON_Plane* plane,
|
|
double tolerance
|
|
) const
|
|
{
|
|
if ( m_profile && m_profile->IsLinear(tolerance) )
|
|
{
|
|
if ( plane )
|
|
{
|
|
ON_3dPoint P0 = m_profile->PointAtStart();
|
|
ON_3dPoint P1 = m_profile->PointAtEnd();
|
|
ON_3dVector pathT = m_path.Tangent();
|
|
ON_3dVector Y = m_up;
|
|
ON_3dVector X = ON_CrossProduct(Y,pathT);
|
|
if ( !X.IsUnitVector() )
|
|
X.Unitize();
|
|
ON_3dPoint Q0 = m_path.from + P0.x*X + P0.y*Y;
|
|
ON_3dPoint Q1 = m_path.from + P1.x*X + P1.y*Y;
|
|
ON_3dVector N = ON_CrossProduct(pathT,Q1-Q0);
|
|
N.Unitize();
|
|
plane->origin = Q0;
|
|
if ( false == m_bTransposed )
|
|
{
|
|
plane->yaxis = pathT;
|
|
plane->zaxis = -N;
|
|
plane->xaxis = ON_CrossProduct(plane->yaxis,plane->zaxis);
|
|
plane->xaxis.Unitize();
|
|
}
|
|
else
|
|
{
|
|
plane->xaxis = pathT;
|
|
plane->zaxis = N;
|
|
plane->yaxis = ON_CrossProduct(plane->zaxis,plane->xaxis);
|
|
plane->yaxis.Unitize();
|
|
}
|
|
plane->UpdateEquation();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ON_Extrusion::IsClosed(int dir) const
|
|
{
|
|
const int path_dir = PathParameter();
|
|
if ( 1-path_dir == dir && m_profile )
|
|
return m_profile->IsClosed();
|
|
return false;
|
|
}
|
|
|
|
bool ON_Extrusion::IsPeriodic( int dir ) const
|
|
{
|
|
const int path_dir = PathParameter();
|
|
if ( 1-path_dir == dir && m_profile )
|
|
return m_profile->IsPeriodic();
|
|
return false;
|
|
}
|
|
|
|
bool ON_Extrusion::GetNextDiscontinuity(
|
|
int dir,
|
|
ON::continuity c,
|
|
double t0,
|
|
double t1,
|
|
double* t,
|
|
int* hint,
|
|
int* dtype,
|
|
double cos_angle_tolerance,
|
|
double curvature_tolerance
|
|
) const
|
|
{
|
|
const int path_dir = PathParameter();
|
|
if ( path_dir == dir )
|
|
{
|
|
return ON_Surface::GetNextDiscontinuity(dir,c,t0,t1,t,hint,dtype,cos_angle_tolerance,curvature_tolerance);
|
|
}
|
|
if ( 1-path_dir==dir && m_profile)
|
|
{
|
|
return m_profile->GetNextDiscontinuity(c,t0,t1,t,hint,dtype,cos_angle_tolerance,curvature_tolerance);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ON_Extrusion::IsContinuous(
|
|
ON::continuity c,
|
|
double s,
|
|
double t,
|
|
int* hint,
|
|
double point_tolerance,
|
|
double d1_tolerance,
|
|
double d2_tolerance,
|
|
double cos_angle_tolerance,
|
|
double curvature_tolerance
|
|
) const
|
|
{
|
|
if ( !m_profile )
|
|
return false;
|
|
int* crv_hint = 0;
|
|
double curvet;
|
|
if ( m_bTransposed )
|
|
{
|
|
curvet = s;
|
|
crv_hint = hint;
|
|
}
|
|
else
|
|
{
|
|
curvet = t;
|
|
crv_hint = hint ? hint+1 : 0;
|
|
}
|
|
return m_profile->IsContinuous(c,curvet,crv_hint,point_tolerance,d1_tolerance,d2_tolerance,cos_angle_tolerance,curvature_tolerance);
|
|
}
|
|
|
|
ON_Surface::ISO ON_Extrusion::IsIsoparametric(
|
|
const ON_BoundingBox& bbox
|
|
) const
|
|
{
|
|
return ON_Surface::IsIsoparametric(bbox);
|
|
}
|
|
|
|
bool ON_Extrusion::Reverse( int dir )
|
|
{
|
|
if ( 0 == m_profile )
|
|
return false;
|
|
|
|
const int path_dir = PathParameter();
|
|
|
|
if ( path_dir == dir )
|
|
{
|
|
m_path_domain.Reverse();
|
|
m_path.Reverse();
|
|
|
|
// Need to mirror profile about 2d x-axis
|
|
// NOTE:
|
|
// If the profile is closed and the extrusion
|
|
// is capped, then this will leave the extrusion
|
|
// in an invalid "inside-out" state.
|
|
// It is the responsibility of the caller
|
|
// to explicitly reverse the profile as well.
|
|
// I cannot "automatically" do it here because
|
|
// it breaks existing reparamterization code
|
|
// that checks for closed objects and makes
|
|
// a sequence changes that results in a valid
|
|
// final result.
|
|
ON_Xform profile_xform(ON_Xform::IdentityTransformation);
|
|
profile_xform.m_xform[0][0] = -1.0;
|
|
bool bNeedReverse = false;
|
|
return Profile2dTransform(*this,profile_xform,bNeedReverse);
|
|
}
|
|
|
|
if ( 1-path_dir == dir )
|
|
{
|
|
return m_profile->Reverse();
|
|
// NOTE:
|
|
// If the profile is closed and the extrusion
|
|
// is capped, then this will leave the extrusion
|
|
// in an invalid "inside-out" state.
|
|
// It is the responsibility of the caller
|
|
// to explicitly reverse the profile as well.
|
|
// I cannot "automatically" do it here because
|
|
// it breaks existing reparamterization code
|
|
// that checks for closed objects and makes
|
|
// a sequence changes that results in a valid
|
|
// final result.
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ON_Extrusion::Transpose() // transpose surface parameterization (swap "s" and "t")
|
|
{
|
|
m_bTransposed = m_bTransposed?false:true;
|
|
return true;
|
|
}
|
|
|
|
bool ON_Extrusion::Evaluate( // returns false if unable to evaluate
|
|
double u, double v, // evaluation parameters
|
|
int num_der, // number of derivatives (>=0)
|
|
int array_stride, // array stride (>=Dimension())
|
|
double* der_array, // array of length stride*(ndir+1)*(ndir+2)/2
|
|
int quadrant , // 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
|
|
{
|
|
if ( !m_profile )
|
|
return false;
|
|
|
|
double x,y,dx,dy;
|
|
//int side = 0;
|
|
if ( m_bTransposed )
|
|
{
|
|
x = u; u = v; v = x;
|
|
if ( 4 == quadrant )
|
|
quadrant = 2;
|
|
else if ( 2 == quadrant )
|
|
quadrant = 4;
|
|
}
|
|
|
|
if ( !m_profile->Evaluate( u, num_der, array_stride, der_array,
|
|
(1==quadrant||4==quadrant)?1:((2==quadrant||3==quadrant)?-1:0),
|
|
hint)
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// TODO: After testing, add special case that avoids
|
|
// two calls to GetProfileTransformation() when
|
|
// mitering is trivial.
|
|
const double t1 = m_path_domain.NormalizedParameterAt(v);
|
|
const double t0 = 1.0-t1;
|
|
ON_Xform xform0, xform1;
|
|
const ON_3dVector T = m_path.Tangent();
|
|
|
|
if ( 0.0 != t0 || num_der > 0 )
|
|
{
|
|
if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,0,0) )
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
xform0 = ON_Xform::Zero4x4;
|
|
}
|
|
|
|
if ( 0.0 != t1 || num_der > 0 )
|
|
{
|
|
if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,0,0) )
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
xform1 = ON_Xform::Zero4x4;
|
|
}
|
|
|
|
double xformP[3][3], xformD[3][3];
|
|
xformP[0][0] = t0*xform0.m_xform[0][0] + t1*xform1.m_xform[0][0];
|
|
xformP[0][1] = t0*xform0.m_xform[0][1] + t1*xform1.m_xform[0][1];
|
|
xformP[0][2] = t0*xform0.m_xform[0][3] + t1*xform1.m_xform[0][3];
|
|
xformP[1][0] = t0*xform0.m_xform[1][0] + t1*xform1.m_xform[1][0];
|
|
xformP[1][1] = t0*xform0.m_xform[1][1] + t1*xform1.m_xform[1][1];
|
|
xformP[1][2] = t0*xform0.m_xform[1][3] + t1*xform1.m_xform[1][3];
|
|
xformP[2][0] = t0*xform0.m_xform[2][0] + t1*xform1.m_xform[2][0];
|
|
xformP[2][1] = t0*xform0.m_xform[2][1] + t1*xform1.m_xform[2][1];
|
|
xformP[2][2] = t0*xform0.m_xform[2][3] + t1*xform1.m_xform[2][3];
|
|
|
|
int i,j;
|
|
i = num_der+1;
|
|
double* d1 = der_array + array_stride*(i*(i+1)/2 - 1);
|
|
double* d0 = der_array + array_stride*(i - 1);
|
|
x = d0[0];
|
|
y = d0[1];
|
|
if ( num_der > 0 )
|
|
{
|
|
double d = m_path_domain.m_t[1] - m_path_domain.m_t[0];
|
|
if ( d > 0.0 )
|
|
d = 1.0/d;
|
|
xformD[0][0] = d*(xform1.m_xform[0][0] - xform0.m_xform[0][0]);
|
|
xformD[0][1] = d*(xform1.m_xform[0][1] - xform0.m_xform[0][1]);
|
|
xformD[0][2] = d*(xform1.m_xform[0][3] - xform0.m_xform[0][3]);
|
|
xformD[1][0] = d*(xform1.m_xform[1][0] - xform0.m_xform[1][0]);
|
|
xformD[1][1] = d*(xform1.m_xform[1][1] - xform0.m_xform[1][1]);
|
|
xformD[1][2] = d*(xform1.m_xform[1][3] - xform0.m_xform[1][3]);
|
|
xformD[2][0] = d*(xform1.m_xform[2][0] - xform0.m_xform[2][0]);
|
|
xformD[2][1] = d*(xform1.m_xform[2][1] - xform0.m_xform[2][1]);
|
|
xformD[2][2] = d*(xform1.m_xform[2][3] - xform0.m_xform[2][3]);
|
|
|
|
for ( i = num_der; i > 0; i-- )
|
|
{
|
|
dx = x;
|
|
dy = y;
|
|
d0 -= array_stride;
|
|
x = d0[0];
|
|
y = d0[1];
|
|
|
|
// all partials involving two or more derivatives with
|
|
// respect to "v" are zero.
|
|
j = i;
|
|
while ( --j )
|
|
{
|
|
d1[0] = d1[1] = d1[2] = 0.0;
|
|
d1 -= array_stride;
|
|
}
|
|
|
|
// The partial involving a single derivative with respect to "v"
|
|
if ( 1 == i )
|
|
{
|
|
// xformD transform is applied to curve location ((x,y) = point)
|
|
d1[0] = xformD[0][0]*x + xformD[0][1]*y + xformD[0][2];
|
|
d1[1] = xformD[1][0]*x + xformD[1][1]*y + xformD[1][2];
|
|
d1[2] = xformD[2][0]*x + xformD[2][1]*y + xformD[2][2];
|
|
}
|
|
else
|
|
{
|
|
// xformD transform is applied to a curve derivative ((x,y) = vector)
|
|
d1[0] = xformD[0][0]*x + xformD[0][1]*y;
|
|
d1[1] = xformD[1][0]*x + xformD[1][1]*y;
|
|
d1[2] = xformD[2][0]*x + xformD[2][1]*y;
|
|
}
|
|
d1 -= array_stride;
|
|
|
|
// The partial involving a all derivatives with respect to "u"
|
|
// xformP transformation is applied to a curve derivative ((x,y) = vector)
|
|
d1[0] = xformP[0][0]*dx + xformP[0][1]*dy;
|
|
d1[1] = xformP[1][0]*dx + xformP[1][1]*dy;
|
|
d1[2] = xformP[2][0]*dx + xformP[2][1]*dy;
|
|
d1 -= array_stride;
|
|
}
|
|
}
|
|
// xformP transformation is applied curve location ((x,y) = point)
|
|
d1[0] = xformP[0][0]*x + xformP[0][1]*y + xformP[0][2];
|
|
d1[1] = xformP[1][0]*x + xformP[1][1]*y + xformP[1][2];
|
|
d1[2] = xformP[2][0]*x + xformP[2][1]*y + xformP[2][2];
|
|
|
|
if ( m_bTransposed && num_der > 0)
|
|
{
|
|
// reverse order of derivatives
|
|
const size_t sz = ((3 <= array_stride)?3:array_stride)*sizeof(double);
|
|
void* tmp = ( sz <= sizeof(xform0) )
|
|
? ((void*)&xform0.m_xform[0][0])
|
|
: onmalloc(sz);
|
|
for ( i = 1; i <= num_der; i++ )
|
|
{
|
|
d0 = der_array + array_stride*(i*(i+1))/2;
|
|
d1 = d0 + array_stride*i;
|
|
while ( d0 < d1)
|
|
{
|
|
memcpy(tmp,d0,sz);
|
|
memcpy(d0,d1,sz);
|
|
memcpy(d1,tmp,sz);
|
|
d0 += array_stride;
|
|
d1 -= array_stride;
|
|
}
|
|
}
|
|
if ( tmp != ((void*)&xform0.m_xform[0][0]) )
|
|
onfree(tmp);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ON_Curve* ON_Extrusion::IsoCurve(
|
|
int dir,
|
|
double c
|
|
) const
|
|
{
|
|
// dir 0 first parameter varies and second parameter is constant
|
|
// e.g., point on IsoCurve(0,c) at t is srf(t,c)
|
|
// 1 first parameter is constant and second parameter varies
|
|
// e.g., point on IsoCurve(1,c) at t is srf(c,t)
|
|
|
|
if ( !m_profile )
|
|
return 0;
|
|
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
const ON_3dVector T = m_path.Tangent();
|
|
|
|
ON_Xform xform0, xform1;
|
|
if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,0,0) )
|
|
return 0;
|
|
if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,0,0) )
|
|
return 0;
|
|
|
|
ON_Curve* isocurve = 0;
|
|
if ( 1 == dir )
|
|
{
|
|
ON_3dPoint P = m_profile->PointAt(c);
|
|
ON_LineCurve* line_curve = new ON_LineCurve();
|
|
line_curve->m_t = m_path_domain;
|
|
line_curve->m_dim = 3;
|
|
line_curve->m_line.from = xform0*P;
|
|
line_curve->m_line.to = xform1*P;
|
|
isocurve = line_curve;
|
|
}
|
|
else if ( 0 == dir )
|
|
{
|
|
double s1 = m_path_domain.NormalizedParameterAt(c);
|
|
const double s0 = 1.0-s1;
|
|
xform1.m_xform[0][0] = s0*xform0.m_xform[0][0] + s1*xform1.m_xform[0][0];
|
|
xform1.m_xform[0][1] = s0*xform0.m_xform[0][1] + s1*xform1.m_xform[0][1];
|
|
xform1.m_xform[0][2] = s0*xform0.m_xform[0][2] + s1*xform1.m_xform[0][2];
|
|
xform1.m_xform[0][3] = s0*xform0.m_xform[0][3] + s1*xform1.m_xform[0][3];
|
|
|
|
xform1.m_xform[1][0] = s0*xform0.m_xform[1][0] + s1*xform1.m_xform[1][0];
|
|
xform1.m_xform[1][1] = s0*xform0.m_xform[1][1] + s1*xform1.m_xform[1][1];
|
|
xform1.m_xform[1][2] = s0*xform0.m_xform[1][2] + s1*xform1.m_xform[1][2];
|
|
xform1.m_xform[1][3] = s0*xform0.m_xform[1][3] + s1*xform1.m_xform[1][3];
|
|
|
|
xform1.m_xform[2][0] = s0*xform0.m_xform[2][0] + s1*xform1.m_xform[2][0];
|
|
xform1.m_xform[2][1] = s0*xform0.m_xform[2][1] + s1*xform1.m_xform[2][1];
|
|
xform1.m_xform[2][2] = s0*xform0.m_xform[2][2] + s1*xform1.m_xform[2][2];
|
|
xform1.m_xform[2][3] = s0*xform0.m_xform[2][3] + s1*xform1.m_xform[2][3];
|
|
|
|
xform1.m_xform[3][0] = s0*xform0.m_xform[3][0] + s1*xform1.m_xform[3][0];
|
|
xform1.m_xform[3][1] = s0*xform0.m_xform[3][1] + s1*xform1.m_xform[3][1];
|
|
xform1.m_xform[3][2] = s0*xform0.m_xform[3][2] + s1*xform1.m_xform[3][2];
|
|
xform1.m_xform[3][3] = s0*xform0.m_xform[3][3] + s1*xform1.m_xform[3][3];
|
|
|
|
isocurve = m_profile->DuplicateCurve();
|
|
if ( isocurve )
|
|
{
|
|
isocurve->ChangeDimension(3);
|
|
if ( !isocurve->Transform(xform1) )
|
|
{
|
|
// isocurve is probably a circle
|
|
ON_NurbsCurve* nurbs_curve = isocurve->NurbsCurve();
|
|
delete isocurve;
|
|
isocurve = nurbs_curve;
|
|
nurbs_curve = 0;
|
|
if ( isocurve )
|
|
isocurve->Transform(xform1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return isocurve;
|
|
}
|
|
|
|
bool ON_Extrusion::Trim(
|
|
int dir,
|
|
const ON_Interval& domain
|
|
)
|
|
{
|
|
bool rc = false;
|
|
if (!domain.IsIncreasing())
|
|
return false;
|
|
if ( m_bTransposed )
|
|
dir = 1-dir;
|
|
if ( 1 == dir )
|
|
{
|
|
rc = m_path_domain.IsIncreasing();
|
|
if ( rc && m_path_domain != domain )
|
|
{
|
|
ON_Interval dom;
|
|
dom.Intersection(domain,m_path_domain);
|
|
rc = dom.IsIncreasing();
|
|
if (rc)
|
|
{
|
|
double s0 = m_path_domain.NormalizedParameterAt(dom[0]);
|
|
double s1 = m_path_domain.NormalizedParameterAt(dom[1]);
|
|
double t0 = (1.0-s0)*m_t[0] + s0*m_t[1];
|
|
double t1 = (1.0-s1)*m_t[0] + s1*m_t[1];
|
|
rc = (s0 < s1 && 0.0 <= t0 && t0 < t1 && t1 <= 1.0);
|
|
if (rc)
|
|
{
|
|
bool bChanged = false;
|
|
if (t0 != m_t[0] && t0 > 0.0 )
|
|
{
|
|
bChanged = true;
|
|
m_t[0] = t0;
|
|
m_bHaveN[0] = false;
|
|
}
|
|
if ( t1 != m_t[1] && t1 < 1.0 )
|
|
{
|
|
bChanged = true;
|
|
m_t[1] = t1;
|
|
m_bHaveN[1] = false;
|
|
}
|
|
if ( bChanged )
|
|
{
|
|
m_path_domain = dom;
|
|
DestroySurfaceTree();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( 0 == dir )
|
|
{
|
|
if ( m_profile )
|
|
{
|
|
rc = m_profile->Trim(domain)?true:false;
|
|
DestroySurfaceTree();
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Extrusion::Extend(
|
|
int dir,
|
|
const ON_Interval& domain
|
|
)
|
|
{
|
|
bool rc = false;
|
|
if ( 1 == dir )
|
|
{
|
|
rc = domain.IsIncreasing() && m_path_domain.IsIncreasing();
|
|
if ( rc )
|
|
{
|
|
double s0 = m_path_domain.NormalizedParameterAt(domain[0]);
|
|
if ( s0 > 0.0 )
|
|
s0 = 0.0;
|
|
double s1 = m_path_domain.NormalizedParameterAt(domain[1]);
|
|
if ( s1 < 1.0 )
|
|
s1 = 1.0;
|
|
double t0 = (1.0-s0)*m_t[0] + s0*m_t[1];
|
|
double t1 = (1.0-s1)*m_t[0] + s1*m_t[1];
|
|
bool bChanged = false;
|
|
ON_3dPoint P0 = m_path.from;
|
|
ON_3dPoint P1 = m_path.to;
|
|
if ( t0 < m_t[0] )
|
|
{
|
|
bChanged = true;
|
|
m_path_domain.m_t[0] = domain[0];
|
|
if ( t0 < 0.0 )
|
|
{
|
|
P0 = m_path.PointAt(t0);
|
|
m_t[0] = 0.0;
|
|
}
|
|
else
|
|
m_t[0] = t0;
|
|
}
|
|
if ( t1 > m_t[1] )
|
|
{
|
|
bChanged = true;
|
|
m_path_domain.m_t[1] = domain[1];
|
|
if ( t1 > 1.0 )
|
|
{
|
|
P1 = m_path.PointAt(t1);
|
|
m_t[1] = 1.0;
|
|
}
|
|
else
|
|
m_t[1] = t1;
|
|
}
|
|
if ( bChanged )
|
|
{
|
|
m_path.from = P0;
|
|
m_path.to = P1;
|
|
DestroySurfaceTree();
|
|
}
|
|
}
|
|
}
|
|
else if ( 0 == dir )
|
|
{
|
|
if ( m_profile )
|
|
{
|
|
rc = m_profile->Extend(domain);
|
|
if (rc)
|
|
DestroySurfaceTree();
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Extrusion::Split(
|
|
int dir,
|
|
double c,
|
|
ON_Surface*& west_or_south_side,
|
|
ON_Surface*& east_or_north_side
|
|
) const
|
|
{
|
|
if ( dir < 0 || dir > 1 || !ON_IsValid(c) )
|
|
return false;
|
|
if ( 0 != west_or_south_side && west_or_south_side == east_or_north_side )
|
|
return false;
|
|
|
|
ON_Interval domain = Domain(dir);
|
|
double s = domain.NormalizedParameterAt(c);
|
|
if ( s <= 0.0 || s >= 1.0 )
|
|
return false;
|
|
if (c <= domain[0] || c >= domain[1] )
|
|
return false;
|
|
|
|
ON_Extrusion* left = 0;
|
|
ON_Extrusion* right = 0;
|
|
if ( west_or_south_side )
|
|
{
|
|
left = ON_Extrusion::Cast(west_or_south_side);
|
|
if ( !left )
|
|
return false;
|
|
}
|
|
if ( east_or_north_side )
|
|
{
|
|
right = ON_Extrusion::Cast(east_or_north_side);
|
|
if ( !right )
|
|
return false;
|
|
}
|
|
|
|
const int path_dir = PathParameter();
|
|
bool rc = false;
|
|
if ( dir == path_dir )
|
|
{
|
|
// split path
|
|
ON_Line left_path, right_path;
|
|
ON_Interval left_domain, right_domain;
|
|
ON_Interval left_t, right_t;
|
|
|
|
const double t0 = m_t[0];
|
|
const double t1 = m_t[1];
|
|
const double t = (1.0-s)*t0 + s*t1;
|
|
if ( !ON_IsValid(t) || t <= t0 || t >= t1 )
|
|
return false;
|
|
|
|
ON_3dPoint P = m_path.PointAt(s);
|
|
left_path.from = m_path.from;
|
|
left_path.to = P;
|
|
right_path.from = P;
|
|
right_path.to = m_path.to;
|
|
left_domain.Set(domain[0],c);
|
|
right_domain.Set(c,domain[1]);
|
|
left_t.Set(t0,t);
|
|
right_t.Set(t,t1);
|
|
if ( !left_path.IsValid() || left_path.Length() <= m_path_length_min )
|
|
return false;
|
|
if ( !right_path.IsValid() || right_path.Length() <= m_path_length_min )
|
|
return false;
|
|
|
|
// return result
|
|
if ( !left )
|
|
left = new ON_Extrusion(*this);
|
|
else if ( left != this )
|
|
left->operator =(*this);
|
|
else
|
|
left->DestroyRuntimeCache();
|
|
if ( !right )
|
|
right = new ON_Extrusion(*this);
|
|
else if ( right != this )
|
|
right->operator =(*this);
|
|
else
|
|
right->DestroyRuntimeCache();
|
|
left->m_path = left_path;
|
|
left->m_path_domain = left_domain;
|
|
left->m_t = left_t;
|
|
right->m_path = right_path;
|
|
right->m_path_domain = right_domain;
|
|
right->m_t = right_t;
|
|
|
|
west_or_south_side = left;
|
|
east_or_north_side = right;
|
|
rc = true;
|
|
}
|
|
else
|
|
{
|
|
if ( 0 == m_profile )
|
|
return false;
|
|
ON_Curve* left_profile = 0;
|
|
ON_Curve* right_profile = 0;
|
|
|
|
if ( left == this )
|
|
{
|
|
left_profile = left->m_profile;
|
|
left->DestroyRuntimeCache();
|
|
}
|
|
else if ( 0 != left && 0 != left->m_profile )
|
|
{
|
|
delete left->m_profile;
|
|
left->m_profile = 0;
|
|
}
|
|
|
|
if ( right == this )
|
|
{
|
|
right_profile = right->m_profile;
|
|
right->DestroyRuntimeCache();
|
|
}
|
|
else if ( 0 != right && 0 != right->m_profile )
|
|
{
|
|
delete right->m_profile;
|
|
right->m_profile = 0;
|
|
}
|
|
|
|
if ( !m_profile->Split(c,left_profile,right_profile) )
|
|
return false;
|
|
if ( 0 == left_profile || 0 == right_profile )
|
|
{
|
|
if ( 0 != left_profile && m_profile != left_profile )
|
|
delete left_profile;
|
|
if ( 0 != right_profile && m_profile != right_profile )
|
|
delete right_profile;
|
|
return false;
|
|
}
|
|
|
|
ON_Curve* this_profile = 0;
|
|
if ( left_profile != m_profile && right_profile != m_profile )
|
|
{
|
|
if ( left == this || right == this )
|
|
{
|
|
delete m_profile;
|
|
}
|
|
else
|
|
{
|
|
this_profile = m_profile;
|
|
}
|
|
}
|
|
|
|
// Prevent this m_profile from being copied
|
|
const_cast<ON_Extrusion*>(this)->m_profile = 0;
|
|
|
|
// Create new left and right sides with nullptr profiles
|
|
if ( !left )
|
|
left = new ON_Extrusion(*this);
|
|
else if ( left != this )
|
|
left->operator =(*this);
|
|
if ( !right )
|
|
right = new ON_Extrusion(*this);
|
|
else if ( right != this )
|
|
right->operator =(*this);
|
|
|
|
// Restore this m_profile
|
|
const_cast<ON_Extrusion*>(this)->m_profile = this_profile;
|
|
|
|
// Set left and right profiles
|
|
left->m_profile = left_profile;
|
|
right->m_profile = right_profile;
|
|
|
|
west_or_south_side = left;
|
|
east_or_north_side = right;
|
|
rc = true;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
//bool ON_Extrusion::GetLocalClosestPoint( const ON_3dPoint&, // test_point
|
|
// double,double, // seed_parameters
|
|
// double*,double*, // parameters of local closest point returned here
|
|
// const ON_Interval* = nullptr, // first parameter sub_domain
|
|
// const ON_Interval* = nullptr // second parameter sub_domain
|
|
// ) const
|
|
//ON_Surface* ON_Extrusion::Offset(
|
|
// double offset_distance,
|
|
// double tolerance,
|
|
// double* max_deviation = nullptr
|
|
// ) const
|
|
int ON_Extrusion::GetNurbForm(
|
|
ON_NurbsSurface& nurbs_surface,
|
|
double tolerance
|
|
) const
|
|
{
|
|
if ( !m_profile )
|
|
return 0;
|
|
|
|
ON_Xform xform0,xform1;
|
|
if ( !GetProfileTransformation(0,xform0) )
|
|
return false;
|
|
if ( !GetProfileTransformation(1,xform1) )
|
|
return false;
|
|
|
|
ON_NurbsCurve nc0;
|
|
int rc = m_profile->GetNurbForm(nc0,tolerance);
|
|
if ( rc <= 0 )
|
|
return rc;
|
|
if ( 3 != nc0.m_dim )
|
|
nc0.ChangeDimension(3);
|
|
ON_NurbsCurve nc1 = nc0;
|
|
nc0.Transform(xform0);
|
|
nc1.Transform(xform1);
|
|
|
|
nurbs_surface.Create(3,nc0.m_is_rat,nc0.m_order,2,nc0.m_cv_count,2);
|
|
memcpy(nurbs_surface.m_knot[0],nc0.m_knot,nurbs_surface.KnotCount(0)*sizeof(nurbs_surface.m_knot[0][0]));
|
|
nurbs_surface.m_knot[1][0] = m_path_domain[0];
|
|
nurbs_surface.m_knot[1][1] = m_path_domain[1];
|
|
for ( int i = 0; i < nurbs_surface.m_cv_count[0]; i++ )
|
|
{
|
|
nurbs_surface.SetCV(i,0,ON::intrinsic_point_style,nc0.CV(i));
|
|
nurbs_surface.SetCV(i,1,ON::intrinsic_point_style,nc1.CV(i));
|
|
}
|
|
|
|
if ( m_bTransposed )
|
|
nurbs_surface.Transpose();
|
|
|
|
return rc;
|
|
}
|
|
|
|
int ON_Extrusion::HasNurbForm() const
|
|
{
|
|
return m_profile ? m_profile->HasNurbForm() : 0;
|
|
}
|
|
|
|
bool ON_Extrusion::GetSurfaceParameterFromNurbFormParameter(
|
|
double nurbs_s, double nurbs_t,
|
|
double* surface_s, double* surface_t
|
|
) const
|
|
{
|
|
bool rc = true;
|
|
if ( m_bTransposed )
|
|
{
|
|
double* p = surface_s;
|
|
surface_s = surface_t;
|
|
surface_t = p;
|
|
double t = nurbs_s;
|
|
nurbs_s = nurbs_t;
|
|
nurbs_t = t;
|
|
}
|
|
if ( surface_s )
|
|
{
|
|
rc = m_profile
|
|
? (m_profile->GetCurveParameterFromNurbFormParameter(nurbs_s,surface_s)?true:false)
|
|
: false;
|
|
}
|
|
if ( surface_t )
|
|
*surface_t = nurbs_t;
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Extrusion::GetNurbFormParameterFromSurfaceParameter(
|
|
double surface_s, double surface_t,
|
|
double* nurbs_s, double* nurbs_t
|
|
) const
|
|
{
|
|
bool rc = true;
|
|
if ( m_bTransposed )
|
|
{
|
|
double p = surface_s;
|
|
surface_s = surface_t;
|
|
surface_t = p;
|
|
double* t = nurbs_s;
|
|
nurbs_s = nurbs_t;
|
|
nurbs_t = t;
|
|
}
|
|
if ( nurbs_s )
|
|
{
|
|
rc = m_profile
|
|
? (m_profile->GetNurbFormParameterFromCurveParameter(surface_s,nurbs_s)?true:false)
|
|
: false;
|
|
}
|
|
if ( nurbs_t )
|
|
*nurbs_t = surface_t;
|
|
return rc;
|
|
}
|
|
|
|
ON_SumSurface* ON_Extrusion::SumSurfaceForm(
|
|
ON_SumSurface* sum_surface
|
|
) const
|
|
{
|
|
int i;
|
|
if ( 0 != sum_surface )
|
|
{
|
|
for ( i = 0; i < 2; i++ )
|
|
{
|
|
if ( sum_surface->m_curve[i] )
|
|
{
|
|
delete sum_surface->m_curve[i];
|
|
sum_surface->m_curve[i] = 0;
|
|
}
|
|
sum_surface->m_basepoint = ON_3dVector::ZeroVector;
|
|
sum_surface->m_bbox.Destroy();
|
|
}
|
|
}
|
|
|
|
if ( 0 == m_profile || !m_path.IsValid() )
|
|
return 0;
|
|
|
|
if ( IsMitered() )
|
|
return 0; // mitered extrusions cannot be represented as sum surfaces
|
|
|
|
ON_Xform xform0;
|
|
if ( !GetProfileTransformation(0.0,xform0) )
|
|
return 0;
|
|
|
|
ON_Curve* profile3d = 0;
|
|
ON_LineCurve* path = 0;
|
|
ON_Curve* curve0 = 0;
|
|
ON_Curve* curve1 = 0;
|
|
for(;;)
|
|
{
|
|
if ( 1 == ProfileCount() )
|
|
{
|
|
const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile);
|
|
if ( 0 != polycurve && 1 == polycurve->Count() )
|
|
{
|
|
const ON_Curve* segment = polycurve->SegmentCurve(0);
|
|
if ( 0 != segment )
|
|
{
|
|
profile3d = segment->DuplicateCurve();
|
|
profile3d->SetDomain( m_profile->Domain() );
|
|
}
|
|
}
|
|
}
|
|
if ( 0 == profile3d )
|
|
{
|
|
profile3d = m_profile->DuplicateCurve();
|
|
if ( 0 == profile3d )
|
|
break;
|
|
}
|
|
if ( profile3d->IsLinear() && 0 == ON_LineCurve::Cast(profile3d) )
|
|
{
|
|
ON_LineCurve* line_curve = new ON_LineCurve();
|
|
line_curve->m_line.from = profile3d->PointAtStart();
|
|
line_curve->m_line.to = profile3d->PointAtEnd();
|
|
line_curve->ON_Curve::SetDomain(profile3d->Domain());
|
|
delete profile3d;
|
|
profile3d = line_curve;
|
|
}
|
|
if ( !profile3d->ChangeDimension(3) )
|
|
break;
|
|
if ( !xform0.IsIdentity() && !profile3d->Transform(xform0) )
|
|
break;
|
|
|
|
path = new ON_LineCurve();
|
|
if ( 0 == path )
|
|
break;
|
|
path->m_line.from = ON_3dPoint::Origin;
|
|
path->m_line.to = (m_path.to - m_path.from);
|
|
if ( !path->SetDomain( m_path_domain[0], m_path_domain[1] ) )
|
|
break;
|
|
|
|
curve0 = profile3d;
|
|
curve1 = path;
|
|
profile3d = 0;
|
|
path = 0;
|
|
break;
|
|
}
|
|
if ( 0 == curve0 || 0 == curve1 )
|
|
{
|
|
if ( 0 != profile3d )
|
|
delete profile3d;
|
|
if ( 0 != path )
|
|
delete path;
|
|
return 0;
|
|
}
|
|
|
|
ON_SumSurface* sumsrf = ( 0 != sum_surface ) ? sum_surface : new ON_SumSurface();
|
|
if ( 0 == sumsrf )
|
|
{
|
|
delete curve0;
|
|
delete curve1;
|
|
return 0;
|
|
}
|
|
|
|
sumsrf->m_curve[0] = curve0;
|
|
sumsrf->m_curve[1] = curve1;
|
|
sumsrf->m_basepoint = ON_3dVector::ZeroVector;
|
|
sumsrf->m_bbox = BoundingBox();
|
|
|
|
if ( m_bTransposed )
|
|
sumsrf->Transpose();
|
|
|
|
return sumsrf;
|
|
}
|
|
|
|
ON_Extrusion* ON_Extrusion::Cylinder(
|
|
const ON_Cylinder& cylinder,
|
|
bool bCapBottom,
|
|
bool bCapTop,
|
|
ON_Extrusion* extrusion
|
|
)
|
|
{
|
|
if ( !cylinder.IsValid() || !cylinder.IsFinite() )
|
|
return 0;
|
|
|
|
ON_Line path;
|
|
path.from = cylinder.circle.plane.PointAt(0.0,0.0,cylinder.height[0]);
|
|
path.to = cylinder.circle.plane.PointAt(0.0,0.0,cylinder.height[1]);
|
|
if ( !path.IsValid() || !(path.Length() > ON_ZERO_TOLERANCE) )
|
|
return 0;
|
|
|
|
ON_3dVector up = cylinder.circle.plane.yaxis;
|
|
if ( !up.IsValid()
|
|
|| !up.IsUnitVector()
|
|
|| fabs(up*path.Tangent()) > ON_SQRT_EPSILON
|
|
)
|
|
return 0;
|
|
|
|
ON_ArcCurve* circle_curve = new ON_ArcCurve(cylinder.circle);
|
|
circle_curve->m_arc.plane = ON_Plane::World_xy;
|
|
circle_curve->m_dim = 2;
|
|
if ( !circle_curve->IsValid() )
|
|
{
|
|
delete circle_curve;
|
|
return 0;
|
|
}
|
|
|
|
ON_Extrusion* extrusion_cylinder = 0;
|
|
if ( extrusion )
|
|
{
|
|
extrusion->Destroy();
|
|
extrusion_cylinder = extrusion;
|
|
}
|
|
else
|
|
{
|
|
extrusion_cylinder = new ON_Extrusion();
|
|
}
|
|
|
|
if ( !extrusion_cylinder->SetPathAndUp(path.from,path.to,up)
|
|
|| !extrusion_cylinder->SetOuterProfile(circle_curve,false)
|
|
|| !extrusion_cylinder->IsValid()
|
|
|| !extrusion_cylinder->SetDomain(extrusion_cylinder->PathParameter(),cylinder.height[0],cylinder.height[1])
|
|
)
|
|
{
|
|
if ( 0 == extrusion )
|
|
delete extrusion_cylinder;
|
|
return 0;
|
|
}
|
|
|
|
extrusion_cylinder->m_bCap[0] = bCapBottom ? true : false;
|
|
extrusion_cylinder->m_bCap[1] = bCapTop ? true : false;
|
|
|
|
if ( !extrusion_cylinder->IsValid() )
|
|
{
|
|
if ( 0 == extrusion )
|
|
delete extrusion_cylinder;
|
|
return 0;
|
|
}
|
|
|
|
return extrusion_cylinder;
|
|
}
|
|
|
|
|
|
|
|
ON_Extrusion* ON_Extrusion::Pipe(
|
|
const ON_Cylinder& cylinder,
|
|
double other_radius,
|
|
bool bCapBottom,
|
|
bool bCapTop,
|
|
ON_Extrusion* extrusion
|
|
)
|
|
{
|
|
if ( !cylinder.IsValid()
|
|
|| !ON_IsValid(other_radius)
|
|
|| !(fabs(other_radius - cylinder.circle.Radius()) > ON_ZERO_TOLERANCE)
|
|
)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
double inner_radius = (other_radius < cylinder.circle.radius)
|
|
? other_radius
|
|
: cylinder.circle.radius;
|
|
double outer_radius = (other_radius < cylinder.circle.radius)
|
|
? cylinder.circle.radius
|
|
: other_radius;
|
|
if ( !ON_IsValid(inner_radius)
|
|
|| !ON_IsValid(outer_radius)
|
|
|| !(outer_radius - inner_radius > ON_ZERO_TOLERANCE)
|
|
)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ON_Cylinder outer_cylinder = cylinder;
|
|
outer_cylinder.circle.radius = outer_radius;
|
|
|
|
ON_Circle inner_circle(ON_Plane::World_xy,inner_radius);
|
|
ON_ArcCurve* inner_profile = new ON_ArcCurve(inner_circle);
|
|
inner_profile->m_dim = 2;
|
|
if ( !inner_profile->IsValid() )
|
|
{
|
|
delete inner_profile;
|
|
return 0;
|
|
}
|
|
|
|
ON_Extrusion* extrusion_pipe = ON_Extrusion::Cylinder(outer_cylinder,bCapBottom,bCapTop,extrusion);
|
|
if ( 0 == extrusion_pipe )
|
|
{
|
|
delete inner_profile;
|
|
return 0;
|
|
}
|
|
|
|
if ( !extrusion_pipe->IsValid() )
|
|
{
|
|
if ( 0 == extrusion )
|
|
delete extrusion_pipe;
|
|
delete inner_profile;
|
|
return 0;
|
|
}
|
|
|
|
if ( !extrusion_pipe->AddInnerProfile(inner_profile) )
|
|
{
|
|
if ( 0 == extrusion )
|
|
delete extrusion_pipe;
|
|
delete inner_profile;
|
|
return 0;
|
|
}
|
|
|
|
if ( !extrusion_pipe->IsValid() )
|
|
{
|
|
if ( 0 == extrusion )
|
|
delete extrusion_pipe;
|
|
return 0;
|
|
}
|
|
|
|
return extrusion_pipe;
|
|
}
|
|
|
|
|
|
ON_Extrusion* ON_Extrusion::CreateFrom3dCurve(
|
|
const ON_Curve& curve,
|
|
const ON_Plane* plane,
|
|
double height,
|
|
bool bCap,
|
|
ON_Extrusion* extrusion
|
|
)
|
|
{
|
|
if ( 0 != extrusion )
|
|
extrusion->Destroy();
|
|
|
|
if ( ON_IsValid(height) && 0.0 == height )
|
|
return 0;
|
|
|
|
ON_Interval z(0.0,height);
|
|
if ( z.IsDecreasing() )
|
|
z.Swap();
|
|
if ( !z.IsIncreasing() )
|
|
return 0;
|
|
|
|
if ( !curve.IsValid() )
|
|
return 0;
|
|
|
|
ON_Plane curve_plane;
|
|
if ( 0 == plane )
|
|
{
|
|
if ( !curve.IsPlanar(&curve_plane) )
|
|
return 0;
|
|
plane = &curve_plane;
|
|
}
|
|
|
|
if ( !plane->IsValid() )
|
|
return 0;
|
|
|
|
ON_Xform xform2d;
|
|
xform2d.ChangeBasis(ON_Plane::World_xy,*plane);
|
|
|
|
ON_Curve* curve2d = curve.DuplicateCurve();
|
|
if ( 0 == curve2d )
|
|
return 0;
|
|
|
|
ON_Extrusion* result = 0;
|
|
|
|
for (;;)
|
|
{
|
|
if ( !curve2d->Transform(xform2d) )
|
|
break;
|
|
curve2d->ChangeDimension(2);
|
|
|
|
if ( 0 == extrusion )
|
|
result = new ON_Extrusion();
|
|
else
|
|
result = extrusion;
|
|
|
|
if ( !result->SetPathAndUp(
|
|
plane->PointAt(0.0,0.0,z[0]),
|
|
plane->PointAt(0.0,0.0,z[1]),
|
|
plane->yaxis
|
|
) )
|
|
break;
|
|
|
|
if ( !result->SetOuterProfile(curve2d,bCap) )
|
|
break;
|
|
|
|
if ( !result->IsValid() ){
|
|
//28 Sept 2016 - Chuck. curve2d has been added to result in SetOuterProfile().
|
|
//It will be deleted below as curve2d, so it should be removed from result.
|
|
//See RH-35739
|
|
result->m_profile = 0;
|
|
break;
|
|
}
|
|
|
|
// success
|
|
curve2d = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
if ( 0 != curve2d )
|
|
{
|
|
// failure
|
|
delete curve2d;
|
|
curve2d = 0;
|
|
|
|
if ( 0 != result && result != extrusion )
|
|
delete result;
|
|
|
|
if ( extrusion )
|
|
extrusion->Destroy();
|
|
|
|
result = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
// OBSOLETE - USED TO READ/WRITE V5 files
|
|
|
|
|
|
ON_OBJECT_IMPLEMENT(ON_V5ExtrusionDisplayMeshCache,ON_UserData,"A8130A3E-E4F3-4CB0-BB8A-F10A473912D0");
|
|
|
|
ON_V5ExtrusionDisplayMeshCache::ON_V5ExtrusionDisplayMeshCache()
|
|
: ON_UserData()
|
|
{
|
|
m_userdata_uuid = ON_CLASS_ID(ON_V5ExtrusionDisplayMeshCache);
|
|
m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata
|
|
// The id must be the version 5 id because
|
|
// V6 SaveAs V5 needs to work.
|
|
m_userdata_copycount = 1;
|
|
}
|
|
|
|
ON_V5ExtrusionDisplayMeshCache::~ON_V5ExtrusionDisplayMeshCache()
|
|
{
|
|
DestroyHelper();
|
|
}
|
|
|
|
ON_V5ExtrusionDisplayMeshCache::ON_V5ExtrusionDisplayMeshCache(const ON_V5ExtrusionDisplayMeshCache& src)
|
|
: ON_UserData(src)
|
|
{
|
|
m_userdata_uuid = ON_CLASS_ID(ON_V5ExtrusionDisplayMeshCache);
|
|
m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata
|
|
// The id must be the version 5 id because
|
|
// V6 SaveAs V5 needs to work.
|
|
CopyHelper(src);
|
|
}
|
|
|
|
ON_V5ExtrusionDisplayMeshCache& ON_V5ExtrusionDisplayMeshCache::operator=(const ON_V5ExtrusionDisplayMeshCache& src)
|
|
{
|
|
if ( this != &src )
|
|
{
|
|
DestroyHelper();
|
|
ON_UserData::operator=(src);
|
|
CopyHelper(src);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void ON_V5ExtrusionDisplayMeshCache::CopyHelper(const ON_V5ExtrusionDisplayMeshCache& src)
|
|
{
|
|
m_render_mesh = src.m_render_mesh;
|
|
m_analysis_mesh = src.m_analysis_mesh;
|
|
}
|
|
|
|
void ON_V5ExtrusionDisplayMeshCache::DestroyHelper()
|
|
{
|
|
m_render_mesh.reset();
|
|
m_analysis_mesh.reset();
|
|
}
|
|
|
|
// override virtual ON_Object::Write function
|
|
bool ON_V5ExtrusionDisplayMeshCache::Write(ON_BinaryArchive& binary_archive) const
|
|
{
|
|
bool rc = true;
|
|
bool bSaveMeshes = binary_archive.Save3dmRenderMesh(ON::extrusion_object);
|
|
const ON_Mesh* meshes[3] = { m_render_mesh.get(), m_analysis_mesh.get(), nullptr };
|
|
if (nullptr != meshes[0] && meshes[0]->IsEmpty())
|
|
meshes[0] = nullptr;
|
|
if (nullptr != meshes[1] && meshes[1]->IsEmpty())
|
|
meshes[1] = nullptr;
|
|
for(size_t i = 0; i < sizeof(meshes)/sizeof(meshes[0]) && rc; i++ )
|
|
{
|
|
const ON_Mesh* mesh = bSaveMeshes ? meshes[i] : nullptr;
|
|
rc = binary_archive.WriteObject(mesh);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
// override virtual ON_Object::Read function
|
|
bool ON_V5ExtrusionDisplayMeshCache::Read(ON_BinaryArchive& binary_archive)
|
|
{
|
|
DestroyHelper();
|
|
bool rc = true;
|
|
|
|
ON_Mesh* meshes[3] = { 0 };
|
|
for(size_t i = 0; i < sizeof(meshes)/sizeof(meshes[0]) && rc; i++ )
|
|
{
|
|
ON_Object* p = 0;
|
|
rc = binary_archive.ReadObject(&p);
|
|
if (nullptr != p && i >= 2)
|
|
{
|
|
delete p;
|
|
p = nullptr;
|
|
}
|
|
if ( rc )
|
|
meshes[i] = ON_Mesh::Cast(p);
|
|
if ( 0 != p && 0 == meshes[i] )
|
|
{
|
|
delete p;
|
|
rc = false;
|
|
}
|
|
}
|
|
|
|
m_render_mesh = std::shared_ptr<ON_Mesh>(meshes[0]);
|
|
m_analysis_mesh = std::shared_ptr<ON_Mesh>(meshes[1]);
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_V5ExtrusionDisplayMeshCache::DeleteAfterWrite(
|
|
const class ON_BinaryArchive& archive,
|
|
const class ON_Object* parent_object
|
|
) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool ON_V5ExtrusionDisplayMeshCache::DeleteAfterRead(
|
|
const class ON_BinaryArchive& archive,
|
|
class ON_Object* parent_object
|
|
) const
|
|
{
|
|
ON_Extrusion* extrusion = ON_Extrusion::Cast(parent_object);
|
|
if (nullptr != extrusion)
|
|
{
|
|
if ( nullptr != m_analysis_mesh.get() )
|
|
extrusion->m_mesh_cache.SetMesh(ON_MeshCache::AnalysisMeshId, m_analysis_mesh);
|
|
if ( nullptr != m_render_mesh.get() )
|
|
extrusion->m_mesh_cache.SetMesh(ON_MeshCache::RenderMeshId, m_render_mesh);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//virtual
|
|
bool ON_V5ExtrusionDisplayMeshCache::GetDescription( ON_wString& description )
|
|
{
|
|
description = L"Cached meshes";
|
|
return true;
|
|
}
|
|
|
|
//virtual
|
|
bool ON_V5ExtrusionDisplayMeshCache::Archive() const
|
|
{
|
|
return (nullptr == m_render_mesh.get() && m_analysis_mesh.get()) ? false : true;
|
|
}
|
|
|
|
|
|
ON_V5ExtrusionDisplayMeshCache* ON_V5ExtrusionDisplayMeshCache::CreateMeshCache(const ON_Extrusion* p)
|
|
{
|
|
if ( 0 == p )
|
|
return nullptr;
|
|
|
|
if (p->m_mesh_cache.MeshCount() <= 0)
|
|
return nullptr;
|
|
|
|
std::shared_ptr<ON_Mesh> render_mesh = p->m_mesh_cache.MeshSharedPtr(ON_MeshCache::RenderMeshId);
|
|
std::shared_ptr<ON_Mesh> analysis_mesh = p->m_mesh_cache.MeshSharedPtr(ON_MeshCache::AnalysisMeshId);
|
|
|
|
if (nullptr == render_mesh.get() && nullptr == analysis_mesh.get() )
|
|
return nullptr;
|
|
|
|
ON_V5ExtrusionDisplayMeshCache* mc = ON_V5ExtrusionDisplayMeshCache::Cast( p->GetUserData(ON_CLASS_ID(ON_V5ExtrusionDisplayMeshCache)) );
|
|
if ( nullptr == mc )
|
|
{
|
|
mc = new ON_V5ExtrusionDisplayMeshCache();
|
|
if ( false == const_cast<ON_Extrusion*>(p)->AttachUserData(mc) )
|
|
{
|
|
delete mc;
|
|
mc = nullptr;
|
|
}
|
|
else
|
|
{
|
|
mc->m_render_mesh = render_mesh;
|
|
mc->m_analysis_mesh = analysis_mesh;
|
|
}
|
|
}
|
|
|
|
return mc;
|
|
}
|
|
|
|
|
|
bool ON_Extrusion::SetMesh( ON::mesh_type mt, ON_Mesh* mesh )
|
|
{
|
|
m_mesh_cache.SetMesh(mt,std::shared_ptr<ON_Mesh>(mesh));
|
|
return true;
|
|
}
|
|
|
|
const ON_Mesh* ON_Extrusion::Mesh( ON::mesh_type mt ) const
|
|
{
|
|
return m_mesh_cache.MeshSharedPtr(mt).get();
|
|
}
|
|
|
|
void ON_Extrusion::DestroyMesh( ON::mesh_type mt )
|
|
{
|
|
m_mesh_cache.ClearMesh(mt);
|
|
}
|
|
|