opennurbs_beam.cpp
Go to the documentation of this file.
00001 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
00002 
00003 static bool ON_ExtrusionPolyCurveProfileIsNotValid()
00004 {
00005   return false; // good place for a breakpoint
00006 }
00007 
00008 bool ON_Extrusion::IsValidPolyCurveProfile( const ON_PolyCurve& polycurve, ON_TextLog* text_log )
00009 {
00010   const bool bAllowGaps = true;
00011   bool rc = polycurve.IsValid(bAllowGaps,text_log) ? true : ON_ExtrusionPolyCurveProfileIsNotValid();
00012   if (!rc)
00013     return ON_ExtrusionPolyCurveProfileIsNotValid();
00014 
00015   const int profile_count = polycurve.Count();
00016 
00017   if ( profile_count < 1 )
00018   {
00019     if ( text_log )
00020     {
00021       text_log->Print("polycurve has < 1 segments.\n");
00022     }
00023     return ON_ExtrusionPolyCurveProfileIsNotValid();
00024   }
00025 
00026   if ( 2 != polycurve.Dimension() )
00027   {
00028     if ( 3 != polycurve.Dimension() )
00029     {
00030       if ( text_log )
00031       {
00032         text_log->Print("polycurve dimension = %d (should be 2).\n",polycurve.Dimension());
00033       }
00034       return ON_ExtrusionPolyCurveProfileIsNotValid();
00035     }
00036     ON_BoundingBox bbox = polycurve.BoundingBox();
00037     if ( !bbox.IsValid() )
00038     {
00039       if ( text_log )
00040       {
00041         text_log->Print("polycurve.BoundingBox() is not valid.\n");
00042       }
00043       return ON_ExtrusionPolyCurveProfileIsNotValid();
00044     }
00045     if ( !( 0.0 == bbox.m_min.z) || !(0.0 == bbox.m_max.z) )
00046     {
00047       if ( text_log )
00048       {
00049         text_log->Print("polycurve.BoundingBox() z values are not both 0.0.\n");
00050       }
00051       return ON_ExtrusionPolyCurveProfileIsNotValid();
00052     }
00053   }
00054 
00055   if ( 1 == profile_count )
00056     return true;
00057 
00058   if ( profile_count > 1 )
00059   {
00060     for ( int i = 0; i < profile_count; i++ )
00061     {
00062       const ON_Curve* segment = polycurve.SegmentCurve(i);
00063       if ( 0 == segment )
00064       {
00065         if ( text_log )
00066         {
00067           text_log->Print("polycurve.SegmentCurve(%d) is null.\n",i);
00068         }
00069         return ON_ExtrusionPolyCurveProfileIsNotValid();
00070       }
00071       if ( !segment->IsClosed() )
00072       {
00073         if ( text_log )
00074         {
00075           text_log->Print("polycurve.SegmentCurve(%d) is not closed.\n",i);
00076         }
00077         return ON_ExtrusionPolyCurveProfileIsNotValid();
00078       }
00079       if ( segment->Domain() != polycurve.SegmentDomain(i) )
00080       {
00081         if ( text_log )
00082         {
00083           text_log->Print("polycurve.Segment(%d).Domain() does not match polycurve.SegmentDomain(%d).\n",i,i);
00084         }
00085         return ON_ExtrusionPolyCurveProfileIsNotValid();
00086       }
00087     }
00088   }
00089   return true;
00090 }
00091 
00092 bool ON_Extrusion::CleanupPolyCurveProfile( ON_PolyCurve& polycurve )
00093 {
00094   if ( !ON_Extrusion::IsValidPolyCurveProfile(polycurve) )
00095   {
00096     // see if we can fix it
00097     int i;
00098     const int old_count = polycurve.Count();
00099     if ( old_count <= 1 )
00100       return false;
00101 
00102     // make segments 2d 
00103     for ( i = 0; i < old_count; i++ )
00104     {
00105       ON_Curve* old_segment = polycurve.SegmentCurve(i);
00106       if ( 0 == old_segment )
00107         return false;
00108       if ( 2 != old_segment->Dimension() && !old_segment->ChangeDimension(2) )
00109         return false;
00110     }
00111 
00112     // make segment domains match the polycurve's m_t[] array
00113     polycurve.SynchronizeSegmentDomains();
00114 
00115     // make each segment a closed curve
00116     ON_SimpleArray<ON_PolyCurve*> new_polycurves(old_count);
00117     ON_SimpleArray<ON_Curve*> new_segments(old_count);
00118     ON_PolyCurve* new_segment = 0;
00119     bool rc = true;
00120     for ( i = 0; i < old_count && rc; i++ )
00121     {
00122       ON_Curve* old_segment = polycurve.SegmentCurve(i);
00123       if ( old_segment->IsClosed() )
00124       {
00125         if ( 0 != new_segment )
00126         {
00127           rc = false;
00128           break;
00129         }
00130         new_segments.Append(old_segment);
00131       }
00132       else if ( 0 == new_segment )
00133       {
00134         new_segment = new ON_PolyCurve();
00135         new_polycurves.Append(new_segment);
00136         new_segment->Append(old_segment);
00137       }
00138       else
00139       {
00140         new_segment->Append(old_segment);
00141         if ( new_segment->FindNextGap(0) )
00142         {
00143           rc = false;
00144           break;
00145         }
00146         if ( new_segment->IsClosed() )
00147         {
00148           new_segments.Append(new_segment);
00149           new_segment = 0;
00150         }
00151       }
00152     }
00153 
00154     if ( 0 != new_segment )
00155     {
00156       rc = false;
00157     }
00158 
00159     if ( !rc )
00160     {
00161       // unable to fix polycurve. Delete the new stuff we allocated.
00162       for ( i = 0; i < new_polycurves.Count(); i++ )
00163       {
00164         new_segment = new_polycurves[i];
00165         if ( new_segment )
00166         {
00167           for ( int j =  new_segment->Count()-1; j >= 0; j-- )
00168           {
00169             new_segment->HarvestSegment(j); // do not delete parts of input polycurve
00170           }
00171           delete new_segment;
00172         }
00173       }
00174       return false;
00175     }
00176 
00177     for ( i = 0; i < new_polycurves.Count(); i++ )
00178     {
00179       new_polycurves[i]->RemoveNesting();
00180     }
00181 
00182     for ( i = old_count-1; i >= 0; i-- )
00183     {
00184       polycurve.HarvestSegment(i);
00185       polycurve.Remove(i);
00186     }
00187     for ( i = 0; i < new_segments.Count(); i++ )
00188     {
00189       polycurve.Append(new_segments[i]);
00190     }
00191   }
00192   else
00193   {
00194     polycurve.ChangeDimension(2);
00195   }
00196   return true;
00197 }
00198 
00199 
00200 bool ON_GetEndCapTransformation(ON_3dPoint P, ON_3dVector T, ON_3dVector U, 
00201                                 const ON_3dVector* Normal, 
00202                                 ON_Xform& xform,
00203                                 ON_Xform* scale2d,
00204                                 ON_Xform* rot3d
00205                                 )
00206 {
00207   if ( scale2d )
00208     scale2d->Identity();
00209   if ( rot3d )
00210     rot3d->Identity();
00211   if ( !T.IsUnitVector() && !T.Unitize() )
00212     return false;
00213   if ( !U.IsUnitVector() && !U.Unitize() )
00214     return false;
00215   ON_3dVector N(0.0,0.0,0.0);
00216   if ( Normal )
00217   {
00218     N = *Normal;
00219     if ( !N.IsUnitVector() && !N.Unitize() )
00220       N.Zero();
00221   }
00222 
00223   ON_Plane p0;
00224   p0.origin = P;
00225   p0.zaxis = T;
00226   p0.yaxis = U;
00227   p0.xaxis = ON_CrossProduct(U,T);
00228   if ( !p0.xaxis.IsUnitVector() )
00229     p0.xaxis.Unitize();
00230   p0.UpdateEquation();
00231   xform.Rotation(ON_xy_plane,p0);
00232   if ( rot3d )
00233     *rot3d = xform;
00234   if ( N.z > ON_Extrusion::m_Nz_min && N.IsUnitVector() )
00235   {
00236     //double cosa = T.x*N.x + T.y*N.y + T.z*N.z; // N is relative to T
00237     double cosa = N.z; // N is relative to xy plane.
00238     for(;;)
00239     {
00240       ON_3dVector A(-N.y,N.x,0.0); // N is relative to xy plane.
00241       if ( !A.IsValid() )
00242         break;
00243       double sina = A.Length();
00244       if ( !ON_IsValid(sina) )
00245         break;
00246       if ( !A.Unitize() )
00247         break;
00248       
00249       // S is a non-uniform scale that maps A to A and perpA to 1/cosa*perpA.
00250       // The scale distorts the profile so that after it is rotated
00251       // into the miter plane, the projection of the rotated profile
00252       // onto the xy-plane matches the original profile.
00253       ON_Xform S(0.0);
00254       const double c = 1.0 - 1.0/cosa;
00255       S.m_xform[0][0] = 1.0 - c*A.y*A.y;
00256       S.m_xform[0][1] = c*A.x*A.y;
00257 
00258       S.m_xform[1][0] = S.m_xform[0][1];
00259       S.m_xform[1][1] = 1.0 - c*A.x*A.x;
00260 
00261       S.m_xform[2][2] = 1.0;
00262 
00263       S.m_xform[3][3] = 1.0;
00264       if (scale2d)
00265         *scale2d = S;
00266 
00267       // R rotates the profile plane so its normal is equal to N
00268       ON_Xform R;
00269       R.Rotation(sina,cosa,A,ON_origin);
00270       if ( rot3d )
00271         *rot3d = xform*R;
00272 
00273       xform = xform*R*S;
00274       break;
00275     }
00276   }
00277   return true;
00278 }
00279 
00280 static void ON_ExtrusionInitializeHelper(ON_Extrusion& extrusion)
00281 {
00282   extrusion.m_path.from.Zero();
00283   extrusion.m_path.to.Zero();
00284   extrusion.m_t.m_t[0] = 0.0;
00285   extrusion.m_t.m_t[1] = 1.0;
00286   extrusion.m_up.Zero();
00287   extrusion.m_profile_count = 0;
00288   extrusion.m_profile = 0;
00289   extrusion.m_bCap[0] = false;
00290   extrusion.m_bCap[1] = false;
00291   extrusion.m_bHaveN[0] = false;
00292   extrusion.m_bHaveN[1] = false;
00293   extrusion.m_N[0].Zero();
00294   extrusion.m_N[1].Zero();
00295   extrusion.m_path_domain.m_t[0] = 0.0;
00296   extrusion.m_path_domain.m_t[1] = 1.0;
00297   extrusion.m_bTransposed = false;
00298 }
00299 
00300 static void ON_ExtrusionCopyHelper(const ON_Extrusion& src,ON_Extrusion& dst)
00301 {
00302   if ( &src != &dst )
00303   {
00304     if ( dst.m_profile )
00305     {
00306       delete dst.m_profile;
00307       dst.m_profile = 0;
00308     }
00309     dst.m_path = src.m_path;
00310     dst.m_t = src.m_t;
00311     dst.m_up = src.m_up;
00312     dst.m_profile_count = src.m_profile_count;
00313     dst.m_profile = src.m_profile 
00314                   ? src.m_profile->DuplicateCurve() 
00315                   : 0;
00316     dst.m_bCap[0] = src.m_bCap[0];
00317     dst.m_bCap[1] = src.m_bCap[1];
00318     dst.m_bHaveN[0] = src.m_bHaveN[0];
00319     dst.m_bHaveN[1] = src.m_bHaveN[1];
00320     dst.m_N[0] = src.m_N[0];
00321     dst.m_N[1] = src.m_N[1];
00322     dst.m_path_domain = src.m_path_domain;
00323     dst.m_bTransposed  = src.m_bTransposed;
00324   }
00325 }
00326 
00327 bool ON_Extrusion::SetPath(ON_3dPoint A, ON_3dPoint B)
00328 {
00329   double distAB = 0.0;
00330   bool rc =    A.IsValid() && B.IsValid() 
00331             && (distAB = A.DistanceTo(B)) > ON_ZERO_TOLERANCE;
00332   if (rc)
00333   {
00334     m_path.from = A;
00335     m_path.to = B;
00336     m_t.Set(0.0,1.0);
00337     m_path_domain.Set(0.0,distAB);
00338   }
00339   return rc;
00340 }
00341 
00342 bool ON_Extrusion::SetPathAndUp( ON_3dPoint A, ON_3dPoint B, ON_3dVector up )
00343 {
00344   double distAB = 0.0;
00345 
00346   bool rc =    up.IsValid() 
00347             && up.Length() > ON_ZERO_TOLERANCE
00348             && A.IsValid() 
00349             && B.IsValid() 
00350             && (distAB = A.DistanceTo(B)) > ON_ZERO_TOLERANCE;
00351 
00352   if (rc)
00353   {
00354     ON_3dVector D = A-B;
00355     D.Unitize();
00356     double d = up*D;
00357     if ( !up.IsUnitVector() || fabs(d) > distAB*ON_SQRT_EPSILON*0.015625 )
00358     {
00359       // need to make up perpendicular to the line segment
00360       // and unitize.
00361       D.Unitize();
00362       up = up - d*D;
00363       up.Unitize();
00364       // validate up
00365       d = up*D;
00366       rc = ( up.IsUnitVector() && fabs(d) <= ON_SQRT_EPSILON );
00367     }
00368 
00369     if (rc)
00370     {
00371       m_path.from = A;
00372       m_path.to = B;
00373       m_t.Set(0.0,1.0);
00374       m_path_domain.Set(0.0,distAB);
00375       m_up = up;
00376     }
00377   }
00378 
00379   return rc;
00380 }
00381 
00382 int ON_Extrusion::PathParameter() const
00383 {
00384   return m_bTransposed ? 0 : 1;
00385 }
00386 
00387 int ON_Extrusion::ProfileParameter() const
00388 {
00389   return m_bTransposed ? 1 : 0;
00390 }
00391 
00392 ON_3dPoint ON_Extrusion::PathStart() const
00393 {
00394   ON_3dPoint P(ON_UNSET_POINT);
00395   const double t = m_t.m_t[0];
00396   if ( 0.0 <= t && t <= 1.0 && m_path.IsValid() )
00397     P = m_path.PointAt(t);
00398   return P;
00399 }
00400 
00401 ON_3dPoint ON_Extrusion::PathEnd() const
00402 {
00403   ON_3dPoint P(ON_UNSET_POINT);
00404   const double t = m_t.m_t[1];
00405   if ( 0.0 <= t && t <= 1.0 && m_path.IsValid() )
00406     P = m_path.PointAt(t);
00407   return P;
00408 }
00409 
00410 ON_3dVector ON_Extrusion::PathTangent() const
00411 {
00412   ON_3dVector T(ON_UNSET_VECTOR);
00413   if ( m_path.IsValid() )
00414     T = m_path.Tangent();
00415   return T;
00416 }
00417 
00418 void ON_Extrusion::Destroy()
00419 {
00420   if ( m_profile)
00421   {
00422     delete m_profile;
00423     m_profile = 0;
00424   }
00425   ON_ExtrusionInitializeHelper(*this);
00426   DestroyRuntimeCache();
00427   PurgeUserData();
00428 }
00429 
00430 bool ON_Extrusion::SetMiterPlaneNormal(ON_3dVector N, int end)
00431 {
00432   bool rc = false;
00433   if ( end >= 0 && end <= 1 )
00434   {
00435     if (    N.IsValid() 
00436          && N.z > ON_Extrusion::m_Nz_min
00437          && (N.IsUnitVector() || N.Unitize())
00438        )
00439     {
00440       if (fabs(N.x) <= ON_SQRT_EPSILON && fabs(N.y) <= ON_SQRT_EPSILON)
00441         N.Set(0.0,0.0,1.0);
00442       m_N[end] = N;
00443       m_bHaveN[end] = (N.z != 1.0);
00444       rc = true;
00445     }
00446     else if ( N.IsZero() || ON_UNSET_VECTOR == N )
00447     {
00448       m_bHaveN[end] = false;
00449       rc = true;
00450     }
00451   }
00452   return rc;
00453 }
00454 
00455 void ON_Extrusion::GetMiterPlaneNormal(int end, ON_3dVector& N) const
00456 {
00457   if ( end >= 0 && end <= 1 && m_bHaveN[end] )
00458     N = m_N[end];
00459   else
00460     N.Set(0.0,0.0,1.0);
00461 }
00462 
00463 int ON_Extrusion::IsMitered() const
00464 {
00465   int rc = 0;
00466   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) )
00467     rc += 1;
00468   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) )
00469     rc += 2;
00470   return rc;
00471 }
00472 
00473 int ON_Extrusion::CapCount() const
00474 {
00475   // Returns number of end caps.
00476   switch (IsCapped())
00477   {
00478   case 1:
00479   case 2:
00480     return 1;
00481   case 3:
00482     return 2;
00483   }
00484   return 0;
00485 }
00486 
00487 int ON_Extrusion::IsCapped() const
00488 {
00489   // 0 = no caps, 1 = bottom cap, 2 = top cap, 3 = both caps
00490 
00491   if ( !m_bCap[0] && !m_bCap[1] )
00492     return 0;
00493 
00494   if ( m_profile_count < 1 || 0 == m_profile )
00495     return 0;
00496 
00497   if ( 1 == m_profile_count )
00498   {
00499     if ( !m_profile->IsClosed() )
00500       return 0;
00501   }
00502   else if ( m_profile_count > 1 )
00503   {
00504     const ON_PolyCurve* p = ON_PolyCurve::Cast(m_profile);
00505     if ( 0 == p )
00506       return 0;
00507     const ON_Curve* outer_profile = p->SegmentCurve(0);
00508     if ( 0 == outer_profile )
00509       return 0;
00510     if ( !outer_profile->IsClosed() )
00511       return 0;
00512   }
00513 
00514   return (m_bCap[0] ? (m_bCap[1] ? 3 : 1) : 2);
00515 }
00516 
00517 int ON_Extrusion::FaceCount() const
00518 {
00519   int face_count = 0;
00520   const ON_Curve* profile0 = Profile(0);
00521 
00522   if ( m_profile_count > 0 && 0 != profile0 )
00523   {
00524     int is_capped = IsCapped();
00525     if ( is_capped != 0 && !profile0->IsClosed() )
00526     {
00527       is_capped = 0;
00528     }
00529 
00530     switch(is_capped)
00531     {
00532     case 1: // single bottom cap + sides
00533     case 2: // single top cap + sides
00534       face_count = m_profile_count + 1;
00535       break;
00536 
00537     case 3: // bottom and top cap + sides
00538       face_count = m_profile_count + 2;
00539       break;
00540 
00541     default: // no caps
00542       face_count = 1;
00543       break;
00544     }
00545   }
00546 
00547   return face_count;
00548 }
00549 
00550 
00551 bool ON_Extrusion::IsSolid() const
00552 {
00553   if ( !m_bCap[0] || !m_bCap[1] )
00554     return false;
00555   return 3 == IsCapped();
00556 }
00557 
00558 bool ON_Extrusion::GetPathPlane( double s, ON_Plane& plane ) const
00559 {
00560   ON_Plane p;
00561   p.origin = ON_3dPoint::Origin;
00562   p.zaxis = PathTangent();
00563   p.yaxis = m_up;
00564   p.xaxis = ON_CrossProduct(p.yaxis,p.zaxis);
00565   if ( !p.xaxis.Unitize() )
00566     return false;
00567   if ( !p.yaxis.Unitize() )
00568     return false;
00569   p.UpdateEquation();
00570   if ( !p.IsValid() )
00571   {
00572     p.yaxis = ON_CrossProduct(p.zaxis,p.xaxis);
00573     p.yaxis.Unitize();
00574     if ( !p.IsValid() )
00575       return false;
00576   }
00577   p.origin = m_path.PointAt(m_t.ParameterAt(s));
00578   p.UpdateEquation();
00579   plane = p;
00580   return plane.IsValid();
00581 }
00582 
00583 bool ON_Extrusion::GetProfilePlane( double s, ON_Plane& plane ) const
00584 {
00585   ON_Plane p;
00586   p.origin = ON_3dPoint::Origin;
00587   p.zaxis = PathTangent();
00588   p.yaxis = m_up;
00589   p.xaxis = ON_CrossProduct(p.yaxis,p.zaxis);
00590   if ( !p.xaxis.Unitize() )
00591     return false;
00592   if ( !p.yaxis.Unitize() )
00593     return false;
00594   p.UpdateEquation();
00595   if ( !p.IsValid() )
00596   {
00597     p.yaxis = ON_CrossProduct(p.zaxis,p.xaxis);
00598     p.yaxis.Unitize();
00599     if ( !p.IsValid() )
00600       return false;
00601   }
00602   if (    (!m_bHaveN[0] || (0.0 == m_N[0].x && 0.0 == m_N[0].y)) 
00603        && (!m_bHaveN[1] || (0.0 == m_N[1].x && 0.0 == m_N[1].y)) 
00604      )
00605   {
00606     p.origin = m_path.PointAt(m_t.ParameterAt(s));
00607     p.UpdateEquation();
00608     plane = p;
00609   }
00610   else
00611   {
00612     ON_Xform xform;
00613     if ( !GetProfileTransformation(s,xform) )
00614       return false;
00615     if (!p.Transform(xform))
00616       return false;
00617     plane = p;
00618   }
00619   return plane.IsValid();
00620 }
00621 
00622 
00623 bool ON_Extrusion::GetProfileTransformation( double s, ON_Xform& xform ) const
00624 {
00625   //const ON_3dVector* N = (end?(m_bHaveN[1]?&m_N[1]:0):(m_bHaveN[0]?&m_N[0]:0));
00626   const ON_3dVector T = m_path.Tangent();
00627   if ( 0.0 == s )
00628   {
00629     return ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform,0,0);
00630   }
00631   if ( 1.0 == s )
00632   {
00633     return ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform,0,0);
00634   }
00635   ON_Xform xform0, xform1;
00636   if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,0,0) )
00637     return false;
00638   if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,0,0) )
00639     return false;
00640 
00641   const double s0 = 1.0-s;
00642   xform.m_xform[0][0] = s0*xform0.m_xform[0][0] + s*xform1.m_xform[0][0];
00643   xform.m_xform[0][1] = s0*xform0.m_xform[0][1] + s*xform1.m_xform[0][1];
00644   xform.m_xform[0][2] = s0*xform0.m_xform[0][2] + s*xform1.m_xform[0][2];
00645   xform.m_xform[0][3] = s0*xform0.m_xform[0][3] + s*xform1.m_xform[0][3];
00646   xform.m_xform[1][0] = s0*xform0.m_xform[1][0] + s*xform1.m_xform[1][0];
00647   xform.m_xform[1][1] = s0*xform0.m_xform[1][1] + s*xform1.m_xform[1][1];
00648   xform.m_xform[1][2] = s0*xform0.m_xform[1][2] + s*xform1.m_xform[1][2];
00649   xform.m_xform[1][3] = s0*xform0.m_xform[1][3] + s*xform1.m_xform[1][3];
00650   xform.m_xform[2][0] = s0*xform0.m_xform[2][0] + s*xform1.m_xform[2][0];
00651   xform.m_xform[2][1] = s0*xform0.m_xform[2][1] + s*xform1.m_xform[2][1];
00652   xform.m_xform[2][2] = s0*xform0.m_xform[2][2] + s*xform1.m_xform[2][2];
00653   xform.m_xform[2][3] = s0*xform0.m_xform[2][3] + s*xform1.m_xform[2][3];
00654   xform.m_xform[3][0] = s0*xform0.m_xform[3][0] + s*xform1.m_xform[3][0];
00655   xform.m_xform[3][1] = s0*xform0.m_xform[3][1] + s*xform1.m_xform[3][1];
00656   xform.m_xform[3][2] = s0*xform0.m_xform[3][2] + s*xform1.m_xform[3][2];
00657   xform.m_xform[3][3] = s0*xform0.m_xform[3][3] + s*xform1.m_xform[3][3];
00658 
00659   return true;
00660 }
00661 
00662 static bool CleanProfileSegment( ON_Curve* curve )
00663 {
00664   ON_NurbsCurve* nurbs_curve = ON_NurbsCurve::Cast(curve);
00665   if ( nurbs_curve )
00666   {
00667     nurbs_curve->RemoveSingularSpans();
00668     return ( nurbs_curve->IsValid() && false == nurbs_curve->SpanIsSingular(0) );
00669   }
00670   
00671   return true;
00672 }
00673 
00674 static bool ProfileHelper( int desired_orientation, ON_Curve* profile )
00675 {
00676   // desired_orientation  0: outer profile that can be open or closed.
00677   // desired_orientation  1: outer profile that must be closed
00678   // desired_orientation -1: inner profile
00679 
00680   if ( 0 == profile )
00681   {
00682     ON_ERROR("ON_Extrusion::Set/Add Profile - null input curve pointer.");
00683     return false;
00684   }
00685   ON_BoundingBox bbox = profile->BoundingBox();
00686   if ( !bbox.IsValid() )
00687   {
00688     ON_ERROR("ON_Extrusion::Set/Add Profile - profile->BoundingBox() failed.");
00689     return false;
00690   }
00691   if ( fabs(bbox.m_min.z) > ON_ZERO_TOLERANCE || fabs(bbox.m_max.z) > ON_ZERO_TOLERANCE )
00692   {
00693     ON_ERROR("ON_Extrusion::Set/Add Profile - profile->BoundingBox() is not in the world xy plane.");
00694     return false;
00695   }
00696   if ( !profile->ChangeDimension(2) )
00697   {
00698     ON_ERROR("ON_Extrusion::Set/Add Profile - profile->ChangeDimension(2) failed.");
00699     return false;
00700   }
00701 
00702   if ( profile->IsClosed() )
00703   {
00704     int profile_orientation = ON_ClosedCurveOrientation(*profile,0);
00705     switch(desired_orientation)
00706     {
00707     case 1:
00708     case 0:
00709       if ( -1 == profile_orientation )
00710       {
00711         if ( !profile->Reverse() )
00712         {
00713           ON_ERROR("ON_Extrusion::SetOuterProfile() - profile->Reverse() failed.");
00714           return false;
00715         }
00716         profile_orientation = 1;
00717       }
00718       if ( 1 == desired_orientation && 1 != profile_orientation )
00719       {
00720         ON_ERROR("ON_Extrusion::SetOuterProfile() - profile has wrong orientation.");
00721         return false;
00722       }
00723       break;
00724     case -1:
00725       if ( 1 == profile_orientation )
00726       {
00727         if ( !profile->Reverse() )
00728         {
00729           ON_ERROR("ON_Extrusion::AddInnerProfile() - profile->Reverse() failed.");
00730           return false;
00731         }
00732         profile_orientation = -1;
00733       }
00734       if ( -1 != profile_orientation )
00735       {
00736         ON_ERROR("ON_Extrusion::AddInnerProfile() - profile has wrong orientation.");
00737         return false;
00738       }
00739       break;
00740     default:
00741       {
00742         ON_ERROR("ON_Extrusion::Set/Add Profile - invalid desired_orientation parameter.");
00743         return false;
00744       }
00745       break;
00746     }
00747   }
00748   else if ( 0 != desired_orientation )
00749   {
00750     ON_ERROR("ON_Extrusion::Set/Add Profile - profile is an open curve.");
00751     return false;
00752   }
00753 
00754   ON_PolyCurve* polycurve = ON_PolyCurve::Cast(profile);
00755   if ( 0 != polycurve )
00756   {
00757     polycurve->RemoveNesting();
00758     
00759     if ( polycurve->SegmentCurves().Count() < 1 )
00760     {
00761       ON_ERROR("ON_Extrusion::Set/Add Profile - ON_PolyCurve has no segments.");
00762       return false;
00763     }
00764     
00765     if ( polycurve->SegmentCurves().Count() + 1 != polycurve->SegmentParameters().Count() )
00766     {
00767       ON_ERROR("ON_Extrusion::Set/Add Profile - ON_PolyCurve segment and parameter counts do not agree.");
00768       return false;
00769     }
00770 
00771     for ( int i = polycurve->Count()-1; i >= 0; i-- )
00772     {
00773       ON_Curve* segment = polycurve->SegmentCurve(i);
00774       if ( !CleanProfileSegment(segment) )
00775         polycurve->Remove(i);
00776     }
00777 
00778     for ( int i = 0; i < polycurve->Count(); i++ )
00779     {
00780       ON_Curve* segment = polycurve->SegmentCurve(i);
00781       if ( 0 == segment )
00782       {
00783         ON_ERROR("ON_Extrusion::Set/Add Profile - ON_PolyCurve has null segment.");
00784         return false;
00785       }
00786       const ON_Interval segment_domain = polycurve->SegmentDomain(i);
00787       if ( !segment_domain.IsIncreasing() )
00788       {
00789         ON_ERROR("ON_Extrusion::Set/Add Profile - segment has invalid domain.");
00790         return false;
00791       }
00792       if ( !segment->SetDomain( segment_domain ) )
00793       {
00794         ON_ERROR("ON_Extrusion::Set/Add Profile - segment->SetDomain() failed.");
00795         return false;
00796       }
00797     }
00798   }
00799   else if ( !CleanProfileSegment(profile) )
00800   {
00801   }
00802 
00803   return true;
00804 }
00805 
00806 bool ON_Extrusion::SetOuterProfile( ON_Curve* outer_profile, bool bCap )
00807 {
00808   if ( 0 != m_profile )
00809   {
00810     ON_ERROR("ON_Extrusion::SetOuterProfile() called when m_profile is already not null.");
00811     return false;
00812   }
00813 
00814   if ( !ProfileHelper( 0, outer_profile ) )
00815     return false;
00816 
00817   m_profile_count = 1;
00818   m_profile = outer_profile;
00819 
00820   if ( outer_profile->IsClosed() )
00821   {
00822     m_bCap[0] = m_bCap[1] = (bCap ? true : false);
00823   }
00824   else
00825   {
00826     m_bCap[0] = m_bCap[1] = false;
00827   }
00828 
00829   return true;
00830 }
00831 
00832 bool ON_Extrusion::AddInnerProfile( ON_Curve* inner_profile )
00833 {
00834   if ( m_profile_count < 1 )
00835   {
00836     ON_ERROR("ON_Extrusion::AddInnerProfile() called when m_profile_count < 1.");
00837     return false;
00838   }
00839   if ( 0 == m_profile )
00840   {
00841     ON_ERROR("ON_Extrusion::AddInnerProfile() called when m_profile is null.");
00842     return false;
00843   }
00844 
00845   if ( 1 == m_profile_count && !m_profile->IsClosed() )
00846   {
00847     ON_ERROR("ON_Extrusion::AddInnerProfile() called when outer profile is not closed.");
00848     return false;
00849   }
00850 
00851   ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile);
00852   if (  m_profile_count > 1 && 0 == polycurve )
00853   {
00854     ON_ERROR("ON_Extrusion::AddInnerProfile() called when  m_profile_count > 1 but m_profile is not an ON_PolyCurve.");
00855     return false;
00856   }
00857   if ( m_profile_count > 1 && m_profile_count != polycurve->Count() )
00858   {
00859     ON_ERROR("ON_Extrusion::AddInnerProfile() called when  m_profile_count > 1 but m_profile_count != m_profile->Count().");
00860     return false;
00861   }
00862 
00863   if ( !ProfileHelper( -1, inner_profile ) )
00864     return false;
00865 
00866   if ( 1 == m_profile_count )
00867   {
00868     if ( 0 != polycurve )
00869     {
00870       polycurve->RemoveNesting();
00871     }
00872 
00873     if ( 0 == polycurve || 1 != polycurve->Count() )
00874     {
00875       polycurve = new ON_PolyCurve();
00876       polycurve->Append(m_profile);
00877       m_profile = polycurve;
00878     }
00879   }
00880 
00881   polycurve->Append(inner_profile);
00882   if ( polycurve->SegmentDomain(m_profile_count) != inner_profile->Domain() )
00883   {
00884     inner_profile->SetDomain( polycurve->SegmentDomain(m_profile_count) );
00885 
00886     // If inner_profile is itself a polycurve, clean up segment domains
00887     // so we don't get bugs caused by fuzz as we adjust parameters when evaluating.
00888     polycurve = ON_PolyCurve::Cast(inner_profile);
00889     if ( 0 != polycurve )
00890     {
00891       polycurve->SynchronizeSegmentDomains();
00892     }
00893   }
00894   m_profile_count++;
00895 
00896  
00897   return true;
00898 }
00899 
00900 const ON_PolyCurve* ON_Extrusion::PolyProfile() const
00901 {
00902   if ( m_profile_count <= 1 )
00903     return 0;
00904   const ON_PolyCurve* poly_profile = ON_PolyCurve::Cast(m_profile);
00905   return (0 != poly_profile && m_profile_count == poly_profile->Count() ) ? poly_profile : 0;
00906 }
00907 
00908 const ON_Curve* ON_Extrusion::Profile(int profile_index) const
00909 {
00910   if ( 0 == profile_index && 1 == m_profile_count )
00911     return m_profile;
00912   if ( profile_index < 0 || profile_index > m_profile_count )
00913     return 0;
00914   const ON_PolyCurve* poly_profile = PolyProfile();
00915   return ( 0 != poly_profile ? poly_profile->SegmentCurve(profile_index) : 0 );
00916 }
00917 
00918 ON_Curve* ON_Extrusion::Profile3d( ON_COMPONENT_INDEX ci ) const
00919 {
00920   double s = ON_UNSET_VALUE;
00921   if ( ON_COMPONENT_INDEX::extrusion_bottom_profile == ci.m_type )
00922     s = 0.0;
00923   else if ( ON_COMPONENT_INDEX::extrusion_top_profile == ci.m_type )
00924     s = 1.0;
00925   else
00926     return 0;
00927   return Profile3d(ci.m_index,s);
00928 }
00929 
00930 ON_LineCurve* ON_Extrusion::PathLineCurve(ON_LineCurve* line_curve) const
00931 {
00932   if ( !m_path.IsValid() )
00933     return 0;
00934 
00935   ON_Interval path_domain = Domain(PathParameter());
00936   if ( !path_domain.IsIncreasing() )
00937     return 0;
00938 
00939   if ( 0 == line_curve )
00940     line_curve = new ON_LineCurve();
00941   line_curve->m_line = m_path;
00942   line_curve->SetDomain( path_domain[0], path_domain[1] );
00943 
00944   return line_curve;
00945 }
00946 
00947 
00948 ON_Curve* ON_Extrusion::WallEdge( ON_COMPONENT_INDEX ci ) const
00949 {
00950   if ( ON_COMPONENT_INDEX::extrusion_wall_edge != ci.m_type )
00951     return 0;
00952   if ( ci.m_index < 0 )
00953     return 0;
00954 
00955   int profile_index = ci.m_index/2;
00956   int profile_end   = ci.m_index % 2;
00957   const ON_Curve* profile2d = Profile(profile_index);
00958   if ( 0 == profile2d )
00959     return 0;
00960 
00961   ON_3dPoint profileP = profile_end
00962                       ? profile2d->PointAtEnd()
00963                       : profile2d->PointAtStart();
00964   if ( !profileP.IsValid() )
00965     return 0;
00966   profileP.z = 0.0;
00967 
00968   ON_Xform xform0, xform1;
00969   if ( !GetProfileTransformation(0.0,xform0) )
00970     return 0;
00971   if ( !GetProfileTransformation(1.0,xform1) )
00972     return 0;
00973 
00974   ON_Line line;
00975   line.from = xform0*profileP;
00976   line.to   = xform1*profileP;
00977   if ( !line.IsValid() )
00978     return 0;
00979 
00980   ON_LineCurve* line_curve = new ON_LineCurve();
00981   line_curve->m_line = line;
00982   
00983   ON_Interval path_domain = Domain(PathParameter());
00984   line_curve->SetDomain( path_domain[0], path_domain[1] );
00985 
00986   return line_curve;
00987 }
00988 
00989 
00990 ON_Surface* ON_Extrusion::WallSurface( ON_COMPONENT_INDEX ci ) const
00991 {
00992   if ( ON_COMPONENT_INDEX::extrusion_wall_surface != ci.m_type )
00993     return 0;
00994 
00995   const ON_Curve* profile2d = Profile(ci.m_index);
00996   if ( 0 == profile2d )
00997     return 0;
00998 
00999   ON_Interval wall_profile2d_domain = m_path_domain;
01000   if ( m_profile_count > 1 )
01001   {
01002     const ON_PolyCurve* polyprofile2d = PolyProfile();
01003     if ( 0 == polyprofile2d )
01004       return 0;
01005     if ( polyprofile2d->Count() != m_profile_count )
01006       return 0;
01007     wall_profile2d_domain = polyprofile2d->SegmentDomain(ci.m_index);
01008   }
01009 
01010   ON_Curve* wall_profile2d = profile2d->DuplicateCurve();
01011   if ( 0 == wall_profile2d )
01012     return 0;
01013   wall_profile2d->SetDomain(wall_profile2d_domain);
01014   wall_profile2d->ChangeDimension(2);
01015 
01016   ON_Extrusion* wall = new ON_Extrusion();
01017   wall->SetOuterProfile(wall_profile2d,false);
01018 
01019   wall->m_path = m_path;
01020   wall->m_t    = m_t;
01021   wall->m_up   = m_up;
01022   wall->m_bHaveN[0] = m_bHaveN[0];
01023   wall->m_bHaveN[1] = m_bHaveN[1];
01024   wall->m_N[0] = m_N[0];
01025   wall->m_N[1] = m_N[1];
01026   wall->m_bTransposed = m_bTransposed;
01027 
01028   return wall;
01029 }
01030 
01031 ON_Curve* ON_Extrusion::Profile3d( int profile_index, double s ) const
01032 {
01033   if ( profile_index < 0 || !(0.0 <= s && s <= 1.0) || 0 == m_profile )
01034     return 0;
01035 
01036   ON_Xform xform;
01037   if ( !GetProfileTransformation(s,xform) )
01038     return 0;
01039 
01040   const ON_Curve* profile2d = Profile(profile_index);
01041   if ( 0 == profile2d )
01042     return 0;
01043 
01044   ON_Curve* profile3d = profile2d->DuplicateCurve();
01045   if ( 0 == profile3d )
01046     return 0;
01047 
01048   if (    !profile3d->ChangeDimension(3) 
01049        || !profile3d->Transform(xform)
01050      )
01051   {
01052     delete profile3d;
01053     return 0;
01054   }
01055 
01056   return profile3d;
01057 }
01058 
01059 int ON_Extrusion::ProfileIndex( double profile_parameter ) const
01060 {
01061   if ( !m_profile )
01062     return -1;
01063 
01064   if ( m_profile_count < 1 )
01065     return -1;
01066 
01067   if ( 1 == m_profile_count )
01068   {
01069     return m_profile->Domain().Includes(profile_parameter,false) ? 0 : -1;
01070   }
01071 
01072   const ON_PolyCurve* polycurve = PolyProfile();
01073   if ( 0 == polycurve )
01074     return -1;
01075 
01076   const ON_SimpleArray<double>& polycurve_t = polycurve->SegmentParameters();
01077   if ( polycurve_t.Count() != m_profile_count+1 )
01078     return -1;
01079 
01080   int profile_index = ON_SearchMonotoneArray( polycurve_t.Array(), polycurve_t.Count(), profile_parameter );
01081   if ( profile_index == m_profile_count )
01082     profile_index = m_profile_count-1;
01083   else if ( profile_index < 0 || profile_index > m_profile_count )
01084     profile_index = -1;
01085   return profile_index;
01086 }
01087 
01088 int ON_Extrusion::ProfileCount() const
01089 {
01090   if ( !m_profile )
01091     return 0;
01092 
01093   if ( m_profile_count < 1 )
01094     return 0;
01095 
01096   if ( m_profile_count > 1 )
01097   {
01098     const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile);
01099     if ( 0 == polycurve )
01100       return 0;
01101     if ( polycurve->Count() != m_profile_count )
01102       return 0;
01103   }
01104 
01105   return m_profile_count;
01106 }
01107 
01108 
01109 int ON_Extrusion::GetProfileCurves( ON_SimpleArray< const ON_Curve* >& profile_curves ) const
01110 {
01111   if ( !m_profile )
01112     return 0;
01113 
01114   if ( m_profile_count < 1 )
01115     return 0;
01116 
01117   if ( 1 == m_profile_count )
01118   {
01119     profile_curves.Reserve(profile_curves.Count()+1);
01120     profile_curves.Append(m_profile);
01121   }
01122   else
01123   {
01124     const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile);
01125     if ( 0 == polycurve )
01126       return 0;
01127     if ( polycurve->Count() != m_profile_count )
01128       return 0;
01129     const int count0 = profile_curves.Count();
01130     profile_curves.Reserve(count0+m_profile_count);
01131     for ( int i = 0; i < m_profile_count; i++ )
01132     {
01133       const ON_Curve* segment = polycurve->SegmentCurve(i);
01134       if ( 0 == segment )
01135       {
01136         profile_curves.SetCount(count0);
01137         return 0;
01138       }
01139       profile_curves.Append(segment);
01140     }
01141   }
01142 
01143   return m_profile_count;
01144 }
01145 
01146 
01147 const double ON_Extrusion::m_Nz_min = 1.0/64.0;
01148 
01149 const double ON_Extrusion::m_path_length_min = ON_ZERO_TOLERANCE;
01150 
01151 ON_OBJECT_IMPLEMENT(ON_Extrusion,ON_Surface,"36F53175-72B8-4d47-BF1F-B4E6FC24F4B9");
01152 
01153 ON_Extrusion::ON_Extrusion()
01154 {
01155   ON_ExtrusionInitializeHelper(*this);
01156 }
01157 
01158 ON_Extrusion::ON_Extrusion(const ON_Extrusion& src) : ON_Surface(src), m_profile(0)
01159 {
01160   ON_ExtrusionCopyHelper(src,*this);
01161 }
01162 
01163 ON_Extrusion::~ON_Extrusion()
01164 {
01165   if ( m_profile)
01166   {
01167     delete m_profile;
01168   }
01169 }
01170 
01171 ON_Extrusion& ON_Extrusion::operator=(const ON_Extrusion& src)
01172 {
01173   if ( this != &src )
01174   {
01175     Destroy();
01176     ON_Surface::operator=(src);
01177     ON_ExtrusionCopyHelper(src,*this);
01178   }
01179   return *this;
01180 }
01181 
01182 static
01183 void ON_Extrusion_IsNotValidMessage( ON_TextLog* text_log, const char* msg )
01184 {
01185   // this is a good place for a breakpoint
01186   if ( text_log && msg && msg[0] )
01187     text_log->Print("%s\n",msg);
01188 }
01189 
01190 static bool ON_ExtrusionIsNotValid()
01191 {
01192   return ON_IsNotValid(); // good place for a breakpoint
01193 }
01194 
01195 ON_BOOL32 ON_Extrusion::IsValid( ON_TextLog* text_log ) const
01196 {
01197   // check m_profile
01198   if ( m_profile_count < 1 )
01199   {
01200     ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count < 1.");
01201     return ON_ExtrusionIsNotValid();
01202   }
01203   if ( !m_profile )
01204   {
01205     ON_Extrusion_IsNotValidMessage(text_log,"m_profile is NULL.");
01206     return ON_ExtrusionIsNotValid();
01207   }
01208   if ( m_profile_count > 1 )
01209   {
01210     const ON_PolyCurve* c = ON_PolyCurve::Cast(m_profile);
01211     if ( 0 == c )
01212     {
01213       ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but m_profile is not an ON_PolyCurve.");
01214       return ON_ExtrusionIsNotValid();
01215     }
01216     if ( m_profile_count != c->Count() )
01217     {
01218       ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but m_profile_count != m_profile->SegmentCount().");
01219       return ON_ExtrusionIsNotValid();
01220     }
01221 
01222     if ( !ON_Extrusion::IsValidPolyCurveProfile(*c,text_log) )
01223     {
01224       ON_Extrusion_IsNotValidMessage(text_log,"m_profile is not a valid ON_PolyCurve.");
01225       return ON_ExtrusionIsNotValid();
01226     }
01227     for ( int i = 0; i < m_profile_count; i++ )
01228     {
01229       const ON_Curve* segment = c->SegmentCurve(i);
01230       if ( 0 == segment )
01231       {
01232         ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but a m_profile_count->SegmentCurve() is null.");
01233         return ON_ExtrusionIsNotValid();
01234       }
01235       if ( !segment->IsClosed() )
01236       {
01237         ON_Extrusion_IsNotValidMessage(text_log,"m_profile_count > 1 but a m_profile_count->SegmentCurve() is not closed.");
01238         return ON_ExtrusionIsNotValid();
01239       }
01240     }
01241   }
01242   else if ( !m_profile->IsValid(text_log) )
01243   {
01244     ON_Extrusion_IsNotValidMessage(text_log,"m_profile is not valid.");
01245     return ON_ExtrusionIsNotValid();
01246   }
01247 
01248   // check m_path
01249   if ( !m_path.IsValid() )
01250   {
01251     ON_Extrusion_IsNotValidMessage(text_log,"m_path is not valid.");
01252     return ON_ExtrusionIsNotValid();
01253   }
01254   ON_3dVector D = m_path.to - m_path.from;
01255   double len = D.Length();
01256   if ( !ON_IsValid(len) || len <= 0.0 )
01257   {
01258     ON_Extrusion_IsNotValidMessage(text_log,"m_path has zero length.");
01259     return ON_ExtrusionIsNotValid();
01260   }
01261   if ( !ON_IsValid(len) || len <= ON_Extrusion::m_path_length_min )
01262   {
01263     if ( text_log )
01264       text_log->Print("m_path has zero length <= ON_Extrusion::m_path_length_min.");
01265     return ON_ExtrusionIsNotValid();
01266   }
01267   if ( !D.Unitize() || !D.IsUnitVector() )
01268   {
01269     ON_Extrusion_IsNotValidMessage(text_log,"m_path has zero direction.");
01270     return ON_ExtrusionIsNotValid();
01271   }
01272   
01273   // check m_t
01274   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) )
01275   {
01276     ON_Extrusion_IsNotValidMessage(text_log,"m_t does not satisfy 0<=m_t[0]<m_t[1]<=1");
01277     return ON_ExtrusionIsNotValid();
01278   }
01279 
01280   // check m_up
01281   if ( !m_up.IsUnitVector() )
01282   {
01283    ON_Extrusion_IsNotValidMessage(text_log,"m_up is not a unit vector.");
01284     return ON_ExtrusionIsNotValid();
01285   }
01286   len = m_up*D;
01287   if ( fabs(len) > ON_SQRT_EPSILON )
01288   {
01289     ON_Extrusion_IsNotValidMessage(text_log,"m_up is not perpendicular to m_path.");
01290     return ON_ExtrusionIsNotValid();
01291   }
01292 
01293   // validate ends
01294   if ( m_bHaveN[0] )
01295   {
01296     if ( !m_N[0].IsUnitVector() )
01297     {
01298       ON_Extrusion_IsNotValidMessage(text_log,"m_N[0] is not a unit vector.");
01299       return ON_ExtrusionIsNotValid();
01300     }
01301     if ( !(m_N[0].z > ON_Extrusion::m_Nz_min) )
01302     {
01303       ON_Extrusion_IsNotValidMessage(text_log,"m_N[0].z is too small (<=ON_Extrusion::m_Nz_min) or negative");
01304       return ON_ExtrusionIsNotValid();
01305     }
01306   }
01307   if ( m_bHaveN[1] )
01308   {
01309     if ( !m_N[1].IsUnitVector() )
01310     {
01311       ON_Extrusion_IsNotValidMessage(text_log,"m_N[1] is not a unit vector.");
01312       return ON_ExtrusionIsNotValid();
01313     }
01314     if ( !(m_N[1].z > ON_Extrusion::m_Nz_min) )
01315     {
01316       ON_Extrusion_IsNotValidMessage(text_log,"m_N[1].z is too small (<=ON_Extrusion::m_Nz_min) or negative");
01317       return ON_ExtrusionIsNotValid();
01318     }
01319   }
01320 
01321   return true;
01322 }
01323 
01324 void ON_Extrusion::Dump( ON_TextLog& text_log ) const
01325 {
01326   text_log.Print("ON_Extrusion: \n");
01327   {
01328     text_log.PushIndent();
01329   text_log.Print("Path: ");
01330   text_log.Print(m_path.PointAt(m_t[0]));
01331   text_log.Print(" ");
01332   text_log.Print(m_path.PointAt(m_t[1]));
01333   text_log.Print("\n");
01334   text_log.Print("Up: ");
01335   text_log.Print(m_up);
01336   text_log.Print("\n");
01337   text_log.Print("m_bCap[] = (%d, %d)\n",m_bCap[0],m_bCap[1]);
01338   text_log.Print("m_bHaveN[] = (%d, %d)\n",m_bHaveN[0],m_bHaveN[1]);
01339   text_log.Print("m_N[] = (");
01340   text_log.Print(m_N[0]);
01341   text_log.Print(", ");
01342   text_log.Print(m_N[1]);
01343   text_log.Print("\n");
01344   text_log.Print("m_path_domain = (%.17g, %.17g)\n",m_path_domain[0],m_path_domain[1]);
01345   text_log.Print("m_bTransposed = %d\n",m_bTransposed);
01346   text_log.Print("Profile Count: %d\n",m_profile_count);
01347   text_log.Print("Profile:\n");
01348     {
01349   text_log.PushIndent();
01350   if ( !m_profile )
01351     text_log.Print("NULL");
01352   else
01353     m_profile->Dump(text_log);
01354   text_log.PopIndent();
01355     }
01356     text_log.PopIndent();
01357   }
01358   return;
01359 }
01360 
01361 unsigned int ON_Extrusion::SizeOf() const
01362 {
01363   unsigned int sz = sizeof(*this) - sizeof(ON_Surface);
01364   if ( m_profile )
01365     sz += m_profile->SizeOf();
01366   return sz;
01367 }
01368 
01369 ON__UINT32 ON_Extrusion::DataCRC(ON__UINT32 current_remainder) const
01370 {
01371   if ( m_profile )
01372     current_remainder = m_profile->DataCRC(current_remainder);
01373   current_remainder = ON_CRC32(current_remainder,sizeof(m_path),&m_path);
01374   current_remainder = ON_CRC32(current_remainder,sizeof(m_t),&m_t);
01375   current_remainder = ON_CRC32(current_remainder,sizeof(m_up),&m_up);
01376   current_remainder = ON_CRC32(current_remainder,sizeof(m_bHaveN[0]), &m_bHaveN[0]);
01377   current_remainder = ON_CRC32(current_remainder,sizeof(m_bHaveN[1]), &m_bHaveN[1]);
01378   current_remainder = ON_CRC32(current_remainder,sizeof(m_N[0]), &m_N[0]);
01379   current_remainder = ON_CRC32(current_remainder,sizeof(m_N[1]), &m_N[1]);
01380   current_remainder = ON_CRC32(current_remainder,sizeof(m_path_domain), &m_path_domain);
01381   current_remainder = ON_CRC32(current_remainder,sizeof(m_bTransposed), &m_bTransposed);
01382   current_remainder = ON_CRC32(current_remainder,sizeof(m_profile_count), &m_profile_count);
01383   current_remainder = ON_CRC32(current_remainder,sizeof(m_bCap[0]), &m_bCap[0]);
01384   current_remainder = ON_CRC32(current_remainder,sizeof(m_bCap[1]), &m_bCap[1]);
01385   if ( m_profile )
01386     current_remainder = m_profile->DataCRC(current_remainder);
01387   return current_remainder;
01388 }
01389 
01390 ON_BOOL32 ON_Extrusion::Write(
01391        ON_BinaryArchive& binary_archive
01392      ) const
01393 {
01394   bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,2);
01395   if (!rc)
01396     return false;
01397   for (;;)
01398   {
01399     rc = binary_archive.WriteObject(m_profile);
01400     if (!rc) break;
01401     rc = binary_archive.WriteLine(m_path);
01402     if (!rc) break;
01403     rc = binary_archive.WriteInterval(m_t);
01404     if (!rc) break;
01405     rc = binary_archive.WriteVector(m_up);
01406     if (!rc) break;
01407     rc = binary_archive.WriteBool(m_bHaveN[0]);
01408     if (!rc) break;
01409     rc = binary_archive.WriteBool(m_bHaveN[1]);
01410     if (!rc) break;
01411     rc = binary_archive.WriteVector(m_N[0]);
01412     if (!rc) break;
01413     rc = binary_archive.WriteVector(m_N[1]);
01414     if (!rc) break;
01415     rc = binary_archive.WriteInterval(m_path_domain);
01416     if (!rc) break;
01417     rc = binary_archive.WriteBool(m_bTransposed);
01418     if (!rc) break;
01419     // 1.1 
01420     rc = binary_archive.WriteInt(m_profile_count);
01421     if (!rc) break;
01422     // 1.2
01423     rc = binary_archive.WriteBool(m_bCap[0]);
01424     if (!rc) break;
01425     rc = binary_archive.WriteBool(m_bCap[1]);
01426     if (!rc) break;
01427 
01428     break;
01429   }
01430   if ( !binary_archive.EndWrite3dmChunk() )
01431     rc = false;
01432   return rc;
01433 }
01434 
01435 
01436 ON_BOOL32 ON_Extrusion::Read(
01437        ON_BinaryArchive& binary_archive
01438      )
01439 {
01440   Destroy();
01441   int major_version = 0;
01442   int minor_version = 0;
01443   bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
01444   if (!rc)
01445     return false;
01446   for (;;)
01447   {
01448     rc = (1 == major_version);
01449     if (!rc) break;
01450     ON_Object* obj = 0;
01451     rc = (1==binary_archive.ReadObject(&obj));
01452     if (!rc) break;
01453     if ( obj )
01454     {
01455       m_profile = ON_Curve::Cast(obj);
01456       if ( !m_profile )
01457       {
01458         delete obj;
01459         rc = false;
01460         break;
01461       }
01462     }
01463     rc = binary_archive.ReadLine(m_path);
01464     if (!rc) break;
01465     rc = binary_archive.ReadInterval(m_t);
01466     if (!rc) break;
01467     rc = binary_archive.ReadVector(m_up);
01468     if (!rc) break;
01469     rc = binary_archive.ReadBool(&m_bHaveN[0]);
01470     if (!rc) break;
01471     rc = binary_archive.ReadBool(&m_bHaveN[1]);
01472     if (!rc) break;
01473     rc = binary_archive.ReadVector(m_N[0]);
01474     if (!rc) break;
01475     rc = binary_archive.ReadVector(m_N[1]);
01476     if (!rc) break;
01477     rc = binary_archive.ReadInterval(m_path_domain);
01478     if (!rc) break;
01479     rc = binary_archive.ReadBool(&m_bTransposed);
01480     if (!rc) break;
01481 
01482     // set profile count if extrusion was read from an old file.
01483     m_profile_count = (0 != m_profile) ? 1 : 0;
01484 
01485     if ( minor_version >= 1 )
01486     {
01487       // 1.1 
01488       rc = binary_archive.ReadInt(&m_profile_count);
01489       if (!rc) break;
01490 
01491       if ( minor_version >= 2 )
01492       {
01493         // 1.2
01494         rc = binary_archive.ReadBool(&m_bCap[0]);
01495         if (!rc) break;
01496         rc = binary_archive.ReadBool(&m_bCap[1]);
01497         if (!rc) break;
01498       }
01499     }
01500 
01501     if ( minor_version < 2 )
01502     {
01503       const ON_Curve* outer_profile = Profile(0);
01504       if ( 0 != outer_profile && outer_profile->IsClosed() )
01505       {
01506         m_bCap[0] = m_bCap[1] = true;
01507       }
01508     }
01509 
01510     break;
01511   }
01512   if ( !binary_archive.EndRead3dmChunk() )
01513     rc = false;
01514   return rc;
01515 }
01516 
01517 ON::object_type ON_Extrusion::ObjectType() const
01518 {
01519   return ON::extrusion_object;
01520 }
01521 
01522 
01523 
01524 int ON_Extrusion::Dimension() const
01525 {
01526   return 3;
01527 }
01528 
01529 static 
01530 bool GetBoundingBoxHelper(const ON_Extrusion& extrusion, 
01531                           ON_BoundingBox& bbox,
01532                           const ON_Xform* xform
01533                           )
01534 {
01535   // input bbox = profile curve bounding box.
01536   ON_3dPoint corners[8];
01537   bbox.m_min.z = 0.0;
01538   bbox.m_max.z = 0.0;
01539   corners[0] = corners[1] = bbox.m_min;
01540   corners[1].x = bbox.m_max.x;
01541   corners[2] = corners[3] = bbox.m_max;
01542   corners[3].x = bbox.m_min.x;
01543   corners[4] = corners[0];
01544   corners[5] = corners[1];
01545   corners[6] = corners[2];
01546   corners[7] = corners[3];
01547 
01548   ON_Xform xform0;
01549   if ( !extrusion.GetProfileTransformation(0,xform0) )
01550     return false;
01551   ON_Xform xform1;
01552   if ( !extrusion.GetProfileTransformation(1,xform1) )
01553     return false;
01554   if ( xform && !xform->IsIdentity() )
01555   {
01556     xform0 = (*xform)*xform0;
01557     xform1 = (*xform)*xform1;
01558   }
01559   corners[0] = xform0*corners[0];
01560   corners[1] = xform0*corners[1];
01561   corners[2] = xform0*corners[2];
01562   corners[3] = xform0*corners[3];
01563   corners[4] = xform1*corners[4];
01564   corners[5] = xform1*corners[5];
01565   corners[6] = xform1*corners[6];
01566   corners[7] = xform1*corners[7];
01567   bbox.Set(3,0,8,3,&corners[0].x,false);
01568   return true;
01569 }
01570 
01571 ON_BOOL32 ON_Extrusion::GetBBox(double* boxmin,double* boxmax,int bGrowBox) const
01572 {
01573   bool rc = false;
01574   if ( m_path.IsValid() && m_profile )
01575   {
01576     ON_BoundingBox bbox;
01577     if ( m_profile->GetTightBoundingBox(bbox) && GetBoundingBoxHelper(*this,bbox,0) )
01578     {
01579       rc = true;
01580       if ( bGrowBox )
01581       {
01582         bGrowBox =  ( boxmin[0] <= boxmax[0] 
01583                       && boxmin[1] <= boxmax[1] 
01584                       && boxmin[2] <= boxmax[2]
01585                       && ON_IsValid(boxmax[0]) 
01586                       && ON_IsValid(boxmax[1]) 
01587                       && ON_IsValid(boxmax[2]));
01588       }
01589       if ( bGrowBox )
01590       {
01591         if ( boxmin[0] > bbox.m_min.x ) boxmin[0] = bbox.m_min.x;
01592         if ( boxmin[1] > bbox.m_min.y ) boxmin[1] = bbox.m_min.y;
01593         if ( boxmin[2] > bbox.m_min.z ) boxmin[2] = bbox.m_min.z;
01594         if ( boxmax[0] < bbox.m_max.x ) boxmax[0] = bbox.m_max.x;
01595         if ( boxmax[1] < bbox.m_max.y ) boxmax[1] = bbox.m_max.y;
01596         if ( boxmax[2] < bbox.m_max.z ) boxmax[2] = bbox.m_max.z;
01597       }
01598       else
01599       {
01600         boxmin[0] = bbox.m_min.x;
01601         boxmin[1] = bbox.m_min.y;
01602         boxmin[2] = bbox.m_min.z;
01603         boxmax[0] = bbox.m_max.x;
01604         boxmax[1] = bbox.m_max.y;
01605         boxmax[2] = bbox.m_max.z;
01606       }
01607     }
01608   }
01609   return rc;
01610 }
01611 
01612 
01613 bool ON_Extrusion::GetTightBoundingBox(ON_BoundingBox& tight_bbox, int bGrowBox, const ON_Xform* xform ) const
01614 {
01615   bool rc = false;
01616   if ( m_path.IsValid() && m_profile )
01617   {
01618     ON_BoundingBox bbox;
01619     if ( m_profile->GetTightBoundingBox(bbox) && GetBoundingBoxHelper(*this,bbox,xform) )
01620     {
01621       if ( bGrowBox )
01622         tight_bbox.Union(bbox);
01623       else
01624         tight_bbox = bbox;
01625       rc = true;
01626     }
01627   }
01628   return rc;
01629 }
01630 
01631 static bool ON_Extrusion_TransformFailed()
01632 {
01633   return false; // good place for a breakpoint
01634 }
01635 
01636 static bool Profile2dTransform( ON_Extrusion& e, const ON_Xform& profile_xform, bool bNeedReverse )
01637 {
01638   if ( profile_xform.IsIdentity() )
01639   {
01640     // profile curves are unchanged (translation, rotation, 1D scale along path, ... )
01641     return true; 
01642   }
01643 
01644   // transform 2d profiles
01645   bool rc = false;
01646   bool bNeedDeformable = (    fabs(profile_xform.m_xform[0][0]) != fabs(profile_xform.m_xform[1][1]) 
01647                            || 0.0 != profile_xform.m_xform[1][0] 
01648                          );
01649   ON_PolyCurve* polyprofile = const_cast<ON_PolyCurve*>(e.PolyProfile());
01650   if ( 0 != polyprofile )
01651   {
01652     rc = true;
01653     if ( bNeedDeformable )
01654       polyprofile->MakeDeformable();
01655     for ( int i = 0; i < polyprofile->Count(); i++ )
01656     {
01657       ON_Curve* profile_segment = polyprofile->SegmentCurve(i);
01658       if ( 0 == profile_segment )
01659       {
01660         continue;
01661       }
01662       if ( !profile_segment->Transform(profile_xform) )
01663       {
01664         rc = ON_Extrusion_TransformFailed();
01665         continue;
01666       }
01667       if ( bNeedReverse )
01668       {
01669         double t0, t1;
01670         if ( profile_segment->GetDomain(&t0,&t1) )
01671         {
01672           // reverse the segment so the correct clounterclockwise/clockwise
01673           // orientation is maintained after reflection across the y axis.
01674           profile_segment->Reverse();
01675           // preserve the evaluation domain of each segment
01676           profile_segment->SetDomain(t0,t1);
01677         }
01678       }
01679     }
01680   }
01681   else
01682   {
01683     if ( bNeedDeformable && !e.m_profile->IsDeformable() )
01684     {
01685       ON_NurbsCurve* c = e.m_profile->NurbsCurve();
01686       if ( 0 != c )
01687       {
01688         c->CopyUserData(*e.m_profile);
01689         if( c->Transform(profile_xform) )
01690         {
01691           rc = true;
01692           delete e.m_profile;
01693           e.m_profile = c;
01694         }
01695         else
01696         {
01697           delete c;
01698           rc = ON_Extrusion_TransformFailed();
01699         }
01700       }
01701       else
01702       {
01703         rc = ON_Extrusion_TransformFailed();
01704       }
01705     }
01706     else
01707     {
01708       rc = e.m_profile->Transform(profile_xform)?true:ON_Extrusion_TransformFailed();
01709     }
01710     if ( rc && bNeedReverse )
01711     {
01712       double t0, t1;
01713       if ( e.m_profile->GetDomain(&t0,&t1) )
01714       {
01715         // reverse the segment so the correct clounterclockwise/clockwise
01716         // orientation is maintained after reflection across the y axis.
01717         e.m_profile->Reverse();
01718         // preserve the evaluation domain of each segment
01719         e.m_profile->SetDomain(t0,t1);
01720       }
01721     }
01722   }
01723 
01724   return rc;
01725 }
01726 
01727 ON_BOOL32 ON_Extrusion::Transform( const ON_Xform& xform )
01728 {
01729   if ( !m_path.IsValid() || !m_up.IsUnitVector() || m_profile_count < 1 )
01730     return ON_Extrusion_TransformFailed();
01731 
01732   ON_3dVector T = m_path.Tangent();
01733   ON_3dVector UxT = ON_CrossProduct(m_up,T);
01734   if ( !UxT.IsUnitVector() && !UxT.Unitize() )
01735     return ON_Extrusion_TransformFailed();
01736 
01737   const bool bUseVectorXform = (0.0 == xform[3][0] && 0.0 == xform[3][1] && 0.0 == xform[3][2]);
01738 
01739   ON_3dPoint E[2], QE[2];
01740   E[0] = m_path.from;
01741   E[1] = m_path.to;
01742   QE[0] = xform*E[0];
01743   QE[1] = xform*E[1];
01744   if ( !QE[0].IsValid() )
01745     return ON_Extrusion_TransformFailed();
01746   if ( !QE[1].IsValid() )
01747     return ON_Extrusion_TransformFailed();
01748   ON_3dVector QT0 = bUseVectorXform ? (xform*(E[1] - E[0])) : (QE[1]-QE[0]);
01749   if ( !QT0.Unitize() )
01750     return ON_Extrusion_TransformFailed();
01751   
01752   const int base_index = ( QE[1].DistanceTo(E[1]) < QE[0].DistanceTo(E[0]) ) ? 1 : 0;
01753   const ON_3dPoint B(E[base_index]);
01754   const ON_3dPoint QB(QE[base_index]);
01755 
01756   const double QT0oT = QT0*T;
01757   bool bSamePathDir = ( fabs(fabs(QT0oT) - 1.0) <= ON_SQRT_EPSILON );
01758   ON_3dVector QT = bSamePathDir ? ((QT0oT < 0.0) ? -T : T) : QT0;
01759   const double Qlen = QE[0].DistanceTo(QE[1]);
01760   if ( bSamePathDir )
01761   {
01762     ON_3dPoint R =  QB + (base_index ? -Qlen : Qlen)*QT;
01763     if ( QE[1-base_index].DistanceTo( R ) <= ON_SQRT_EPSILON*Qlen )
01764     {
01765       QE[1-base_index] = R;
01766     }
01767     else
01768     {
01769       bSamePathDir = false;
01770       QT = QT0;
01771     }
01772   }
01773 
01774   const ON_3dPoint X0 = xform*(B + UxT);
01775   const ON_3dPoint Y0 = xform*(B + m_up);
01776   const ON_3dVector QDY = bUseVectorXform ? (xform*m_up) : (Y0-QB);
01777   ON_3dVector QY = QDY;
01778   if ( !QY.Unitize() )
01779     return ON_Extrusion_TransformFailed();
01780   ON_3dVector QU0 = QDY - (QDY*QT)*QT;
01781   if ( !QU0.Unitize() )
01782     return ON_Extrusion_TransformFailed();
01783 
01784   const double QU0oU = QU0*m_up;
01785   bool bSameUpDir = ( fabs(fabs(QU0oU) - 1.0) <= ON_SQRT_EPSILON );
01786   ON_3dVector QU = bSameUpDir ? ((QU0oU < 0.0) ? -m_up : m_up) : QU0;
01787 
01788   if (bSameUpDir && !bSamePathDir && fabs(QU*QT) > fabs(QU0*QT) )
01789   {
01790     // 12 July 2008 Dale Lear - this test fixes http://dev.mcneel.com/bugtrack/?q=88130
01791     bSameUpDir = false;
01792     QU = QU0;
01793   }
01794 
01795   ON_3dVector QUxQT = ON_CrossProduct(QU,QT);
01796   if ( !QUxQT.Unitize() )
01797     return ON_Extrusion_TransformFailed();
01798 
01799   const double QUxQToUxT = QUxQT*UxT;
01800   if ( (bSamePathDir && bSameUpDir) || fabs(fabs(QUxQToUxT) - 1.0) <= ON_SQRT_EPSILON )
01801   {
01802     if ( QUxQToUxT < 0.0 )
01803       QUxQT = -UxT;
01804     else
01805       QUxQT = UxT;
01806   }
01807 
01808   const double QUoQY = QU*QY;
01809   if ( fabs(QUoQY - 1.0) < ON_SQRT_EPSILON )
01810     QY = QU;
01811   else if ( fabs(QUoQY + 1.0) < ON_SQRT_EPSILON )
01812     QY = -QU;
01813 
01814   // profile_xform will be the transformation 
01815   // applied to the 2d profile curve.
01816   const ON_3dVector QDX = bUseVectorXform ? (xform*UxT) : (X0 - QB);
01817   ON_3dVector QX = QDX - (QDX*QY)*QY;
01818   if ( !QX.Unitize() )
01819     return ON_Extrusion_TransformFailed();
01820 
01821   const double QUxQToQX = QUxQT*QX;
01822   if ( fabs(QUxQToQX - 1.0) < ON_SQRT_EPSILON )
01823     QX = QUxQT;
01824   else if ( fabs(QUxQToQX + 1.0) < ON_SQRT_EPSILON )
01825     QX = -QUxQT;
01826 
01827 
01828   ON_3dVector QXxQY = ON_CrossProduct(QX,QY);
01829   if ( !QXxQY.IsUnitVector() && !QXxQY.Unitize() )
01830     return ON_Extrusion_TransformFailed();
01831   if ( QXxQY*QT < 0.0 )
01832   {
01833     QX.Reverse();
01834     QXxQY.Reverse();
01835   }
01836 
01837   ON_3dVector path_shear(0.0,QU*QXxQY,QT*QXxQY);
01838   bool bHavePathShear =    path_shear.z > ON_Extrusion::m_Nz_min 
01839                         && path_shear.z < 1.0 - ON_SQRT_EPSILON
01840                         && path_shear.y < 1.0 - ON_SQRT_EPSILON;
01841   if ( bHavePathShear )
01842   {
01843     double x2 = path_shear.y*path_shear.y + path_shear.z*path_shear.z;
01844     if ( x2 > ON_SQRT_EPSILON && x2 <= 1.0 )
01845     {
01846       double QUxQToQXxQY = QUxQT*QXxQY;
01847       path_shear.x = sqrt(1.0 - x2);
01848       if ( QUxQToQXxQY < 0.0 )
01849         path_shear.x = -path_shear.x;
01850     }
01851     if ( fabs(path_shear.y) <= ON_SQRT_EPSILON )
01852       path_shear.y = 0.0;
01853     bHavePathShear = path_shear.IsUnitVector() && (path_shear.x != 0.0 || path_shear.y != 0.0);
01854   }
01855 
01856   if ( !bHavePathShear )
01857   {
01858     path_shear.Set(0.0,0.0,1.0);
01859     QXxQY = QT;
01860   }
01861 
01862   // miter normals
01863   ON_3dVector QN[2] = {ON_3dVector::ZeroVector,ON_3dVector::ZeroVector};
01864   bool bHaveQN[2] = {false,false};
01865   bool bUseQN[2] = {false,false};
01866   for ( int i = 0; i < 2; i++ )
01867   {
01868     if ( m_bHaveN[i] )
01869     {
01870       QN[i] = m_N[i];
01871       bHaveQN[i] = true;
01872       bUseQN[i] = true;
01873 
01874       // In order for this to work with  transformations that
01875       // have bHavePathShear = true, we have to see where the
01876       // miter *plane* is mapped which, for a shear 
01877       // transformation, cannot be done by transforming 
01878       // m_N[i] = normal to the miter plane.
01879       ON_3dVector QN3d;
01880       if ( bHavePathShear )
01881       {
01882         // calculate a world 3d basis for the miter plane
01883         ON_3dVector V1(m_N[i].y,-m_N[i].x,0.0);
01884         V1.Unitize();
01885         ON_3dVector V2 = ON_CrossProduct( m_N[i],V1);
01886         V1 = V1.x*UxT + V1.y*m_up + V1.z*T;
01887         V2 = V2.x*UxT + V2.y*m_up + V2.z*T;
01888 
01889         // transform the basis to get a world 3d basis
01890         // for the transformed miter plane.
01891         ON_3dVector QV1 = xform*V1;
01892         ON_3dVector QV2 = xform*V2;
01893         double lenQV1 = QV1.Length();
01894         double lenQV2 = QV2.Length();
01895         if ( fabs(lenQV1 - lenQV2) > 0.125*(lenQV1 + lenQV2) )
01896         {
01897           // if lengths are "different", then normalize
01898           // before taking the cross product. Otherwise,
01899           // skip normalization to get slightly
01900           // more precision in a calculation that is
01901           // already pretty fuzzy.
01902           QV1.Unitize();
01903           QV2.Unitize();
01904         }
01905 
01906         // now get a world 3d normal to the miter plane
01907         QN3d = ON_CrossProduct(QV1,QV2);
01908       }
01909       else
01910       {
01911         const ON_3dVector N3d = m_N[i].x*UxT + m_N[i].y*m_up + m_N[i].z*T;
01912         ON_3dPoint QTip = xform*(E[i] + N3d);
01913         QN3d = bUseVectorXform ? (xform*N3d) : (QTip - QE[i]);
01914       }
01915       if ( !QN3d.Unitize() )
01916         continue;
01917 
01918       // QN[i] = miter vector
01919       QN[i].x = QUxQT*QN3d;
01920       QN[i].y = QU*QN3d;
01921       QN[i].z = QT*QN3d;
01922       if ( QN[i].z < 0.0 )
01923         QN[i].Reverse(); // necessary for some mirror transformations
01924       if ( !QN[i].Unitize() )
01925       {
01926          bHaveQN[i] = false;
01927       }
01928       if ( fabs(QN[i].x) <= ON_SQRT_EPSILON )
01929         QN[i].x = 0.0;
01930       if ( fabs(QN[i].y) <= ON_SQRT_EPSILON )
01931         QN[i].y = 0.0;
01932       if (    QN[i].z < ON_Extrusion::m_Nz_min
01933            || QN[i].z >= 1.0 - ON_SQRT_EPSILON
01934            || (fabs(QN[i].x) <= ON_SQRT_EPSILON && fabs(QN[i].y) <= ON_SQRT_EPSILON )
01935          )
01936       {
01937         bHaveQN[i] = false;
01938       }
01939 
01940       if ( !bHaveQN[i] )
01941         QN[i] = ON_3dVector::ZeroVector;
01942       else if ( fabs(QN[i]*m_N[i] - 1.0) <= 1.0e-6 ) // ON_SQRT_EPSILON 
01943         QN[i] = m_N[i];
01944     }
01945   }
01946 
01947   // get 2d profile transformation
01948   ON_Xform profile_xform(1.0);
01949   // set 2d profile y scale
01950   const double profile_y_scale = QDY.Length(); // = QYoQDY "mathematically"
01951 
01952   if ( !bHavePathShear )
01953   {
01954     const double profile_x_scale = QX*QDX;
01955 
01956     if ( !ON_IsValid(profile_y_scale) || 0.0 == profile_y_scale )
01957     {
01958       // cannot flatten profile
01959       return ON_Extrusion_TransformFailed(); 
01960     } 
01961 
01962     if ( !ON_IsValid(profile_x_scale) || 0.0 == profile_x_scale )
01963     {
01964       // cannot flatten profile
01965       return ON_Extrusion_TransformFailed(); 
01966     } 
01967 
01968     // NOTE: 
01969     //   profile_xform.m_xform[1][1] must always be > 0.0 and
01970     //   is the most precise number number in the matrix.
01971     const double profile_y_scale_tol = ON_SQRT_EPSILON;
01972     const double profile_x_scale_tol = 10.0*profile_y_scale_tol;
01973 
01974     if ( fabs(profile_y_scale - 1.0) <= profile_y_scale_tol )
01975       profile_xform.m_xform[1][1] = 1.0;
01976     else
01977       profile_xform.m_xform[1][1] = profile_y_scale;
01978 
01979     if ( fabs( profile_x_scale - 1.0 ) <= profile_x_scale_tol )
01980       profile_xform.m_xform[0][0] = 1.0;
01981     else if ( fabs( profile_x_scale + 1.0 ) <= profile_x_scale_tol )
01982       profile_xform.m_xform[0][0] = -1.0;
01983     else
01984       profile_xform.m_xform[0][0] = profile_x_scale;
01985 
01986     const double profile_xform_tol = profile_x_scale_tol*(fabs(profile_xform.m_xform[0][0]) + profile_xform.m_xform[1][1]);
01987     if ( fabs(fabs(profile_xform.m_xform[0][0]) - profile_xform.m_xform[1][1]) <= profile_xform_tol )
01988     {
01989       profile_xform.m_xform[0][0] = ((profile_xform.m_xform[0][0] < 0.0)?-1.0:1.0)*profile_xform.m_xform[1][1];
01990     }
01991     else if ( fabs(profile_xform.m_xform[0][0]) <= profile_xform_tol )
01992     {
01993       // cannot flatten profile
01994       return ON_Extrusion_TransformFailed(); 
01995     }
01996 
01997     double profile_det = profile_xform.m_xform[0][0]*profile_xform.m_xform[1][1];
01998     if ( !ON_IsValid(profile_det) || 0.0 == profile_det )
01999       return ON_Extrusion_TransformFailed();
02000 
02001     // set 2d profile shear
02002     const double profile_shear_tol = profile_xform_tol;
02003     const double QYoQDX = QY*QDX;
02004     if ( fabs( QYoQDX ) <= profile_shear_tol )
02005       profile_xform.m_xform[1][0] = 0.0;
02006     else
02007       profile_xform.m_xform[1][0] = QYoQDX;
02008   }
02009   else
02010   {
02011     // plane0 is world 3d plane that provides the coordinate system
02012     // for mapping the original 2d profile into world 3d.
02013     ON_Plane plane0;
02014     plane0.origin = B; plane0.xaxis = UxT; plane0.yaxis = m_up;   plane0.zaxis = T;
02015     plane0.UpdateEquation();
02016 
02017     // plane1 is world 3d plane that provides the coordinate system
02018     // for mapping the transformed 2d profile into world 3d.
02019     ON_Plane plane1;
02020     plane1.origin = QB; plane1.xaxis = QUxQT; plane1.yaxis = QU;   plane1.zaxis = QT;
02021     plane1.UpdateEquation();
02022 
02023     ON_Xform xform1, xform3, xform4, xform5;
02024 
02025     // xform 1 maps original 2d profile to world 3d
02026     xform1.Rotation(ON_Plane::World_xy,plane0);
02027 
02028     // xform maps plane1 to 
02029 
02030     // xform 3 maps the transformed 3d profile to the world 3d plane
02031     // that is orthoganal to the transformed axis vector.
02032     xform3.PlanarProjection(plane1);
02033 
02034     // xform4 maps the transformed world 3d profile back to the xy plane
02035     xform4.Rotation(plane1, ON_Plane::World_xy);
02036 
02037     profile_xform = xform4*xform3*xform*xform1;
02038     profile_xform.m_xform[0][2] = 0.0;
02039     profile_xform.m_xform[1][2] = 0.0;
02040     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;
02041     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;
02042 
02043     const double xform_tol = 10.0*ON_SQRT_EPSILON;
02044     if ( profile_y_scale > xform_tol && fabs(profile_y_scale - 1.0) > xform_tol )
02045     {
02046       double profile_scale_tol = profile_y_scale*ON_SQRT_EPSILON;
02047       if ( fabs(profile_xform.m_xform[0][0] - profile_y_scale) < profile_scale_tol )
02048       {
02049         profile_xform.m_xform[0][0] = profile_y_scale;
02050         if ( fabs(profile_xform.m_xform[1][1] - profile_y_scale) < profile_scale_tol )
02051           profile_xform.m_xform[1][1] = profile_y_scale;
02052         else if ( fabs(profile_xform.m_xform[1][1] + profile_y_scale) < profile_scale_tol )
02053           profile_xform.m_xform[1][1] = -profile_y_scale;
02054       }
02055     }
02056 
02057     for ( int i = 0; i < 2; i++ ) for ( int j = 0; j < 4; j++ )
02058     {
02059       if ( 2 == j )
02060         continue;
02061       double x = fabs(profile_xform.m_xform[i][j]);
02062       if ( fabs(x) <= xform_tol )
02063         profile_xform.m_xform[i][j] = 0.0;
02064       else if ( fabs(1.0-x) <= xform_tol )
02065         profile_xform.m_xform[i][j] = 1.0;
02066       else if ( fabs(x-1.0) <= xform_tol )
02067         profile_xform.m_xform[i][j] = -1.0;
02068     }
02069   }
02070 
02071   // start transforming informtion here
02072   TransformUserData(xform);
02073 
02074   // transform path, up, and miter directions
02075   m_path.from = QE[0];
02076   m_path.to = QE[1];
02077   m_up = QU;
02078   for ( int i = 0; i < 2; i++ )
02079   {
02080     if ( bUseQN[i] )
02081     {
02082       m_N[i] = QN[i];
02083       m_bHaveN[i] = bHaveQN[i];
02084     }
02085     else if ( bHavePathShear )
02086     {
02087       m_N[i] = path_shear;
02088       m_bHaveN[i] = true;
02089     }
02090     else
02091     {
02092       m_N[i].Set(0.0,0.0,0.0);
02093       m_bHaveN[i] = false;
02094     }
02095   }
02096 
02097   //if ( m_bHaveN[0] && QN[0].IsValid() && QN[0].IsUnitVector() )
02098   //  m_N[0] = QN[0];
02099   //if ( m_bHaveN[1] && QN[1].IsValid() && QN[1].IsUnitVector() )
02100   //  m_N[1] = QN[1];
02101   //if ( bHavePathShear )
02102   //{
02103   //  // TODO: 
02104   //  //  1) combine with eisting mitering
02105   //  //  2) fix bug when eigenvector of the shear transform
02106   //  //     is not parallel to a profile plane axis.
02107   //  m_bHaveN[0] = true;
02108   //  m_N[0] = path_shear;
02109   //  m_bHaveN[1] = true;
02110   //  m_N[1] = path_shear;
02111   //}
02112 
02113   if ( profile_xform.IsIdentity() )
02114     return true; // should happen on all rotations and translations.
02115   
02116   double profile_det = profile_xform[0][0]*profile_xform[1][1] - profile_xform[1][0]*profile_xform[0][1];
02117   bool bNeedReverse = (profile_det < 0.0);
02118   return  Profile2dTransform( *this, profile_xform, bNeedReverse );
02119 }
02120 
02121 class CMyBrepIsSolidSetter : public ON_Brep
02122 {
02123 public:
02124   void SetIsSolid(int is_solid) {m_is_solid = is_solid;}
02125   void SetBBox( const ON_Extrusion& extrusion)
02126   {
02127     ON_BoundingBox brep_bbox = BoundingBox();
02128     ON_BoundingBox extr_bbox = extrusion.BoundingBox();
02129     ON_BoundingBox bbox;
02130     bbox.Intersection(brep_bbox,extr_bbox);
02131     m_bbox = bbox;
02132   }
02133 };
02134 
02135 class ON_Extrusion_BrepForm_FaceInfo
02136 {
02137 public:
02138   ON_Extrusion_BrepForm_FaceInfo();
02139 
02140   ~ON_Extrusion_BrepForm_FaceInfo();
02141 
02142   /*
02143   Returns
02144     false if m_face_index < 0.  This happens when the 3d profile
02145     for the extrusion is too short or bogus or the nurbs surface
02146     generated from this profile is bogus.  One way this happens
02147     is when the transformation from 2d to 3d has an enormous
02148     translation component.
02149   */
02150   bool HaveBrepFaceFace() const;
02151 
02152   void Init();
02153 
02154   // Information about the entire profile
02155   //   If this profile has kinks and we are splitting up
02156   //   kinky faces, then the ON_Extrusion_BrepForm_FaceInfo
02157   //   will be face made by extruding a smooth sub-curve
02158   //   of the entire profile.
02159   bool m_bClosedProfile;
02160   int m_profile_orientation;
02161   int m_profile_index;
02162 
02163   // Information for this wall face
02164   ON_Curve* m_extrusion_profile;
02165   ON_Extrusion* m_extrusion_srf;
02166   int m_face_index;
02167   int m_vid[4];
02168   int m_eid[4];
02169   ON_BOOL32 m_bRev3d[4];
02170 
02171   // capping information (bottom,top)
02172   int m_cap_trim_index[2]; // indices of wall trims
02173   int m_cap_edge_index[2];
02174   ON_NurbsCurve* m_cap_c2[2]; // 2d cap trim curves
02175 };
02176 
02177 ON_Extrusion_BrepForm_FaceInfo::ON_Extrusion_BrepForm_FaceInfo()
02178 {
02179   Init();
02180 }
02181 
02182 ON_Extrusion_BrepForm_FaceInfo::~ON_Extrusion_BrepForm_FaceInfo()
02183 {
02184   // when m_extrusion_srf is not null, its destructor deletes m_extrusion_profile.
02185   if ( 0 != m_extrusion_srf )
02186   {
02187     // When m_extrusion_srf is not null, it 
02188     // manages the m_extrusion_profile curve
02189     m_extrusion_profile = 0;
02190     delete m_extrusion_srf;
02191     m_extrusion_srf = 0;
02192   }
02193   
02194   if ( 0 != m_extrusion_profile )
02195   {
02196     delete m_extrusion_profile;
02197     m_extrusion_profile = 0;
02198   }
02199 
02200   if ( m_cap_c2[0] )
02201   {
02202     delete m_cap_c2[0];
02203     m_cap_c2[0] = 0;
02204   }
02205   
02206   if ( m_cap_c2[1] )
02207   {
02208     delete m_cap_c2[1];
02209     m_cap_c2[1] = 0;
02210   }
02211 
02212   memset(this,0,sizeof(*this));
02213 }
02214 
02215 bool ON_Extrusion_BrepForm_FaceInfo::HaveBrepFaceFace() const
02216 {
02217   if ( m_face_index < 0 )
02218     return false;
02219   return true;
02220 }
02221 
02222 void ON_Extrusion_BrepForm_FaceInfo::Init()
02223 {
02224   memset(this,0,sizeof(*this));
02225   
02226   m_bClosedProfile = false;
02227   m_profile_orientation = 0;
02228   m_profile_index = -1;
02229 
02230   m_extrusion_profile = 0;
02231   m_extrusion_srf = 0;
02232   m_face_index = -1;
02233   m_vid[0] = m_vid[1] = m_vid[2] = m_vid[3] = -1;
02234   m_eid[0] = m_eid[1] = m_eid[2] = m_eid[3] = -1;
02235   // Set the m_bRev3d[] flags so the 3d edges orientation
02236   // matches the surface iso-curve orientation.
02237   m_bRev3d[0] = m_bRev3d[1] = 0;
02238   m_bRev3d[2] = m_bRev3d[3] = 1;
02239 
02240   m_cap_trim_index[0] = m_cap_trim_index[1] = -1;
02241   m_cap_edge_index[0] = m_cap_edge_index[1] = -1;
02242   m_cap_c2[0] = m_cap_c2[1] = 0;
02243 }
02244 
02245 ON_Brep* ON_Extrusion::BrepForm( ON_Brep* brep ) const
02246 {
02247   return BrepForm(brep,true);
02248 }
02249 
02250 static 
02251 ON_PlaneSurface* MakeCapPlaneHelper(
02252     ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo >& finfo,
02253     int cap_index, // 0 = bottom, 1 = top
02254     const ON_Xform& rot 
02255     )
02256 {
02257   double d;
02258   ON_BoundingBox bbox;
02259 
02260   // get bounding box of trimming curves
02261   int outer_loop_trim_count = 0;
02262   int outer_loop_iso_trim_count = 0;
02263   for ( int i = 0; i < finfo.Count(); i++ )
02264   {
02265     if ( false == finfo[i].HaveBrepFaceFace() )
02266       continue; // profile curve segment was too short or bogus
02267 
02268     if ( 0 != finfo[i].m_profile_index )
02269     {
02270       // this and the rest of the finfo[] elements attach
02271       // attach to an inner boundary on the cap.
02272       break;
02273     }
02274     const ON_NurbsCurve* c2 = finfo[i].m_cap_c2[cap_index];
02275     if ( 0 == c2 )
02276       return 0;
02277     ON_BoundingBox c2bbox;
02278     c2->GetTightBoundingBox( c2bbox, false );
02279     if ( 0 == i )
02280       bbox = c2bbox;
02281     else
02282       bbox.Union(c2bbox);
02283     if ( outer_loop_iso_trim_count ==  outer_loop_trim_count )
02284     {
02285       // check for iso trims as long as all previous trims are iso trims.
02286       ON_3dVector D = c2bbox.Diagonal();
02287       double zero_tol = ON_ZERO_TOLERANCE;
02288       double nonzero_tol = 1000.0*ON_ZERO_TOLERANCE;
02289       if (     (D.x <= zero_tol && D.y > nonzero_tol)
02290             || (D.y <= zero_tol && D.x > nonzero_tol)
02291          )
02292       {
02293         outer_loop_iso_trim_count++;
02294       }
02295     }
02296     outer_loop_trim_count++;
02297   }
02298 
02299   if ( outer_loop_trim_count <= 0 )
02300   {
02301     // must have an outer boundary.
02302     return 0; 
02303   }
02304 
02305   ON_Interval u(bbox.m_min.x,bbox.m_max.x);
02306   ON_Interval v(bbox.m_min.y,bbox.m_max.y);
02307   
02308   if (    outer_loop_iso_trim_count < outer_loop_trim_count 
02309        || !u.IsIncreasing()
02310        || !v.IsIncreasing()
02311      )
02312   {
02313     // grow cap plane to extend a little beyond the trims
02314     d = u.Length();
02315     if ( 0.0 == d )
02316       d = 1.0; // happens when profile is a line
02317     d *= 0.125; u.m_t[0] -= d; u.m_t[1] += d;
02318     
02319     d = v.Length();
02320     if ( 0.0 == d )
02321       d = 1.0; // happens when profile is a line
02322     d *= 0.125; v.m_t[0] -= d; v.m_t[1] += d;
02323   }
02324 
02325   if ( !u.IsIncreasing() || !v.IsIncreasing() )
02326     return 0;
02327 
02328   ON_PlaneSurface* plane = new ON_PlaneSurface(ON_xy_plane);
02329   plane->SetExtents(0,u,true);
02330   plane->SetExtents(1,v,true);
02331   if ( !rot.IsIdentity() )
02332     plane->Transform(rot);
02333 
02334   return plane;
02335 }
02336 
02337 static 
02338 int MakeCapLoopHelper(
02339           ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo >& finfo,
02340           int fi0,
02341           int cap_index, // 0 = bottom, 1 = top
02342           ON_BrepFace* capface, 
02343           ON_BrepLoop::TYPE loop_type,
02344           bool* bTrimsWereModified
02345           )
02346 {
02347   ON_BrepEdge* edge;
02348 
02349   if ( 0 == capface )
02350     return fi0; // happens when extrusion has an open end
02351 
02352   ON_Brep* brep = capface->Brep();
02353   if ( 0 == brep )
02354     return fi0;
02355 
02356   if ( fi0 < 0 || fi0 >= finfo.Count() )
02357     return fi0;
02358 
02359   ON_BrepLoop& loop = brep->NewLoop(loop_type,*capface);
02360 
02361   bool bRev3d = false;
02362   bool bCloseGaps = true;
02363 
02364   int fi1;
02365   for ( fi1 = fi0; fi1 < finfo.Count(); fi1++ )
02366   {
02367     if ( false == finfo[fi1].HaveBrepFaceFace() )
02368       continue; // profile segment for this face was too short or bogus
02369 
02370     if ( finfo[fi0].m_profile_index != finfo[fi1].m_profile_index )
02371       break;
02372 
02373     ON_NurbsCurve* c2 = finfo[fi1].m_cap_c2[cap_index];
02374     if ( 0 == c2 )
02375     {
02376       bCloseGaps = false;
02377       break;
02378     }
02379     int c2i = brep->AddTrimCurve(c2);
02380     finfo[fi1].m_cap_c2[cap_index] = 0;
02381     edge = brep->Edge(finfo[fi1].m_cap_edge_index[cap_index]);
02382     if ( 0 == edge )
02383     {
02384       bCloseGaps = false;
02385       break;
02386     }
02387     ON_BrepTrim& trim = brep->NewTrim(*edge, bRev3d, loop, c2i);
02388     trim.m_tolerance[0] = trim.m_tolerance[1] = 0.0;
02389   }
02390   brep->SetTrimIsoFlags( loop );
02391 
02392   // 6 June 2012 Dale Lear
02393   //    Fix bug 105311
02394   //    Sometimes there are gaps in ON_PolyCurve profiles that
02395   //    need to be closed to make a valid ON_Brep.  The gaps
02396   //    need to be closed after the call to SetTrimIsoFlags().
02397   if ( bCloseGaps ) for ( int lti = 0; lti < loop.m_ti.Count(); lti++ )
02398   {
02399     ON_BrepTrim& trim0 = brep->m_T[loop.m_ti[lti]];
02400     ON_BrepTrim& trim1 = brep->m_T[loop.m_ti[(lti+1)%loop.m_ti.Count()]];
02401     if ( trim0.PointAtEnd() != trim1.PointAtStart() )
02402     {
02403       if ( brep->CloseTrimGap(trim0,trim1) )
02404       {
02405         edge = trim0.Edge();
02406         if ( edge )
02407           edge->m_tolerance = ON_UNSET_VALUE;
02408         edge = trim1.Edge();
02409         if ( edge )
02410           edge->m_tolerance = ON_UNSET_VALUE;
02411         if ( 0 != bTrimsWereModified )
02412           *bTrimsWereModified = true;
02413       }
02414     }
02415   }
02416 
02417   return fi1;
02418 }
02419 
02420 static
02421 bool MakeCap2dCurvesHelper(
02422         ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo >& finfo,
02423         int is_capped,
02424         const ON_Xform& scale0,
02425         const ON_Xform& scale1
02426         )
02427 {
02428   switch(is_capped)
02429   {
02430   case 1:
02431   case 2:
02432   case 3:
02433     break;
02434   default:
02435     return false;
02436   }
02437 
02438   for ( int i = 0; i < finfo.Count(); i++ )
02439   {
02440     ON_NurbsCurve* c20 = 0;
02441     ON_NurbsCurve* c21 = 0;
02442 
02443     if ( false == finfo[i].HaveBrepFaceFace() )
02444       continue; // profile curve was too short or bogus
02445 
02446     c20 = finfo[i].m_extrusion_srf->m_profile->NurbsCurve();
02447     if ( 0 == c20 )
02448       return false;
02449 
02450     c20->ChangeDimension(2);
02451     c20->MakePiecewiseBezier(true);
02452     if ( 2 == is_capped )
02453     {
02454       c21 = c20;
02455       c20 = 0;
02456     }
02457     else if ( 3 == is_capped )
02458     {
02459       c21 = c20->Duplicate();
02460     }
02461 
02462     if ( 0 != c20 && !scale0.IsIdentity() )
02463       c20->Transform(scale0);
02464     if ( 0 != c21 && !scale1.IsIdentity() )
02465       c21->Transform(scale1);
02466 
02467     finfo[i].m_cap_c2[0] = c20;
02468     finfo[i].m_cap_c2[1] = c21;
02469   }
02470 
02471   return true;
02472 }
02473 
02474 
02475 static bool GetNextProfileSegmentDiscontinuity(
02476                 const ON_Curve* profile_segment,
02477                 double t0,
02478                 double t1,
02479                 double *t
02480                 )
02481 {
02482   // Do NOT change ON_DEFAULT_ANGLE_TOLERANCE_COSINE to another value.
02483   // See comments in CRhinoDoc::AddObject() for details.
02484   if ( 0 == profile_segment )
02485     return false;
02486   return profile_segment->GetNextDiscontinuity(
02487                             ON::Gsmooth_continuous,
02488                             t0,t1,t,
02489                             0,0,
02490                             ON_DEFAULT_ANGLE_TOLERANCE_COSINE,
02491                             ON_SQRT_EPSILON
02492                             );
02493 }
02494 
02495 
02496 bool ON_Extrusion::ProfileIsKinked( int profile_index ) const
02497 {
02498   const ON_Curve* profile = Profile(profile_index);
02499   if ( 0 == profile )
02500     return false;
02501   double t0 = ON_UNSET_VALUE;
02502   double t1 = ON_UNSET_VALUE;
02503   double t;
02504   if ( !profile->GetDomain(&t0,&t1) )
02505     return 0;
02506   if ( !ON_IsValid(t0) || !(t0 < t1) )
02507     return 0;
02508   t  = t0;
02509   return (GetNextProfileSegmentDiscontinuity( profile, t0,t1, &t) && t0 < t && t < t1);
02510 }
02511 
02512 int ON_Extrusion::ProfileSmoothSegmentCount( int profile_index ) const
02513 {
02514   if ( 0 == Profile(profile_index) )
02515     return 0;
02516   ON_SimpleArray<double> * k  = 0;
02517   return (1 + GetProfileKinkParameters(profile_index,*k));
02518 }
02519 
02520 int ON_Extrusion::GetProfileKinkParameters( int profile_index, ON_SimpleArray<double>& profile_kink_parameters ) const
02521 {
02522   const ON_Curve* profile2d = Profile(profile_index);
02523   if ( 0 == profile2d )
02524     return 0;
02525   double t0 = ON_UNSET_VALUE;
02526   double t1 = ON_UNSET_VALUE;
02527   double t;
02528   if ( !profile2d->GetDomain(&t0,&t1) )
02529     return 0;
02530   if ( !ON_IsValid(t0) || !(t0 < t1) )
02531     return 0;
02532   ON_SimpleArray<double> * k = &profile_kink_parameters;
02533   int count = 0;
02534   while ( GetNextProfileSegmentDiscontinuity( profile2d, t0,t1, &t) )
02535   {
02536     if ( t0 < t && t < t1 )
02537     {
02538       if ( 0 != k )
02539       {
02540         k->Append(t);
02541         count++;
02542       }
02543       t0 = t;
02544     }
02545   }
02546   return count;
02547 }
02548 
02549 
02550 ON_Brep* ON_Extrusion::BrepForm( ON_Brep* brep, bool bSmoothFaces ) const
02551 {
02552   if ( brep )
02553     brep->Destroy();
02554 
02555   ON_SimpleArray<const ON_Curve*> profile_curves;
02556   const int profile_count = GetProfileCurves(profile_curves );
02557   if ( profile_count < 1 || profile_count != profile_curves.Count() )
02558     return 0;
02559 
02560   // get end cap transformation information
02561 
02562   const ON_3dVector T = m_path.Tangent();
02563   if ( !T.IsUnitVector() )
02564     return 0;
02565 
02566   ON_Xform xform0(1.0), xform1(1.0), scale0(1.0), scale1(1.0), rot0(1.0), rot1(1.0);
02567   if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,&scale0,&rot0) )
02568     return 0;
02569 
02570   if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,&scale1,&rot1) )
02571     return 0;
02572 
02573   ON_Brep* newbrep = brep ? brep : ON_Brep::New();
02574 
02575   if ( 0 == newbrep )
02576     return 0;
02577 
02578   int is_capped = IsCapped();
02579   if ( is_capped < 0 || is_capped > 3 )
02580     is_capped = 0; // don't let future changes or bugs in IsCapped() crash this code.
02581 
02582   int cap_count = (3==is_capped) ? 2 : (0 != is_capped ? 1 : 0);
02583 
02584   newbrep->m_S.Reserve(profile_count + cap_count);
02585   newbrep->m_F.Reserve(profile_count + cap_count);
02586   newbrep->m_L.Reserve((1 + cap_count)*profile_count);
02587 
02588   // Note: 
02589   //  If the profiles have kinks and bSplitKinkyFaces
02590   //  is true, then the m_C2, m_T, m_C3 and m_E arrays will
02591   //  generally be longer than these initial reservations.
02592   newbrep->m_C2.Reserve((4 + cap_count)*profile_count);
02593   newbrep->m_T.Reserve((4 + cap_count)*profile_count);
02594   newbrep->m_C3.Reserve(4*profile_count);
02595   newbrep->m_E.Reserve(4*profile_count);
02596 
02597 
02598   int vidmap[4] = {0,1,2,3};
02599   int eidmap[4] = {0,1,2,3};
02600   if ( m_bTransposed )
02601   {
02602     vidmap[1] = 3;
02603     vidmap[3] = 1;
02604     eidmap[0] = 3;
02605     eidmap[1] = 2;
02606     eidmap[2] = 1;
02607     eidmap[3] = 0;
02608   }
02609 
02610   int fi0, fi1;
02611 
02612   ON_ClassArray< ON_Extrusion_BrepForm_FaceInfo > finfo(2*profile_count);
02613   for ( int profile_index = 0; profile_index < profile_count; profile_index++ )
02614   {
02615     const ON_Curve* profile_segment = profile_curves[profile_index];
02616     if ( 0 == profile_segment )
02617     {
02618       if (newbrep != brep )
02619         delete newbrep;
02620       return 0;
02621     }
02622 
02623     fi0 = finfo.Count();
02624     ON_Extrusion_BrepForm_FaceInfo profile_fi;
02625     profile_fi.Init();
02626     {
02627       ON_Curve* newprofile = profile_segment->DuplicateCurve();
02628       if ( 0 == newprofile )
02629       {
02630         if (newbrep != brep )
02631           delete newbrep;
02632         return 0;
02633       }
02634 
02635       profile_fi.m_bClosedProfile = newprofile->IsClosed() ? true : false;
02636       profile_fi.m_profile_orientation = ( profile_fi.m_bClosedProfile )
02637                               ? ON_ClosedCurveOrientation(*newprofile,0)
02638                               : 0;
02639 
02640       if ( is_capped && profile_fi.m_profile_orientation != ((0==profile_index) ? 1 : -1) )
02641       {
02642         // do not attempt to cap extrusions with incorrectly oriented profiles
02643         is_capped = 0;
02644         cap_count = 0;
02645       }
02646 
02647       if ( bSmoothFaces )
02648       {
02649         // It is important to use the original profile, "profile_segment"
02650         // and not the trimmed copy in the GetNextDiscontinuity() loop
02651         // so the code that creates this brep divides the extrusion profile
02652         // exactly the same way as the code that calculates component indices.
02653         double t0 = ON_UNSET_VALUE;
02654         double t1 = ON_UNSET_VALUE;
02655         if ( !profile_segment->GetDomain(&t0,&t1) 
02656              || !ON_IsValid(t0)
02657              || !ON_IsValid(t1) 
02658              || !(t0 < t1)
02659            )
02660         {
02661           delete newprofile;
02662           if (newbrep != brep )
02663             delete newbrep;
02664           return 0;
02665         }
02666         double t = t1;
02667         while ( GetNextProfileSegmentDiscontinuity(profile_segment,t0,t1,&t) )
02668         {
02669           if ( t0 < t && t < t1 )
02670           {
02671             ON_Curve* left_side = 0;
02672             ON_Curve* right_side = 0;
02673             if ( newprofile->Split(t,left_side,right_side)
02674                  && 0 != left_side
02675                  && 0 != right_side
02676                )
02677             {
02678               finfo.AppendNew().m_extrusion_profile = left_side;
02679               left_side = 0;
02680               delete newprofile;
02681               newprofile = right_side;
02682               right_side = 0;
02683               t0 = t;
02684               t = t1;
02685               continue;
02686             }
02687             if ( 0 != left_side )
02688               delete left_side;
02689             if ( 0 != right_side )
02690               delete right_side;
02691           }
02692           break;
02693         }
02694       }
02695       finfo.AppendNew().m_extrusion_profile = newprofile;
02696     }
02697 
02698     fi1 = finfo.Count();
02699 
02700     if ( fi1 <= fi0 )
02701     {
02702       if (newbrep != brep )
02703         delete newbrep;
02704       return 0;
02705     }
02706 
02707     for ( int finfo_index = fi0; finfo_index < fi1; finfo_index++ )
02708     {
02709       // create an extrusion that represents a single surface
02710       // to store in the brep.
02711       ON_Extrusion_BrepForm_FaceInfo& fi = finfo[finfo_index];
02712       fi.m_face_index = -1;
02713 
02714       fi.m_extrusion_srf = new ON_Extrusion();
02715       fi.m_extrusion_srf->m_path = m_path;
02716       fi.m_extrusion_srf->m_t = m_t;
02717       fi.m_extrusion_srf->m_up = m_up;
02718       fi.m_extrusion_srf->m_profile_count = 1;
02719       fi.m_extrusion_srf->m_profile = fi.m_extrusion_profile;
02720       fi.m_extrusion_srf->m_bCap[0] = false;
02721       fi.m_extrusion_srf->m_bCap[1] = false;
02722       fi.m_extrusion_srf->m_bHaveN[0] = m_bHaveN[0];
02723       fi.m_extrusion_srf->m_bHaveN[1] = m_bHaveN[1];
02724       fi.m_extrusion_srf->m_N[0] = m_N[0];
02725       fi.m_extrusion_srf->m_N[1] = m_N[1];
02726       fi.m_extrusion_srf->m_path_domain = m_path_domain;
02727       fi.m_extrusion_srf->m_bTransposed = m_bTransposed;
02728 
02729       fi.m_profile_index = profile_index;
02730       fi.m_bClosedProfile = profile_fi.m_bClosedProfile;
02731       fi.m_profile_orientation = profile_fi.m_profile_orientation;
02732 
02733       // When a profile has multiple profile_list[] entries,
02734       // the west side of the "next" face is joined to
02735       // the east side of the "previous" face.
02736       fi.m_vid[vidmap[0]] = profile_fi.m_vid[vidmap[1]];
02737       fi.m_vid[vidmap[3]] = profile_fi.m_vid[vidmap[2]];
02738       fi.m_eid[eidmap[3]] = profile_fi.m_eid[eidmap[1]];
02739       if ( fi.m_eid[eidmap[3]] >= 0 )
02740         fi.m_bRev3d[eidmap[3]] = (profile_fi.m_bRev3d[eidmap[1]]) ? false : true;
02741 
02742       if (    profile_fi.m_bClosedProfile
02743            && finfo_index > fi0
02744            && finfo_index == fi1 - 1
02745          )
02746       {
02747         // When a profile is closed and there are multiple
02748         // profile_list[] entries, the east side of the 
02749         // last face is joined to the west side of the
02750         // first face.
02751         fi.m_vid[vidmap[1]] = profile_fi.m_vid[vidmap[0]];
02752         fi.m_vid[vidmap[2]] = profile_fi.m_vid[vidmap[3]];
02753         fi.m_eid[eidmap[1]] = profile_fi.m_eid[eidmap[3]];
02754         if ( fi.m_eid[eidmap[1]] >= 0 )
02755           fi.m_bRev3d[eidmap[1]] = (profile_fi.m_bRev3d[eidmap[3]]) ? false : true;
02756       }
02757 
02758       // Create a face topology around the surface fi.m_extrusion_srf
02759       ON_NurbsSurface* face_srf = fi.m_extrusion_srf->NurbsSurface();
02760       if ( 0 == face_srf )
02761       {
02762         continue;
02766       }
02767 
02768       const ON_BrepFace* face = newbrep->NewFace(face_srf,fi.m_vid,fi.m_eid,fi.m_bRev3d);
02769       if ( 0 == face )
02770       {
02771         // When NewFace() returns null, face_srf is not reference in newbrep.
02772         delete face_srf;
02773 
02774         // 10 July 2012 Dale Lear
02775         //    Continue to attempt to get a brep form.  This failure
02776         //    typically happens when face_srf has two singularities
02777         //    because a profile component is very short.
02778         continue;
02782       }
02783       fi.m_face_index = face->m_face_index; 
02784 
02785       // Save vid[] and eid[] informtion so subsequent faces
02786       // are properly joined.
02787       if ( finfo_index == fi0 )
02788       {
02789         profile_fi.m_vid[vidmap[0]] = fi.m_vid[vidmap[0]];
02790         profile_fi.m_vid[vidmap[3]] = fi.m_vid[vidmap[3]];
02791         profile_fi.m_eid[eidmap[3]] = fi.m_eid[eidmap[3]];
02792         profile_fi.m_bRev3d[eidmap[3]] = fi.m_bRev3d[eidmap[3]];
02793       }
02794       profile_fi.m_vid[vidmap[1]] = fi.m_vid[vidmap[1]];
02795       profile_fi.m_vid[vidmap[2]] = fi.m_vid[vidmap[2]];
02796       profile_fi.m_eid[eidmap[1]] = fi.m_eid[eidmap[1]];
02797       profile_fi.m_bRev3d[eidmap[1]] = fi.m_bRev3d[eidmap[1]];
02798 
02799       if ( 0 == is_capped )
02800         continue;
02801 
02802       const ON_BrepLoop* loop = (1 == face->LoopCount()) ? face->OuterLoop() : 0;
02803       if ( 0 == loop || 4 != loop->TrimCount() )
02804       {
02805         is_capped = 0;
02806         cap_count = 0;
02807         continue;
02808       }
02809 
02810       const ON_BrepTrim* bottom_trim = loop->Trim(eidmap[0]);
02811       if ( 0 == bottom_trim || ON_BrepTrim::boundary != bottom_trim->m_type )
02812       {
02813         is_capped = 0;
02814         cap_count = 0;
02815         continue;
02816       }
02817 
02818       const ON_BrepTrim* top_trim = loop->Trim(eidmap[2]);
02819       if ( 0 == top_trim || ON_BrepTrim::boundary != top_trim->m_type )
02820       {
02821         is_capped = 0;
02822         cap_count = 0;
02823         continue;
02824       }
02825 
02826       const ON_BrepEdge* edge0 = bottom_trim->Edge();
02827       const ON_BrepEdge* edge1 = top_trim->Edge();
02828       if ( 0 == edge0 || 0 == edge1 )
02829       {
02830         is_capped = 0;
02831         cap_count = 0;
02832         continue;
02833       }
02834       fi.m_cap_trim_index[0] = bottom_trim->m_trim_index;
02835       fi.m_cap_trim_index[1] = top_trim->m_trim_index;
02836       fi.m_cap_edge_index[0] = edge0->m_edge_index;
02837       fi.m_cap_edge_index[1] = edge1->m_edge_index;
02838     }
02839   }
02840 
02841   // Add end caps
02842   bool bSetEdgeTolerances = false;
02843 
02844   while ( is_capped > 0 && cap_count == ((3 == is_capped) ? 2 : 1) )
02845   {
02846     // while(...) not a loop 
02847     // - break's are used for flow control and there is a break at the bottom of this scope.
02848 
02849     // Add end caps.
02850     if ( !MakeCap2dCurvesHelper(finfo,is_capped,scale0,scale1) )
02851       break;
02852 
02853     newbrep->m_S.Reserve(newbrep->m_S.Count()+cap_count);
02854     newbrep->m_F.Reserve(newbrep->m_F.Count()+cap_count);
02855     ON_BrepFace* capface0 = 0;
02856     ON_BrepFace* capface1 = 0;
02857     {
02858       ON_PlaneSurface* plane = 0;
02859       int si;
02860       if ( 3 == is_capped  || 1 == is_capped )
02861       {
02862         // create bottom face cap
02863         plane = MakeCapPlaneHelper( finfo, 0, rot0 );
02864         if ( plane )
02865         {
02866           si = newbrep->AddSurface(plane);
02867           plane = 0;
02868           capface0 = &newbrep->NewFace(si);
02869           capface0->m_bRev = m_bTransposed ? false : true;
02870         }
02871       }
02872       if ( 3 == is_capped  || 2 == is_capped )
02873       {
02874         // create top face cap
02875         plane = MakeCapPlaneHelper( finfo, 1, rot1 );
02876         if ( plane )
02877         {
02878           si = newbrep->AddSurface(plane);
02879           plane = 0;
02880           capface1 = &newbrep->NewFace(si);
02881           capface1->m_bRev = m_bTransposed ? true : false;
02882         }
02883       }
02884     }
02885 
02886     if ( 0 == capface0 && 0 == capface1 )
02887       break;
02888 
02889     newbrep->m_C2.Reserve(newbrep->m_C2.Count()+cap_count*finfo.Count());
02890     newbrep->m_L.Reserve(newbrep->m_L.Count()+cap_count*profile_count);
02891     newbrep->m_T.Reserve(newbrep->m_T.Count()+cap_count*finfo.Count());
02892 
02893     int cap_index = 0;
02894     for ( cap_index = 0; cap_index < 2; cap_index++ )
02895     {
02896       // make all the loops and trims for the bottom cap
02897       // before making them for the top cap. This makes
02898       // it easier for the picking code to create ON_Brep
02899       // component indices when picking the extrusion
02900       // and the brep index is not present.
02901       ON_BrepFace* capface = ( 0 == cap_index ) ? capface0 : capface1;
02902       if ( 0 == capface )
02903         continue;
02904 
02905       fi0 = 0;
02906       int profile_index;
02907       for ( profile_index = 0; profile_index < profile_count && fi0 < finfo.Count(); profile_index++ )
02908       {
02909         if ( finfo[fi0].m_profile_index != profile_index )
02910           break;
02911         ON_BrepLoop::TYPE loop_type = ( 0 == profile_index ) ? ON_BrepLoop::outer : ON_BrepLoop::inner;
02912         fi1 = MakeCapLoopHelper(finfo,fi0,cap_index,capface,loop_type,&bSetEdgeTolerances);
02913         if ( fi1 <= fi0 )
02914           break;
02915         fi0 = fi1;
02916       }
02917 
02918       if ( fi0 != finfo.Count() || profile_index != profile_count )
02919       {
02920         ON_ERROR("Failed to add caps to extrusion brep form.");
02921         if ( 0 != capface0 )
02922           newbrep->DeleteFace(*capface0,false);
02923         if ( 0 != capface1 )
02924           newbrep->DeleteFace(*capface1,false);
02925         newbrep->Compact();
02926         break;
02927       }
02928     }
02929 
02930     if ( 2 == cap_index && 2 == cap_count && 3 == is_capped )
02931       ((CMyBrepIsSolidSetter*)newbrep)->SetIsSolid(m_bTransposed?2:1);
02932 
02933     break;
02934   }
02935 
02936   if ( newbrep )
02937   {
02938     // set a tight bounding box
02939     ((CMyBrepIsSolidSetter*)newbrep)->SetBBox(*this);
02940     newbrep->SetTrimBoundingBoxes(true);
02941 
02942     if ( bSetEdgeTolerances )
02943       newbrep->SetEdgeTolerances(true);
02944   }
02945 
02946 #if defined(ON_DEBUG)
02947   if ( !newbrep->IsValid() )
02948   {
02949     newbrep->IsValid(); // breakpoint here
02950   }
02951 #endif
02952 
02953   return newbrep;
02954 }
02955 
02956 bool ON_Extrusion::GetBrepFormComponentIndex(
02957   ON_COMPONENT_INDEX extrusion_ci,
02958   ON_COMPONENT_INDEX& brep_ci
02959   ) const
02960 {
02961   const ON_Brep* null_brep_pointer = 0;
02962   return GetBrepFormComponentIndex(extrusion_ci,ON_UNSET_VALUE,*null_brep_pointer,brep_ci);
02963 }
02964 
02965 static bool GetBrepFormFaceIndex(
02966         const ON_Extrusion& extrusion,
02967         int extrusion_profile_index,
02968         double extrusion_profile_parameter,
02969         bool bCountProfileDiscontinuities,
02970         int* brep_form_face_index,
02971         ON_Interval* profile_segment_domain
02972         )
02973 {
02974   // When extrusion_profile_index = profile_count,
02975   // it means the caller is looking for the index of
02976   // the bottom cap.
02977   int profile_segment_count = 0;
02978   const int profile_count = extrusion.ProfileCount();
02979   double t0 = ON_UNSET_VALUE;
02980   double t1 = ON_UNSET_VALUE;
02981   const ON_Curve* profile;
02982   if ( ON_UNSET_VALUE == extrusion_profile_parameter )
02983   {
02984     if ( extrusion_profile_index != profile_count )
02985     {
02986       profile = extrusion.Profile(extrusion_profile_index);
02987       if (    0 == profile
02988            || !profile->GetDomain(&t0,&t1) 
02989            || !ON_IsValid(t0)
02990            || !ON_IsValid(t1) 
02991            || !(t0 < t1)
02992          )
02993       {
02994         return false;
02995       }
02996     }
02997     profile_segment_count = extrusion_profile_index;
02998   }
02999   else
03000   {
03001     for ( int i = 0; i < profile_count; i++ )
03002     {
03003       profile = extrusion.Profile(i);
03004       if ( 0 == profile )
03005         return false;
03006       // It is important to use the original profile, "profile_segment"
03007       // and not the trimmed copy in the GetNextDiscontinuity() loop
03008       // so the code that creates this brep divides the extrusion profile
03009       // exactly the same way as the code that calculates component indices.
03010       if (    0 == profile
03011            || !profile->GetDomain(&t0,&t1) 
03012            || !ON_IsValid(t0)
03013            || !ON_IsValid(t1) 
03014            || !(t0 < t1)
03015          )
03016       {
03017         return false;
03018       }
03019 
03020       double t = t1;
03021       if ( bCountProfileDiscontinuities )
03022       {
03023         while ( GetNextProfileSegmentDiscontinuity(profile,t0,t1,&t) )
03024         {
03025           if ( t0 < t && t < t1 )
03026           {
03027             if ( i == extrusion_profile_index 
03028                  && t0 <= extrusion_profile_parameter 
03029                  && extrusion_profile_parameter < t 
03030                )
03031             {
03032               break;
03033             }
03034             t0 = t;
03035             t = t1;
03036             profile_segment_count++;
03037             continue;
03038           }
03039           break;
03040         }
03041       }
03042       if ( i == extrusion_profile_index )
03043         break;
03044       profile_segment_count++;
03045     }
03046   }
03047 
03048   if ( 0 != brep_form_face_index )
03049     *brep_form_face_index = profile_segment_count;
03050 
03051   if ( 0 != profile_segment_domain && extrusion_profile_index < profile_count )
03052     profile_segment_domain->Set(t0,t1);
03053 
03054   return true;
03055 }
03056 
03057 bool ON_Extrusion::GetBrepFormComponentIndex(
03058   ON_COMPONENT_INDEX extrusion_ci,
03059   double extrusion_profile_parameter,
03060   const ON_Brep& brep_form,
03061   ON_COMPONENT_INDEX& brep_ci
03062   ) const
03063 {
03064   brep_ci.UnSet();
03065   int face_index = -1;
03066   ON_Interval face_profile_domain(ON_UNSET_VALUE,ON_UNSET_VALUE);
03067   const ON_Brep* brep = &brep_form; // brep pointer can be null
03068 
03069   const int is_capped = IsCapped();
03070   if ( is_capped < 0 || is_capped > 3 )
03071     return false;
03072   const int profile_count = ProfileCount();
03073   if ( profile_count < 1 )
03074     return false;
03075   const ON_Curve* profile0 = Profile(0);
03076   if ( 0 == profile0 )
03077     return false;
03078   const bool bClosedProfile = profile0->IsClosed() ? true : false;
03079   if ( profile_count > 1 && !bClosedProfile )
03080     return false;
03081   const int edges_per_wall_face = bClosedProfile ? 3 : 4;
03082   const int cap_count = (0 == is_capped || !bClosedProfile) ? 0 : ((3==is_capped)?2:1);
03083   int brep_face_count = ( 0 != brep ) ? brep->m_F.Count() : 0;
03084   if ( 0 != brep && brep_face_count < profile_count + cap_count )
03085   {
03086     ON_ERROR("brep_form parameter cannot be extrusion's BrepForm()");
03087     return false;
03088   }
03089   bool bCountProfileDiscontinuities = ( brep_face_count > profile_count + cap_count );
03090 
03091   switch(extrusion_ci.m_type)
03092   {
03093   case ON_COMPONENT_INDEX::extrusion_bottom_profile:
03094   case ON_COMPONENT_INDEX::extrusion_top_profile:
03095     if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= profile_count )
03096       return false;
03097     if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) )
03098       return false;
03099     brep_ci.m_index = edges_per_wall_face*face_index;
03100     if ( ON_COMPONENT_INDEX::extrusion_top_profile == extrusion_ci.m_type )
03101       brep_ci.m_index += 2;
03102     brep_ci.m_type = ON_COMPONENT_INDEX::brep_edge;
03103     break;
03104 
03105   case ON_COMPONENT_INDEX::extrusion_wall_edge:
03106     if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= 2*profile_count )
03107       return false;
03108     if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index/2, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) )
03109       return false;
03110     brep_ci.m_index = edges_per_wall_face*face_index+1;
03111     if ( !bClosedProfile && 1 == (face_index % 1) )
03112       brep_ci.m_index += 2;
03113     brep_ci.m_type = ON_COMPONENT_INDEX::brep_edge;
03114     break;
03115 
03116   case ON_COMPONENT_INDEX::extrusion_wall_surface:
03117     if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index >= profile_count )
03118       return false;
03119     if ( !GetBrepFormFaceIndex( *this, extrusion_ci.m_index, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) )
03120       return false;
03121     brep_ci.m_index = face_index;
03122     brep_ci.m_type = ON_COMPONENT_INDEX::brep_face;
03123     break;
03124 
03125   case ON_COMPONENT_INDEX::extrusion_cap_surface:
03126     if ( extrusion_ci.m_index < 0 || extrusion_ci.m_index > 2 )
03127       return false;
03128     if ( 1 == extrusion_ci.m_index && (is_capped != 1 && is_capped != 3) )
03129       return false;
03130     if ( 2 == extrusion_ci.m_index && (is_capped != 2 && is_capped != 3) )
03131       return false;
03132     if ( 0 != brep )
03133     {
03134       face_index = brep->m_F.Count()-cap_count;
03135     }
03136     else if ( !GetBrepFormFaceIndex( *this, profile_count, extrusion_profile_parameter, bCountProfileDiscontinuities, &face_index, &face_profile_domain ) )
03137     {
03138       return false;
03139     }
03140 
03141     brep_ci.m_index = face_index+extrusion_ci.m_index-1;
03142     brep_ci.m_type = ON_COMPONENT_INDEX::brep_face;
03143     break;
03144 
03145   case ON_COMPONENT_INDEX::extrusion_path:
03146     // There is no corresponding brep component index
03147     break;
03148 
03149   default:
03150     // Other ON_COMPONENT_INDEX::TYPE values intentionally ignored
03151     break;
03152   }
03153 
03154   if ( !brep_ci.IsBrepComponentIndex() )
03155   {
03156     brep_ci.UnSet();
03157     return false;
03158   }
03159 
03160   return true;
03161 }
03162 
03163 ON_BOOL32 ON_Extrusion::SetDomain( 
03164   int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain
03165   double t0, 
03166   double t1
03167   )
03168 {
03169   bool rc = false;
03170   if ( ON_IsValid(t0) && ON_IsValid(t1) && t0 < t1 )
03171   {
03172     const int path_dir = PathParameter();
03173     if ( path_dir == dir )
03174     {
03175       m_path_domain.Set(t0,t1);
03176       rc = true;
03177     }
03178     else if ( 1-path_dir == dir )
03179     {
03180       rc = m_profile->SetDomain(t0,t1)?true:false;
03181     }
03182   }
03183   return rc;
03184 }
03185 
03186 ON_Interval ON_Extrusion::Domain(
03187   int dir // 0 gets first parameter's domain, 1 gets second parameter's domain
03188   ) const 
03189 {
03190   const int path_dir = PathParameter();
03191   return (path_dir == dir ) 
03192          ? m_path_domain 
03193          : ((1-path_dir == dir && m_profile) ? m_profile->Domain() : ON_Interval());
03194 }
03195 
03196 ON_BOOL32 ON_Extrusion::GetSurfaceSize( 
03197     double* width, 
03198     double* height 
03199     ) const
03200 {
03201   bool rc = true;
03202   //int path_dir = PathParameter();
03203   if ( PathParameter() )
03204   {
03205     double* p = width;
03206     width = height;
03207     height = p;
03208   }
03209   if ( width )
03210   {
03211     if ( m_path.IsValid() && m_t.IsIncreasing() )
03212       *width = m_path.Length()*m_t.Length();
03213     else
03214     {
03215       *width = 0.0;
03216       rc = false;
03217     }
03218   }
03219   if (height)
03220   {
03221     if ( m_profile )
03222     {
03223       // A crude over estimate is good enough for file IO 
03224       // needs in the public souce code version. When there
03225       // are multiple profile components, the nurbs_profile_curve
03226       // will have a whacky control polygon, but it's length will
03227       // be ok as an estimate.
03228       ON_NurbsCurve nurbs_profile_curve;
03229       if ( m_profile->GetNurbForm(nurbs_profile_curve) <= 0 )
03230       {
03231         *height = 0.0;
03232         rc = false;
03233       }
03234       else
03235       {
03236         *height = nurbs_profile_curve.ControlPolygonLength();
03237       }
03238     }
03239     else 
03240     {
03241       rc = false;
03242       *height = 0.0;
03243     }
03244   }
03245   return rc;
03246 }
03247 
03248 int ON_Extrusion::SpanCount(
03249   int dir // 0 gets first parameter's domain, 1 gets second parameter's domain
03250   ) const // number of smooth nonempty spans in the parameter direction
03251 {
03252   const int path_dir = PathParameter();
03253   if ( path_dir == dir )
03254     return 1;
03255   if ( 1-path_dir == dir && m_profile )
03256     return m_profile->SpanCount();
03257   return 0;
03258 }
03259 
03260 ON_BOOL32 ON_Extrusion::GetSpanVector( // span "knots" 
03261       int dir, // 0 gets first parameter's domain, 1 gets second parameter's domain
03262       double* span_vector // array of length SpanCount() + 1 
03263       ) const  // 
03264 {
03265   if ( span_vector )
03266   {
03267     const int path_dir = PathParameter();
03268     if ( path_dir == dir )
03269     {
03270       span_vector[0] = m_path_domain[0];
03271       span_vector[1] = m_path_domain[1];
03272       return true;
03273     }
03274     if ( 1-path_dir == dir && m_profile )
03275     {
03276       return m_profile->GetSpanVector(span_vector);
03277     }
03278   }
03279   return false;
03280 }
03281 
03282 ON_BOOL32 ON_Extrusion::GetSpanVectorIndex(
03283       int dir , // 0 gets first parameter's domain, 1 gets second parameter's domain
03284       double t,      // [IN] t = evaluation parameter
03285       int side,         // [IN] side 0 = default, -1 = from below, +1 = from above
03286       int* span_vector_index,        // [OUT] span vector index
03287       ON_Interval* span_interval // [OUT] domain of the span containing "t"
03288       ) const
03289 {
03290   const int path_dir = PathParameter();
03291   if ( path_dir == dir )
03292   {
03293     if ( span_vector_index )
03294       *span_vector_index = 0;
03295     if ( span_interval )
03296       *span_interval = m_path_domain;
03297     return true;
03298   }
03299   if ( 1-path_dir == dir && m_profile )
03300   {
03301     return m_profile->GetSpanVectorIndex(t,side,span_vector_index,span_interval);
03302   }
03303   return false;
03304 }
03305 
03306 int ON_Extrusion::Degree( // returns maximum algebraic degree of any span 
03307                 // ( or a good estimate if curve spans are not algebraic )
03308   int dir // 0 gets first parameter's domain, 1 gets second parameter's domain
03309   ) const  
03310 {
03311   const int path_dir = PathParameter();
03312   if ( path_dir == dir )
03313     return 1;
03314   if ( 1-path_dir == dir && m_profile )
03315     return m_profile->Degree();
03316   return 0;
03317 }
03318 
03319 ON_BOOL32 ON_Extrusion::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus
03320        int dir,        // 0 gets first parameter, 1 gets second parameter
03321        double t,       // t = parameter in domain
03322        double* tminus, // tminus
03323        double* tplus   // tplus
03324        ) const
03325 {
03326   const int path_dir = PathParameter();
03327   if ( path_dir == dir )
03328     return ON_Surface::GetParameterTolerance(dir,t,tminus,tplus);
03329   if ( 1-path_dir==dir && m_profile)
03330     return m_profile->GetParameterTolerance(t,tminus,tplus);
03331   return false;
03332 }
03333 
03334 ON_Surface::ISO ON_Extrusion::IsIsoparametric(
03335       const ON_Curve& curve,
03336       const ON_Interval* curve_domain
03337       ) const
03338 {
03339   return ON_Surface::IsIsoparametric(curve,curve_domain);
03340 }
03341 
03342 ON_BOOL32 ON_Extrusion::IsPlanar(
03343       ON_Plane* plane,
03344       double tolerance
03345       ) const
03346 {
03347   if ( m_profile && m_profile->IsLinear(tolerance) )
03348   {
03349     if ( plane )
03350     {
03351       ON_3dPoint P0 = m_profile->PointAtStart();
03352       ON_3dPoint P1 = m_profile->PointAtEnd();
03353       ON_3dVector pathT = m_path.Tangent();
03354       ON_3dVector Y = m_up;
03355       ON_3dVector X = ON_CrossProduct(Y,pathT);
03356       if ( !X.IsUnitVector() )
03357         X.Unitize();
03358       ON_3dPoint Q0 = m_path.from + P0.x*X + P0.y*Y;
03359       ON_3dPoint Q1 = m_path.from + P1.x*X + P1.y*Y;
03360       ON_3dVector N = ON_CrossProduct(pathT,Q1-Q0);
03361       N.Unitize();
03362       plane->origin = Q0;
03363       if ( false == m_bTransposed )
03364       {
03365         plane->yaxis = pathT;
03366         plane->zaxis = -N;
03367         plane->xaxis = ON_CrossProduct(plane->yaxis,plane->zaxis);
03368         plane->xaxis.Unitize();
03369       }
03370       else
03371       {
03372         plane->xaxis = pathT;
03373         plane->zaxis = N;
03374         plane->yaxis = ON_CrossProduct(plane->zaxis,plane->xaxis);
03375         plane->yaxis.Unitize();
03376       }
03377       plane->UpdateEquation();
03378     }    
03379     return true;
03380   }
03381   return false;
03382 }
03383 
03384 ON_BOOL32 ON_Extrusion::IsClosed(int dir) const
03385 {
03386   const int path_dir = PathParameter();
03387   if ( 1-path_dir == dir && m_profile )
03388     return m_profile->IsClosed();
03389   return false;
03390 }
03391 
03392 ON_BOOL32 ON_Extrusion::IsPeriodic( int dir ) const
03393 {
03394   const int path_dir = PathParameter();
03395   if ( 1-path_dir == dir && m_profile )
03396     return m_profile->IsPeriodic();
03397   return false;
03398 }
03399 
03400 bool ON_Extrusion::GetNextDiscontinuity( 
03401                 int dir,
03402                 ON::continuity c,
03403                 double t0,
03404                 double t1,
03405                 double* t,
03406                 int* hint,
03407                 int* dtype,
03408                 double cos_angle_tolerance,
03409                 double curvature_tolerance
03410                 ) const
03411 {
03412   const int path_dir = PathParameter();
03413   if ( path_dir == dir )
03414   {
03415     return ON_Surface::GetNextDiscontinuity(dir,c,t0,t1,t,hint,dtype,cos_angle_tolerance,curvature_tolerance);
03416   }
03417   if ( 1-path_dir==dir && m_profile)
03418   {
03419     return m_profile->GetNextDiscontinuity(c,t0,t1,t,hint,dtype,cos_angle_tolerance,curvature_tolerance);
03420   }
03421   return false;
03422 }
03423 
03424 bool ON_Extrusion::IsContinuous(
03425   ON::continuity c,
03426   double s, 
03427   double t, 
03428   int* hint,
03429   double point_tolerance,
03430   double d1_tolerance,
03431   double d2_tolerance,
03432   double cos_angle_tolerance,
03433   double curvature_tolerance
03434   ) const
03435 {
03436   if ( !m_profile )
03437     return false;
03438   int* crv_hint = 0;
03439   double curvet;
03440   if ( m_bTransposed )
03441   {
03442     curvet = s;
03443     crv_hint = hint;
03444   }
03445   else
03446   {
03447     curvet = t;
03448     crv_hint = hint ? hint+1 : 0;
03449   }
03450   return m_profile->IsContinuous(c,curvet,crv_hint,point_tolerance,d1_tolerance,d2_tolerance,cos_angle_tolerance,curvature_tolerance);
03451 }
03452 
03453 ON_Surface::ISO ON_Extrusion::IsIsoparametric(
03454       const ON_BoundingBox& bbox
03455       ) const
03456 {
03457   return ON_Surface::IsIsoparametric(bbox);
03458 }
03459 
03460 ON_BOOL32 ON_Extrusion::Reverse( int dir )
03461 {
03462   if ( 0 == m_profile )
03463     return false;
03464 
03465   const int path_dir = PathParameter();
03466 
03467   if ( path_dir == dir )
03468   {
03469     m_path_domain.Reverse();
03470     m_path.Reverse();
03471 
03472     // Need to mirror profile about 2d x-axis
03473     // NOTE:
03474     //   If the profile is closed and the extrusion
03475     //   is capped, then this will leave the extrusion
03476     //   in an invalid "inside-out" state.
03477     //   It is the responsibility of the caller
03478     //   to explicitly reverse the profile as well.
03479     //   I cannot "automatically" do it here because
03480     //   it breaks existing reparamterization code 
03481     //   that checks for closed objects and makes
03482     //   a sequence changes that results in a valid
03483     //   final result.
03484     ON_Xform profile_xform(1.0);
03485     profile_xform.m_xform[0][0] = -1.0;
03486     bool bNeedReverse = false;
03487     return Profile2dTransform(*this,profile_xform,bNeedReverse);
03488   }
03489 
03490   if ( 1-path_dir == dir )
03491   {
03492     return m_profile->Reverse();
03493     // NOTE:
03494     //   If the profile is closed and the extrusion
03495     //   is capped, then this will leave the extrusion
03496     //   in an invalid "inside-out" state.
03497     //   It is the responsibility of the caller
03498     //   to explicitly reverse the profile as well.
03499     //   I cannot "automatically" do it here because
03500     //   it breaks existing reparamterization code 
03501     //   that checks for closed objects and makes
03502     //   a sequence changes that results in a valid
03503     //   final result.
03504   }
03505 
03506   return false;
03507 }
03508 
03509 ON_BOOL32 ON_Extrusion::Transpose() // transpose surface parameterization (swap "s" and "t")
03510 {
03511   m_bTransposed = m_bTransposed?false:true;
03512   return true;
03513 }
03514 
03515 ON_BOOL32 ON_Extrusion::Evaluate( // returns false if unable to evaluate
03516        double u, double v,   // evaluation parameters
03517        int num_der,          // number of derivatives (>=0)
03518        int array_stride,     // array stride (>=Dimension())
03519        double* der_array,    // array of length stride*(ndir+1)*(ndir+2)/2
03520        int quadrant ,     // optional - determines which quadrant to evaluate from
03521                              //         0 = default
03522                              //         1 from NE quadrant
03523                              //         2 from NW quadrant
03524                              //         3 from SW quadrant
03525                              //         4 from SE quadrant
03526        int* hint          // optional - evaluation hint (int[2]) used to speed
03527                              //            repeated evaluations
03528        ) const 
03529 {
03530   if ( !m_profile )
03531     return false;
03532 
03533   double x,y,dx,dy;
03534   //int side = 0;
03535   if ( m_bTransposed ) 
03536   {
03537     x = u; u = v; v = x;
03538     if ( 4 == quadrant )
03539       quadrant = 2;
03540     else if ( 2 == quadrant )
03541       quadrant = 4;
03542   }
03543 
03544   if ( !m_profile->Evaluate( u, num_der, array_stride, der_array,
03545                              (1==quadrant||4==quadrant)?1:((2==quadrant||3==quadrant)?-1:0),
03546                                hint) 
03547      )
03548   {
03549     return false;
03550   }
03551 
03552   // TODO: After testing, add special case that avoids
03553   //       two calls to GetProfileTransformation() when 
03554   //       mitering is trivial.
03555   const double t1 = m_path_domain.NormalizedParameterAt(v);
03556   const double t0 = 1.0-t1;
03557   ON_Xform xform0, xform1;
03558   const ON_3dVector T = m_path.Tangent();
03559   if ( 0.0 != t0 || num_der > 0 )
03560   {
03561     if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,0,0) )
03562       return false;
03563   }
03564   else
03565   {
03566     xform0.Zero();
03567   }
03568   if ( 0.0 != t1 || num_der > 0 )
03569   {
03570     if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,0,0) )
03571       return false;
03572   }
03573   else
03574   {
03575     xform1.Zero();
03576   }
03577 
03578   double xformP[3][3], xformD[3][3];
03579   xformP[0][0] = t0*xform0.m_xform[0][0] + t1*xform1.m_xform[0][0];
03580   xformP[0][1] = t0*xform0.m_xform[0][1] + t1*xform1.m_xform[0][1];
03581   xformP[0][2] = t0*xform0.m_xform[0][3] + t1*xform1.m_xform[0][3];
03582   xformP[1][0] = t0*xform0.m_xform[1][0] + t1*xform1.m_xform[1][0];
03583   xformP[1][1] = t0*xform0.m_xform[1][1] + t1*xform1.m_xform[1][1];
03584   xformP[1][2] = t0*xform0.m_xform[1][3] + t1*xform1.m_xform[1][3];
03585   xformP[2][0] = t0*xform0.m_xform[2][0] + t1*xform1.m_xform[2][0];
03586   xformP[2][1] = t0*xform0.m_xform[2][1] + t1*xform1.m_xform[2][1];
03587   xformP[2][2] = t0*xform0.m_xform[2][3] + t1*xform1.m_xform[2][3];
03588 
03589   int i,j;
03590   i = num_der+1;
03591   double* d1 = der_array + array_stride*(i*(i+1)/2 - 1);
03592   double* d0 = der_array + array_stride*(i - 1);
03593   x = d0[0];
03594   y = d0[1];
03595   if ( num_der > 0 )
03596   {
03597     double d = m_path_domain.m_t[1] - m_path_domain.m_t[0];
03598     if ( d > 0.0 )
03599       d = 1.0/d;
03600     xformD[0][0] = d*(xform1.m_xform[0][0] - xform0.m_xform[0][0]);
03601     xformD[0][1] = d*(xform1.m_xform[0][1] - xform0.m_xform[0][1]);
03602     xformD[0][2] = d*(xform1.m_xform[0][3] - xform0.m_xform[0][3]);
03603     xformD[1][0] = d*(xform1.m_xform[1][0] - xform0.m_xform[1][0]);
03604     xformD[1][1] = d*(xform1.m_xform[1][1] - xform0.m_xform[1][1]);
03605     xformD[1][2] = d*(xform1.m_xform[1][3] - xform0.m_xform[1][3]);
03606     xformD[2][0] = d*(xform1.m_xform[2][0] - xform0.m_xform[2][0]);
03607     xformD[2][1] = d*(xform1.m_xform[2][1] - xform0.m_xform[2][1]);
03608     xformD[2][2] = d*(xform1.m_xform[2][3] - xform0.m_xform[2][3]);
03609 
03610     for ( i = num_der; i > 0; i-- )
03611     {
03612       dx = x;
03613       dy = y;
03614       d0 -= array_stride;
03615       x = d0[0];
03616       y = d0[1];
03617 
03618       // all partials involving two or more derivatives with
03619       // respect to "v" are zero.
03620       j = i;
03621       while ( --j )
03622       {
03623         d1[0] = d1[1] = d1[2] = 0.0;
03624         d1 -= array_stride;
03625       }    
03626 
03627       // The partial involving a single derivative with respect to "v"
03628       if ( 1 == i )
03629       {
03630         // xformD transform is applied to curve location ((x,y) = point)
03631         d1[0] = xformD[0][0]*x + xformD[0][1]*y + xformD[0][2];
03632         d1[1] = xformD[1][0]*x + xformD[1][1]*y + xformD[1][2];
03633         d1[2] = xformD[2][0]*x + xformD[2][1]*y + xformD[2][2];
03634       }
03635       else
03636       {
03637         // xformD transform is applied to a curve derivative ((x,y) = vector)
03638         d1[0] = xformD[0][0]*x + xformD[0][1]*y;
03639         d1[1] = xformD[1][0]*x + xformD[1][1]*y;
03640         d1[2] = xformD[2][0]*x + xformD[2][1]*y;
03641       }
03642       d1 -= array_stride;
03643 
03644       // The partial involving a all derivatives with respect to "u"
03645       // xformP transformation is applied to a curve derivative ((x,y) = vector)
03646       d1[0] = xformP[0][0]*dx + xformP[0][1]*dy;
03647       d1[1] = xformP[1][0]*dx + xformP[1][1]*dy;
03648       d1[2] = xformP[2][0]*dx + xformP[2][1]*dy;
03649       d1 -= array_stride;
03650     }
03651   }
03652   // xformP transformation is applied curve location ((x,y) = point)
03653   d1[0] = xformP[0][0]*x + xformP[0][1]*y + xformP[0][2];
03654   d1[1] = xformP[1][0]*x + xformP[1][1]*y + xformP[1][2];
03655   d1[2] = xformP[2][0]*x + xformP[2][1]*y + xformP[2][2];
03656 
03657   if ( m_bTransposed && num_der > 0)
03658   {
03659     // reverse order of derivatives
03660     const size_t sz = ((3 <= array_stride)?3:array_stride)*sizeof(double);
03661     void* tmp = ( sz <= sizeof(xform0) )
03662               ? ((void*)&xform0.m_xform[0][0])
03663               : onmalloc(sz);
03664     for ( i = 1; i <= num_der; i++ )
03665     {
03666       d0 = der_array + array_stride*(i*(i+1))/2;
03667       d1 = d0 + array_stride*i;
03668       while ( d0 < d1)
03669       {
03670         memcpy(tmp,d0,sz);
03671         memcpy(d0,d1,sz);
03672         memcpy(d1,tmp,sz);
03673         d0 += array_stride;
03674         d1 -= array_stride;
03675       }
03676     }
03677     if ( tmp != ((void*)&xform0.m_xform[0][0]) )
03678       onfree(tmp);
03679   }
03680 
03681   return true;
03682 }
03683 
03684 ON_Curve* ON_Extrusion::IsoCurve(
03685        int dir,
03686        double c
03687        ) const
03688 {
03689   // dir 0 first parameter varies and second parameter is constant
03690   //       e.g., point on IsoCurve(0,c) at t is srf(t,c)
03691   //     1 first parameter is constant and second parameter varies
03692   //       e.g., point on IsoCurve(1,c) at t is srf(c,t)
03693 
03694   if ( !m_profile )
03695     return 0;
03696 
03697   if ( m_bTransposed )
03698     dir = 1-dir;
03699   const ON_3dVector T = m_path.Tangent();
03700 
03701   ON_Xform xform0, xform1;
03702   if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[0]),T,m_up,m_bHaveN[0]?&m_N[0]:0,xform0,0,0) )
03703     return 0;
03704   if ( !ON_GetEndCapTransformation(m_path.PointAt(m_t.m_t[1]),T,m_up,m_bHaveN[1]?&m_N[1]:0,xform1,0,0) )
03705     return 0;
03706 
03707   ON_Curve*  isocurve = 0;
03708   if ( 1 == dir )
03709   {
03710     ON_3dPoint P = m_profile->PointAt(c);
03711     ON_LineCurve* line_curve = new ON_LineCurve();
03712     line_curve->m_t = m_path_domain;
03713     line_curve->m_dim = 3;
03714     line_curve->m_line.from = xform0*P;
03715     line_curve->m_line.to = xform1*P;
03716     isocurve = line_curve;
03717   }
03718   else if ( 0 == dir )
03719   {
03720     double s1 = m_path_domain.NormalizedParameterAt(c);
03721     const double s0 = 1.0-s1;
03722     xform1.m_xform[0][0] = s0*xform0.m_xform[0][0] + s1*xform1.m_xform[0][0];
03723     xform1.m_xform[0][1] = s0*xform0.m_xform[0][1] + s1*xform1.m_xform[0][1];
03724     xform1.m_xform[0][2] = s0*xform0.m_xform[0][2] + s1*xform1.m_xform[0][2];
03725     xform1.m_xform[0][3] = s0*xform0.m_xform[0][3] + s1*xform1.m_xform[0][3];
03726 
03727     xform1.m_xform[1][0] = s0*xform0.m_xform[1][0] + s1*xform1.m_xform[1][0];
03728     xform1.m_xform[1][1] = s0*xform0.m_xform[1][1] + s1*xform1.m_xform[1][1];
03729     xform1.m_xform[1][2] = s0*xform0.m_xform[1][2] + s1*xform1.m_xform[1][2];
03730     xform1.m_xform[1][3] = s0*xform0.m_xform[1][3] + s1*xform1.m_xform[1][3];
03731 
03732     xform1.m_xform[2][0] = s0*xform0.m_xform[2][0] + s1*xform1.m_xform[2][0];
03733     xform1.m_xform[2][1] = s0*xform0.m_xform[2][1] + s1*xform1.m_xform[2][1];
03734     xform1.m_xform[2][2] = s0*xform0.m_xform[2][2] + s1*xform1.m_xform[2][2];
03735     xform1.m_xform[2][3] = s0*xform0.m_xform[2][3] + s1*xform1.m_xform[2][3];
03736 
03737     xform1.m_xform[3][0] = s0*xform0.m_xform[3][0] + s1*xform1.m_xform[3][0];
03738     xform1.m_xform[3][1] = s0*xform0.m_xform[3][1] + s1*xform1.m_xform[3][1];
03739     xform1.m_xform[3][2] = s0*xform0.m_xform[3][2] + s1*xform1.m_xform[3][2];
03740     xform1.m_xform[3][3] = s0*xform0.m_xform[3][3] + s1*xform1.m_xform[3][3];
03741 
03742     isocurve = m_profile->DuplicateCurve();
03743     if ( isocurve )
03744     {
03745       isocurve->ChangeDimension(3);
03746       if ( !isocurve->Transform(xform1) )
03747       {
03748         // isocurve is probably a circle
03749         ON_NurbsCurve* nurbs_curve = isocurve->NurbsCurve();
03750         delete isocurve;
03751         isocurve = nurbs_curve;
03752         nurbs_curve = 0;
03753         if ( isocurve )
03754           isocurve->Transform(xform1);
03755       }
03756     }
03757   }
03758 
03759   return isocurve;
03760 }
03761 
03762 ON_BOOL32 ON_Extrusion::Trim(
03763        int dir,
03764        const ON_Interval& domain
03765        )
03766 {
03767   bool rc = false;
03768   if (!domain.IsIncreasing())
03769     return false;
03770   if ( m_bTransposed )
03771     dir = 1-dir;
03772   if ( 1 == dir )
03773   {
03774     rc = m_path_domain.IsIncreasing();
03775     if ( rc && m_path_domain != domain )
03776     {
03777       ON_Interval dom;
03778       dom.Intersection(domain,m_path_domain);
03779       rc = dom.IsIncreasing();
03780       if (rc)
03781       {
03782         double s0 = m_path_domain.NormalizedParameterAt(dom[0]);
03783         double s1 = m_path_domain.NormalizedParameterAt(dom[1]);
03784         double t0 = (1.0-s0)*m_t[0] + s0*m_t[1];
03785         double t1 = (1.0-s1)*m_t[0] + s1*m_t[1];
03786         rc = (s0 < s1 && 0.0 <= t0 && t0 < t1 && t1 <= 1.0);
03787         if (rc)
03788         {
03789           bool bChanged = false;
03790           if (t0 != m_t[0] && t0 > 0.0 )
03791           {
03792             bChanged = true;
03793             m_t[0] = t0;
03794             m_bHaveN[0] = false;
03795           }
03796           if ( t1 != m_t[1] && t1 < 1.0 )
03797           {
03798             bChanged = true;
03799             m_t[1] = t1;
03800             m_bHaveN[1] = false;
03801           }
03802           if ( bChanged )
03803           {
03804             m_path_domain = dom;
03805             DestroySurfaceTree();
03806           }
03807         }
03808       }
03809     }
03810   }
03811   else if ( 0 == dir )
03812   {
03813     if ( m_profile )
03814     {
03815       rc = m_profile->Trim(domain)?true:false;
03816       DestroySurfaceTree();
03817     }
03818   }
03819   return rc;
03820 }
03821 
03822 bool ON_Extrusion::Extend(
03823   int dir,
03824   const ON_Interval& domain
03825   )
03826 {
03827   bool rc = false;
03828   if ( 1 == dir )
03829   {
03830     rc = domain.IsIncreasing() && m_path_domain.IsIncreasing();
03831     if ( rc )
03832     {
03833       double s0 = m_path_domain.NormalizedParameterAt(domain[0]);
03834       if ( s0 > 0.0 )
03835         s0 = 0.0;
03836       double s1 = m_path_domain.NormalizedParameterAt(domain[1]);
03837       if ( s1 < 1.0 )
03838         s1 = 1.0;
03839       double t0 = (1.0-s0)*m_t[0] + s0*m_t[1];
03840       double t1 = (1.0-s1)*m_t[0] + s1*m_t[1];
03841       bool bChanged = false;
03842       ON_3dPoint P0 = m_path.from;
03843       ON_3dPoint P1 = m_path.to;
03844       if ( t0 < m_t[0] )
03845       {
03846         bChanged = true;
03847         m_path_domain.m_t[0] = domain[0];
03848         if ( t0 < 0.0 )
03849         {
03850           P0 = m_path.PointAt(t0);
03851           m_t[0] = 0.0;
03852         }
03853         else
03854           m_t[0] = t0;
03855       }
03856       if ( t1 > m_t[1] )
03857       {
03858         bChanged = true;
03859         m_path_domain.m_t[1] = domain[1];
03860         if ( t1 > 1.0 )
03861         {
03862           P1 = m_path.PointAt(t1);
03863           m_t[1] = 1.0;
03864         }
03865         else
03866           m_t[1] = t1;
03867       }
03868       if ( bChanged )
03869       {
03870         m_path.from = P0;
03871         m_path.to = P1;
03872         DestroySurfaceTree();
03873       }
03874     }
03875   }
03876   else if ( 0 == dir )
03877   {
03878     if ( m_profile )
03879     {
03880       rc = m_profile->Extend(domain);
03881       if (rc) 
03882         DestroySurfaceTree();
03883     }
03884   }
03885   return rc;
03886 }
03887 
03888 ON_BOOL32 ON_Extrusion::Split(
03889        int dir,
03890        double c,
03891        ON_Surface*& west_or_south_side,
03892        ON_Surface*& east_or_north_side
03893        ) const
03894 {
03895   if ( dir < 0 || dir > 1 || !ON_IsValid(c) )
03896     return false;
03897   if ( 0 != west_or_south_side && west_or_south_side == east_or_north_side )
03898     return false;
03899 
03900   ON_Interval domain = Domain(dir);
03901   double s = domain.NormalizedParameterAt(c);
03902   if ( s <= 0.0 || s >= 1.0 )
03903     return false;
03904   if (c <= domain[0] || c >= domain[1] )
03905     return false;
03906 
03907   ON_Extrusion* left = 0;
03908   ON_Extrusion* right = 0;
03909   if ( west_or_south_side )
03910   {
03911     left = ON_Extrusion::Cast(west_or_south_side);
03912     if ( !left )
03913       return false;
03914   }
03915   if ( east_or_north_side )
03916   {
03917     right = ON_Extrusion::Cast(east_or_north_side);
03918     if ( !right )
03919       return false;
03920   }
03921 
03922   const int path_dir = PathParameter();
03923   bool rc = false;
03924   if ( dir == path_dir )
03925   {
03926     // split path
03927     ON_Line left_path, right_path;
03928     ON_Interval left_domain, right_domain;
03929     ON_Interval left_t, right_t;
03930 
03931     const double t0 = m_t[0];
03932     const double t1 = m_t[1];
03933     const double t = (1.0-s)*t0 + s*t1;
03934     if ( !ON_IsValid(t) || t <= t0 || t >= t1 )
03935       return false;
03936 
03937     ON_3dPoint P = m_path.PointAt(s);
03938     left_path.from = m_path.from;
03939     left_path.to = P;
03940     right_path.from = P;
03941     right_path.to = m_path.to;
03942     left_domain.Set(domain[0],c);
03943     right_domain.Set(c,domain[1]);
03944     left_t.Set(t0,t);
03945     right_t.Set(t,t1);
03946     if ( !left_path.IsValid() || left_path.Length() <= m_path_length_min )
03947       return false;
03948     if ( !right_path.IsValid() || right_path.Length() <= m_path_length_min )
03949       return false;
03950 
03951     // return result
03952     if ( !left )
03953       left = new ON_Extrusion(*this);
03954     else if ( left != this )
03955       left->operator =(*this);
03956     else
03957       left->DestroyRuntimeCache();
03958     if ( !right )
03959       right = new ON_Extrusion(*this);
03960     else if ( right != this )
03961       right->operator =(*this);
03962     else
03963       right->DestroyRuntimeCache();
03964     left->m_path = left_path;
03965     left->m_path_domain = left_domain;
03966     left->m_t = left_t;
03967     right->m_path = right_path;
03968     right->m_path_domain = right_domain;
03969     right->m_t = right_t;
03970 
03971     west_or_south_side = left;
03972     east_or_north_side = right;
03973     rc = true;
03974   }
03975   else
03976   {
03977     if ( 0 == m_profile )
03978       return false;
03979     ON_Curve* left_profile = 0;
03980     ON_Curve* right_profile = 0;
03981 
03982     if ( left == this )
03983     {
03984       left_profile = left->m_profile;
03985       left->DestroyRuntimeCache();
03986     }
03987     else if ( 0 != left && 0 != left->m_profile )
03988     {
03989       delete left->m_profile;
03990       left->m_profile = 0;
03991     }
03992 
03993     if ( right == this )
03994     {
03995       right_profile = right->m_profile;
03996       right->DestroyRuntimeCache();
03997     }
03998     else if ( 0 != right && 0 != right->m_profile )
03999     {
04000       delete right->m_profile;
04001       right->m_profile = 0;
04002     }
04003 
04004     if ( !m_profile->Split(c,left_profile,right_profile) )
04005       return false;
04006     if ( 0 == left_profile || 0 == right_profile )
04007     {
04008       if ( 0 != left_profile && m_profile != left_profile )
04009         delete left_profile;
04010       if ( 0 != right_profile && m_profile != right_profile )
04011         delete right_profile;
04012       return false;
04013     }
04014 
04015     ON_Curve* this_profile = 0;
04016     if ( left_profile != m_profile && right_profile != m_profile )
04017     {
04018       if ( left == this || right == this )
04019       {
04020         delete m_profile;
04021       }
04022       else
04023       {
04024         this_profile = m_profile;
04025       }
04026     }
04027 
04028     // Prevent this m_profile from being copied
04029     const_cast<ON_Extrusion*>(this)->m_profile = 0;
04030 
04031     // Create new left and right sides with NULL profiles
04032     if ( !left )
04033       left = new ON_Extrusion(*this);
04034     else if ( left != this )
04035       left->operator =(*this);
04036     if ( !right )
04037       right = new ON_Extrusion(*this);
04038     else if ( right != this )
04039       right->operator =(*this);
04040 
04041     // Restore this m_profile
04042     const_cast<ON_Extrusion*>(this)->m_profile = this_profile;
04043 
04044     // Set left and right profiles
04045     left->m_profile = left_profile;
04046     right->m_profile = right_profile;
04047 
04048     west_or_south_side = left;
04049     east_or_north_side = right;
04050     rc = true;
04051   }
04052 
04053   return rc;
04054 }
04055 
04056 int ON_Extrusion::GetNurbForm(
04057       ON_NurbsSurface& nurbs_surface,
04058       double tolerance
04059       ) const
04060 {
04061   if ( !m_profile )
04062     return 0;
04063 
04064   ON_Xform xform0,xform1;
04065   if ( !GetProfileTransformation(0,xform0) )
04066     return false;
04067   if ( !GetProfileTransformation(1,xform1) )
04068     return false;
04069 
04070   ON_NurbsCurve nc0;
04071   int rc = m_profile->GetNurbForm(nc0,tolerance);
04072   if ( rc <= 0 )
04073     return rc;
04074   if ( 3 != nc0.m_dim )
04075     nc0.ChangeDimension(3);
04076   ON_NurbsCurve nc1 = nc0;
04077   nc0.Transform(xform0);
04078   nc1.Transform(xform1);
04079 
04080   nurbs_surface.Create(3,nc0.m_is_rat,nc0.m_order,2,nc0.m_cv_count,2);
04081   memcpy(nurbs_surface.m_knot[0],nc0.m_knot,nurbs_surface.KnotCount(0)*sizeof(nurbs_surface.m_knot[0][0]));
04082   nurbs_surface.m_knot[1][0] = m_path_domain[0];
04083   nurbs_surface.m_knot[1][1] = m_path_domain[1];
04084   for ( int i = 0; i < nurbs_surface.m_cv_count[0]; i++ )
04085   {
04086     nurbs_surface.SetCV(i,0,ON::intrinsic_point_style,nc0.CV(i));
04087     nurbs_surface.SetCV(i,1,ON::intrinsic_point_style,nc1.CV(i));
04088   }
04089 
04090   if ( m_bTransposed )
04091     nurbs_surface.Transpose();
04092 
04093   return rc;
04094 }
04095 
04096 int ON_Extrusion::HasNurbForm() const
04097 {
04098   return m_profile ? m_profile->HasNurbForm() : 0;
04099 }
04100 
04101 bool ON_Extrusion::GetSurfaceParameterFromNurbFormParameter(
04102       double nurbs_s, double nurbs_t,
04103       double* surface_s, double* surface_t
04104       ) const
04105 {
04106   bool rc = true;
04107   if ( m_bTransposed )
04108   {
04109     double* p = surface_s; 
04110     surface_s = surface_t; 
04111     surface_t = p;
04112     double t = nurbs_s;
04113     nurbs_s = nurbs_t;
04114     nurbs_t = t;
04115   }
04116   if ( surface_s )
04117   {
04118     rc = m_profile 
04119        ? (m_profile->GetCurveParameterFromNurbFormParameter(nurbs_s,surface_s)?true:false) 
04120        : false;
04121   }
04122   if ( surface_t )
04123     *surface_t = nurbs_t;
04124   return rc;
04125 }
04126 
04127 bool ON_Extrusion::GetNurbFormParameterFromSurfaceParameter(
04128       double surface_s, double surface_t,
04129       double* nurbs_s,  double* nurbs_t
04130       ) const
04131 {
04132   bool rc = true;
04133   if ( m_bTransposed )
04134   {
04135     double p = surface_s; 
04136     surface_s = surface_t; 
04137     surface_t = p;
04138     double* t = nurbs_s;
04139     nurbs_s = nurbs_t;
04140     nurbs_t = t;
04141   }
04142   if ( nurbs_s )
04143   {
04144     rc = m_profile 
04145       ? (m_profile->GetNurbFormParameterFromCurveParameter(surface_s,nurbs_s)?true:false)
04146       : false;
04147   }
04148   if ( nurbs_t )
04149     *nurbs_t = surface_t;
04150   return rc;
04151 }
04152 
04153 ON_SumSurface* ON_Extrusion::SumSurfaceForm( 
04154   ON_SumSurface* sum_surface 
04155   ) const
04156 {
04157   int i;
04158   if ( 0 != sum_surface )
04159   {
04160     for ( i = 0; i < 2; i++ )
04161     {
04162       if ( sum_surface->m_curve[i] )
04163       {
04164         delete sum_surface->m_curve[i];
04165         sum_surface->m_curve[i] = 0;
04166       }
04167       sum_surface->m_basepoint = ON_3dVector::ZeroVector;
04168       sum_surface->m_bbox.Destroy();
04169     }
04170   }
04171 
04172   if ( 0 == m_profile || !m_path.IsValid() )
04173     return 0;
04174 
04175   if ( IsMitered() )
04176     return 0; // mitered extrusions cannot be represented as sum surfaces
04177 
04178   ON_Xform xform0;
04179   if ( !GetProfileTransformation(0.0,xform0) )
04180     return 0;
04181 
04182   ON_Curve* profile3d = 0;
04183   ON_LineCurve* path = 0;
04184   ON_Curve* curve0 = 0;
04185   ON_Curve* curve1 = 0;
04186   for(;;)
04187   {
04188     if ( 1 == ProfileCount() )
04189     {
04190       const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_profile);
04191       if ( 0 != polycurve && 1 == polycurve->Count() )
04192       {
04193         const ON_Curve* segment = polycurve->SegmentCurve(0);
04194         if ( 0 != segment )
04195         {
04196           profile3d = segment->DuplicateCurve();
04197           profile3d->SetDomain( m_profile->Domain() );
04198         }
04199       }
04200     }
04201     if ( 0 == profile3d )
04202     {
04203       profile3d = m_profile->DuplicateCurve();
04204       if ( 0 == profile3d )
04205         break;
04206     }
04207     if ( profile3d->IsLinear() && 0 == ON_LineCurve::Cast(profile3d) )
04208     {
04209       ON_LineCurve* line_curve = new ON_LineCurve();
04210       line_curve->m_line.from = profile3d->PointAtStart();
04211       line_curve->m_line.to = profile3d->PointAtEnd();
04212       line_curve->ON_Curve::SetDomain(profile3d->Domain());
04213       delete profile3d;
04214       profile3d = line_curve;
04215     }
04216     if ( !profile3d->ChangeDimension(3) )
04217       break;
04218     if ( !xform0.IsIdentity() && !profile3d->Transform(xform0) )
04219       break;
04220 
04221     path = new ON_LineCurve();
04222     if ( 0 == path )
04223       break;
04224     path->m_line.from = ON_3dPoint::Origin;
04225     path->m_line.to = (m_path.to - m_path.from);
04226     if ( !path->SetDomain( m_path_domain[0], m_path_domain[1] ) )
04227       break;
04228 
04229     curve0 = profile3d;
04230     curve1 = path;
04231     profile3d = 0;
04232     path = 0;
04233     break;
04234   }
04235   if ( 0 == curve0 || 0 == curve1 )
04236   {
04237     if ( 0 != profile3d )
04238       delete profile3d;
04239     if ( 0 != path )
04240       delete path;
04241     return 0;
04242   }
04243 
04244   ON_SumSurface* sumsrf = ( 0 != sum_surface ) ? sum_surface : new ON_SumSurface();
04245   if ( 0 == sumsrf )
04246   {
04247     delete curve0;
04248     delete curve1;
04249     return 0;
04250   }
04251 
04252   sumsrf->m_curve[0] = curve0;
04253   sumsrf->m_curve[1] = curve1;
04254   sumsrf->m_basepoint = ON_3dVector::ZeroVector;
04255   sumsrf->m_bbox = BoundingBox();
04256 
04257   if ( m_bTransposed )
04258     sumsrf->Transpose();
04259 
04260   return sumsrf;
04261 }
04262 
04263 ON_Extrusion* ON_Extrusion::Cylinder( 
04264   const ON_Cylinder& cylinder, 
04265   bool bCapBottom,
04266   bool bCapTop,
04267   ON_Extrusion* extrusion
04268   )
04269 {
04270   if ( !cylinder.IsValid() || !cylinder.IsFinite() )
04271     return 0;
04272 
04273   ON_Line path;
04274   path.from = cylinder.circle.plane.PointAt(0.0,0.0,cylinder.height[0]);
04275   path.to   = cylinder.circle.plane.PointAt(0.0,0.0,cylinder.height[1]);
04276   if ( !path.IsValid() || !(path.Length() > ON_ZERO_TOLERANCE) )
04277     return 0;
04278 
04279   ON_3dVector up = cylinder.circle.plane.yaxis;
04280   if (    !up.IsValid()
04281        || !up.IsUnitVector()
04282        || fabs(up*path.Tangent()) > ON_SQRT_EPSILON 
04283        )
04284     return 0;
04285 
04286   ON_ArcCurve* circle_curve = new ON_ArcCurve(cylinder.circle);
04287   circle_curve->m_arc.plane = ON_Plane::World_xy;
04288   circle_curve->m_dim = 2;
04289   if ( !circle_curve->IsValid() )
04290   {
04291     delete circle_curve;
04292     return 0;
04293   }
04294 
04295   ON_Extrusion* extrusion_cylinder = 0;
04296   if ( extrusion )
04297   {
04298     extrusion->Destroy();
04299     extrusion_cylinder = extrusion;
04300   }
04301   else
04302   {
04303     extrusion_cylinder = new ON_Extrusion();
04304   }
04305 
04306   if (    !extrusion_cylinder->SetPathAndUp(path.from,path.to,up)
04307        || !extrusion_cylinder->SetOuterProfile(circle_curve,false)
04308        || !extrusion_cylinder->IsValid()
04309        || !extrusion_cylinder->SetDomain(extrusion_cylinder->PathParameter(),cylinder.height[0],cylinder.height[1])
04310      )
04311   {
04312     if ( 0 == extrusion )
04313       delete extrusion_cylinder;
04314     return 0;
04315   }
04316 
04317   extrusion_cylinder->m_bCap[0] = bCapBottom ? true : false;
04318   extrusion_cylinder->m_bCap[1] = bCapTop    ? true : false;
04319 
04320   if ( !extrusion_cylinder->IsValid() )
04321   {
04322     if ( 0 == extrusion )
04323       delete extrusion_cylinder;
04324     return 0;
04325   }
04326 
04327   return extrusion_cylinder;
04328 }
04329 
04330 
04331 
04332 ON_Extrusion* ON_Extrusion::Pipe( 
04333   const ON_Cylinder& cylinder, 
04334   double other_radius,
04335   bool bCapBottom,
04336   bool bCapTop,
04337   ON_Extrusion* extrusion
04338   )
04339 {
04340   if (    !cylinder.IsValid() 
04341        || !ON_IsValid(other_radius)
04342        || !(fabs(other_radius - cylinder.circle.Radius()) > ON_ZERO_TOLERANCE)
04343        )
04344   {
04345     return 0;
04346   }
04347 
04348   double inner_radius = (other_radius < cylinder.circle.radius)
04349                       ? other_radius
04350                       : cylinder.circle.radius;
04351   double outer_radius = (other_radius < cylinder.circle.radius)
04352                       ? cylinder.circle.radius
04353                       : other_radius;
04354   if (    !ON_IsValid(inner_radius) 
04355        || !ON_IsValid(outer_radius)
04356        || !(outer_radius - inner_radius > ON_ZERO_TOLERANCE)
04357      )
04358   {
04359     return 0;
04360   }
04361 
04362   ON_Cylinder outer_cylinder = cylinder;
04363   outer_cylinder.circle.radius = outer_radius;
04364 
04365   ON_Circle inner_circle(ON_Plane::World_xy,inner_radius);
04366   ON_ArcCurve* inner_profile = new ON_ArcCurve(inner_circle);
04367   inner_profile->m_dim = 2;
04368   if ( !inner_profile->IsValid() )
04369   {
04370     delete inner_profile;
04371     return 0;
04372   }
04373 
04374   ON_Extrusion* extrusion_pipe = ON_Extrusion::Cylinder(outer_cylinder,bCapBottom,bCapTop,extrusion);
04375   if ( 0 == extrusion_pipe )
04376   {
04377     delete inner_profile;
04378     return 0;
04379   }
04380 
04381   if ( !extrusion_pipe->IsValid() )
04382   {
04383     if ( 0 == extrusion )
04384       delete extrusion_pipe;
04385     delete inner_profile;
04386     return 0;
04387   }
04388 
04389   if ( !extrusion_pipe->AddInnerProfile(inner_profile) )
04390   {
04391     if ( 0 == extrusion )
04392       delete extrusion_pipe;
04393     delete inner_profile;
04394     return 0;
04395   }
04396 
04397   if ( !extrusion_pipe->IsValid() )
04398   {
04399     if ( 0 == extrusion )
04400       delete extrusion_pipe;
04401     return 0;
04402   }
04403 
04404   return extrusion_pipe;
04405 }
04406 
04407 
04408 ON_Extrusion* ON_Extrusion::CreateFrom3dCurve( 
04409     const ON_Curve& curve,
04410     const ON_Plane* plane,
04411     double height,
04412     bool bCap,
04413     ON_Extrusion* extrusion
04414     )
04415   {
04416     if ( 0 != extrusion )
04417       extrusion->Destroy();
04418 
04419     if ( ON_IsValid(height) && 0.0 == height )
04420       return 0;
04421 
04422     ON_Interval z(0.0,height);
04423     if ( z.IsDecreasing() )
04424       z.Swap();
04425     if ( !z.IsIncreasing() )
04426       return 0;
04427     
04428     if ( !curve.IsValid() )
04429       return 0;
04430 
04431     ON_Plane curve_plane;
04432     if ( 0 == plane )
04433     {
04434       if ( !curve.IsPlanar(&curve_plane) )
04435         return 0;
04436       plane = &curve_plane;
04437     }
04438 
04439     if ( !plane->IsValid() )
04440       return 0;
04441 
04442     ON_Xform xform2d;
04443     xform2d.ChangeBasis(ON_Plane::World_xy,*plane);
04444 
04445     ON_Curve* curve2d = curve.DuplicateCurve();
04446     if ( 0 == curve2d )
04447       return 0;
04448 
04449     ON_Extrusion* result = 0;
04450 
04451     for (;;)
04452     {
04453       if ( !curve2d->Transform(xform2d) )
04454         break;
04455       curve2d->ChangeDimension(2);
04456 
04457       if ( 0 == extrusion )
04458         result = new ON_Extrusion();
04459       else
04460         result = extrusion;
04461 
04462       if ( !result->SetPathAndUp(
04463                   plane->PointAt(0.0,0.0,z[0]),
04464                   plane->PointAt(0.0,0.0,z[1]),
04465                   plane->yaxis
04466                   ) )
04467         break;
04468 
04469       if ( !result->SetOuterProfile(curve2d,bCap) )
04470         break;
04471 
04472       if ( !result->IsValid() )
04473         break;
04474 
04475       // success
04476       curve2d = 0;
04477 
04478       break;
04479     }
04480 
04481     if ( 0 != curve2d )
04482     {
04483       // failure
04484       delete curve2d;
04485       curve2d = 0;
04486       
04487       if ( 0 != result && result != extrusion )
04488         delete result;
04489 
04490       if ( extrusion )
04491         extrusion->Destroy();
04492 
04493       result = 0;
04494     }
04495 
04496     return result;
04497   }


pcl
Author(s): Open Perception
autogenerated on Wed Aug 26 2015 15:26:59