diff --git a/opennurbs_annotationbase.cpp b/opennurbs_annotationbase.cpp index e55a78d9..c05b58f2 100644 --- a/opennurbs_annotationbase.cpp +++ b/opennurbs_annotationbase.cpp @@ -1216,6 +1216,13 @@ bool ON_Annotation::Internal_GetBBox_TextGlyphBox( text_glyph_box = ON_BoundingBox::UnsetBoundingBox; if (m_text) { + ON_Xform txf; + // 20 June 2017 S. Baer (RH-39835) + // GetTextXform can change cached information. Make sure this + // is called before m_text->BoundingBox() + // 23 June, 2020 - Lowell - Moved it above m_text->BoundingBox() + bool b = GetTextXform(vp, dimstyle, dimscale, txf); + text_glyph_box = m_text->BoundingBox(); // if mask, grow 2d bbox @@ -1232,11 +1239,6 @@ bool ON_Annotation::Internal_GetBBox_TextGlyphBox( text_glyph_box.m_max = bmax; } - ON_Xform txf; - // 20 June 2017 S. Baer (RH-39835) - // GetTextXform can change cached information. Make sure this - // is called before m_text->BoundingBox() - bool b = GetTextXform(vp, dimstyle, dimscale, txf); if (b) { text_glyph_box.Transform(txf); diff --git a/opennurbs_array.h b/opennurbs_array.h index 577c492e..40efd3b3 100644 --- a/opennurbs_array.h +++ b/opennurbs_array.h @@ -1572,6 +1572,55 @@ template< class T> static int ON_CompareDecreasing( const T* a, const T* b); +void ON_SHA1_Accumulate2fPointArray( + class ON_SHA1& sha1, + const class ON_SimpleArray& a +); + +void ON_SHA1_Accumulate3fPointArray( + class ON_SHA1& sha1, + const class ON_SimpleArray& a +); + +void ON_SHA1_Accumulate4fPointArray( + class ON_SHA1& sha1, + const class ON_SimpleArray& a +); + +void ON_SHA1_Accumulate2fVectorArray( + class ON_SHA1& sha1, + const class ON_SimpleArray& a +); + +void ON_SHA1_Accumulate3fVectorArray( + class ON_SHA1& sha1, + const class ON_SimpleArray& a +); + +void ON_SHA1_Accumulate2dPointArray( + class ON_SHA1& sha1, + const class ON_SimpleArray& a +); + +void ON_SHA1_Accumulate3dPointArray( + class ON_SHA1& sha1, + const class ON_SimpleArray& a +); + +void ON_SHA1_Accumulate4dPointArray( + class ON_SHA1& sha1, + const class ON_SimpleArray& a +); + +void ON_SHA1_Accumulate2dVectorArray( + class ON_SHA1& sha1, + const class ON_SimpleArray& a +); + +void ON_SHA1_Accumulate3dVectorArray( + class ON_SHA1& sha1, + const class ON_SimpleArray& a +); // definitions of the template functions are in a different file // so that Microsoft's developer studio's autocomplete utility diff --git a/opennurbs_bounding_box.cpp b/opennurbs_bounding_box.cpp index ce0651d2..42f4517a 100644 --- a/opennurbs_bounding_box.cpp +++ b/opennurbs_bounding_box.cpp @@ -1931,6 +1931,13 @@ bool ON_BoundingBox::SwapCoordinates( int i, int j ) return rc; } +bool ON_BoundingBox::Expand(ON_3dVector delta) +{ + m_min -= delta; + m_max += delta; + return IsValid(); +} + bool ON_BoundingBox::IsDisjoint( const ON_BoundingBox& other_bbox ) const { if ( m_min.x > m_max.x || other_bbox.m_min.x > other_bbox.m_max.x diff --git a/opennurbs_bounding_box.h b/opennurbs_bounding_box.h index 7b082df4..91b369c6 100644 --- a/opennurbs_bounding_box.h +++ b/opennurbs_bounding_box.h @@ -560,6 +560,16 @@ public: bool SwapCoordinates( int, int ); + /* + Description: + Expand the box by adding delta to m_max and subtracting + it from m_min. So, when delta is positive and the interval is + increasing this function expands the box on each side. + Returns: + true if the result is Valid. + */ + bool Expand(ON_3dVector delta); + ON_3dPoint m_min; ON_3dPoint m_max; }; diff --git a/opennurbs_brep.cpp b/opennurbs_brep.cpp index 83080f21..af4dbb17 100644 --- a/opennurbs_brep.cpp +++ b/opennurbs_brep.cpp @@ -889,6 +889,29 @@ ON_BrepLoop* ON_BrepFace::OuterLoop() const return 0; } +unsigned int ON_BrepFace::PackId() const +{ + return 0x10000U * ((unsigned int)m_pack_id_high) + ((unsigned int)m_pack_id_low); +} + +void ON_BrepFace::ClearPackId() +{ + m_pack_id_low = 0; + m_pack_id_high = 0; +} + +void ON_BrepFace::SetPackIdForExperts( + unsigned int pack_id +) +{ + if (0 == pack_id) + ClearPackId(); + else + { + m_pack_id_low = (ON__UINT16)(pack_id % 0x10000U); + m_pack_id_high = (ON__UINT16)(pack_id / 0x10000U); + } +} bool ON_BrepFace::IsValid( ON_TextLog* text_log ) const { @@ -1581,13 +1604,12 @@ bool ON_Brep::Transform( const ON_Xform& xform ) // Transforming the bbox makes it grow too large under repeated // rotations. So, we need to reset it. face.m_bbox.Destroy(); - const ON_Surface* srf = face.SurfaceOf(); - if ( 0 != srf ) - { - face.m_bbox = srf->BoundingBox(); - if ( face.m_face_index != -1 ) - m_bbox.Union( face.m_bbox ); - } + + //GBA 20 May 2020. Brep box now computed from face boxes, instead of surface boxes. + face.m_bbox = face.BoundingBox(); + if ( face.m_face_index != -1 ) + m_bbox.Union( face.m_bbox ); + // 12 May 2003 Dale Lear - RR 10528 // Use surface evaluation to update rendermesh when @@ -1600,6 +1622,8 @@ bool ON_Brep::Transform( const ON_Xform& xform ) || xform[3][2] != 0.0 || xform[3][3] != 1.0 ); + const ON_Surface* srf = face.SurfaceOf(); + if ( 0 == srf ) bEvMesh = false; @@ -2222,6 +2246,9 @@ void ON_Brep::Append( const ON_Brep& b ) ON_BrepLoop& ON_Brep::NewLoop( ON_BrepLoop::TYPE looptype ) { + // 2 Sept 2020 S. Baer (RH-59952) + // Destroy cached bounding box on breps when messing around with loops + m_bbox.Destroy(); m_is_solid = 0; int li = m_L.Count(); m_L.Reserve(li+1); @@ -2236,6 +2263,9 @@ ON_Brep::NewLoop( ON_BrepLoop::TYPE looptype ) ON_BrepLoop& ON_Brep::NewLoop( ON_BrepLoop::TYPE looptype, ON_BrepFace& face ) { + // 2 Sept 2020 S. Baer (RH-59952) + // Destroy cached bounding box on breps when messing around with loops + m_bbox.Destroy(); m_is_solid = 0; ON_BrepLoop& loop = NewLoop( looptype ); loop.m_fi = face.m_face_index; @@ -2265,6 +2295,8 @@ ON_BrepLoop* ON_Brep::NewOuterLoop( int face_index ) ON_BrepFace& ON_Brep::NewFace( int si ) { m_bbox.Destroy(); + // GBA 28-MAy-2020 RH-58462. m_bbox is now left unset after this function + // This works since ON_BrepFace::BoundingBox() supports lazy evaluation m_is_solid = 0; int fi = m_F.Count(); m_F.Reserve(fi+1); @@ -2274,11 +2306,8 @@ ON_BrepFace& ON_Brep::NewFace( int si ) face.m_si = si; face.m_brep = this; if ( si >= 0 && si < m_S.Count() ) - { face.SetProxySurface(m_S[si]); - if ( face.ProxySurface() ) - face.m_bbox = face.ProxySurface()->BoundingBox(); - } + return face; } @@ -5439,6 +5468,47 @@ bool ON_Brep::IsValid( ON_TextLog* text_log ) const } + // GBA 28-Aug-20 RH-60112 and RH-58462 + // Adding bounding box (m_bbox) validation tests. + // Brep bounding box is cached and persists across sessions (since at least Rhino 6). + // In Rhino6 and earlier, the bounding box included entire underlying surface. + // Rhino7 bounding box calculation now uses "shrinked" surfaces. + // A bounding box is now reported as invalid if it could be significantly + // reduced by being recalculated. + if (!m_bbox.IsEmpty()) + { + if (!m_bbox.IsValid()) + { + if (text_log) + text_log->Print("Bounding Box is not valid.\n"); + return ON_BrepIsNotValid(); + } + else + { + ON_BoundingBox orig_box = m_bbox; + ON_BoundingBox computed_box; + { + ON_Brep* this_nonconst = const_cast(this); + this_nonconst->ClearBoundingBox(); + computed_box = BoundingBox(); + this_nonconst->m_bbox = orig_box; // restore box as it as it was. + } + // expand the computed_box before we do the incusion test + // I'm trying to avoid making a lot of objects created in Rhino 6 and earlier + // reporting as Invalid objects in Rhino 7. + computed_box.Expand(computed_box.Diagonal() + ON_3dVector(1.0, 1.0, 1.0)); + + if (!computed_box.Includes(orig_box)) + { + if (text_log) + text_log->Print("Stored Bounding Box extends far outside of computed bounding box.\n"); + return ON_BrepIsNotValid(); + + } + + } + } + #if 0 // validate ON_BrepTrim.m_pline for ( ti = 0; ti < trim_count; ti++ ) @@ -6357,6 +6427,9 @@ void ON_BrepFace::ClearBoundingBox() m_bbox.Destroy(); } + +// ON_BrepFace::GetBBox performs lazy evaluation. Namely, if m_bbox is invalid then the bounding box is +// computed and the value is stored in m_bbox to speed future calls. bool ON_BrepFace::GetBBox( double* box_min, // [3], double* box_max, // [3], @@ -6370,11 +6443,49 @@ bool ON_BrepFace::GetBBox( && &m_brep->m_F[m_face_index] == this ) { - const ON_Surface* srf = ProxySurface(); - if ( srf && srf != this ) + ON_BoundingBox pbox; + + for (int li = 0; li < LoopCount(); li++) { - srf->GetBoundingBox( const_cast(this)->m_bbox, false ); + ON_BrepLoop* loop = Loop(li); + if (loop && loop->m_type==ON_BrepLoop::outer) + { + m_brep->SetTrimBoundingBoxes( *loop, true); // sets loop->m_pbox + loop->GetBoundingBox(pbox, pbox.IsValid()); + break; + } } + + ON_Interval pudom(pbox[0].x, pbox[1].x); + ON_Interval pvdom(pbox[0].y, pbox[1].y); + // fatten up invervals to get slightly larger boxes. + pudom.Expand(.1 * pudom.Length()); + pvdom.Expand(.1 * pvdom.Length()); + ON_Interval Sdom[]= { Domain(0), Domain(1) }; + bool Used_pbox = false; + if (pbox.IsValid() && + (Sdom[0].Includes(pudom, true) || Sdom[1].Includes(pvdom, true))) + { + ON_Surface* temp_srf = DuplicateSurface(); + if (temp_srf) + { + if (Sdom[0].Includes(pudom, true)) + temp_srf->Trim(0, pudom); + if (Sdom[1].Includes(pvdom, true)) + temp_srf->Trim(1, pvdom); + temp_srf->GetBoundingBox(const_cast(this)->m_bbox, false); + delete temp_srf; + temp_srf = nullptr; + Used_pbox = true; + } + } + if (!Used_pbox) + { + const ON_Surface* srf = ProxySurface(); + if(srf) + srf->GetBoundingBox(const_cast(this)->m_bbox, false); + } + } bool rc = m_bbox.IsValid(); @@ -6417,10 +6528,11 @@ bool ON_Brep::GetBBox( { if ( m_F[fi].m_face_index == -1 ) continue; - const ON_Surface* srf = m_F[fi].ProxySurface(); - if ( !srf ) - continue; - srf->GetBoundingBox( bbox, bbox.IsValid() ); + + //GBA 20 May 2020. RH-58462. Brep box now computed from face boxes, instead of surface boxes. + const ON_BrepFace* f = Face(fi); + if(f) + f->GetBoundingBox( bbox, bbox.IsValid() ); } ON_Brep* ptr = const_cast(this); ptr->m_bbox = bbox; @@ -8025,22 +8137,34 @@ void ON_Brep::Dump( ON_TextLog& dump ) const } dump.Print(")\n"); dump.PushIndent(); - if ( face.m_render_mesh ) + + if (nullptr != face.m_render_mesh) { - const char* mp_style = "Custom"; const ON_MeshParameters* mp = face.m_render_mesh->MeshParameters(); - if ( mp ) - { - if ( 0 == ON_MeshParameters::CompareGeometrySettings(*mp,ON_MeshParameters::FastRenderMesh) ) - mp_style = "Fast"; - else if ( 0 == ON_MeshParameters::CompareGeometrySettings(*mp,ON_MeshParameters::QualityRenderMesh) ) - mp_style = "Quality"; - } - dump.Print("%s render mesh: %d polygons\n",mp_style,face.m_render_mesh->FaceCount()); + ON_wString mp_description = (nullptr != mp) ? mp->Description() : ON_wString(L"Unknown"); + dump.Print(L"Render mesh = %ls. %d polygons\n", static_cast(mp_description), face.m_render_mesh->FaceCount()); } - if ( face.m_analysis_mesh ) { - dump.Print("Analysis mesh: %d polygons\n",face.m_analysis_mesh->FaceCount()); + else + dump.Print("Render mesh = nullptr\n"); + + if (nullptr != face.m_analysis_mesh) + { + const ON_MeshParameters* mp = face.m_analysis_mesh->MeshParameters(); + ON_wString mp_description = (nullptr != mp) ? mp->Description() : ON_wString(L"Unknown"); + dump.Print(L"Analysis mesh = %ls. %d polygons\n", static_cast(mp_description), face.m_analysis_mesh->FaceCount()); } + else + dump.Print("Analysis mesh = nullptr\n"); + + if (nullptr != face.m_preview_mesh) + { + const ON_MeshParameters* mp = face.m_preview_mesh->MeshParameters(); + ON_wString mp_description = (nullptr != mp) ? mp->Description() : ON_wString(L"Unknown"); + dump.Print(L"Preview mesh = %ls. %d polygons\n", static_cast(mp_description), face.m_preview_mesh->FaceCount()); + } + else + dump.Print("Preview mesh = nullptr\n"); + if ( FaceIsSurface(fi) ) { dump.Print("(Face geometry is the same as underlying surface.)\n"); } @@ -8685,6 +8809,9 @@ void ON_Brep::DeleteTrim(ON_BrepTrim& trim, bool bDeleteTrimEdges ) void ON_Brep::DeleteLoop(ON_BrepLoop& loop, bool bDeleteLoopEdges ) { + // 2 Sept 2020 S. Baer (RH-59952) + // Destroy cached bounding box on breps when messing around with loops + m_bbox.Destroy(); m_is_solid = 0; const int li = loop.m_loop_index; @@ -8718,6 +8845,9 @@ void ON_Brep::DeleteLoop(ON_BrepLoop& loop, bool bDeleteLoopEdges ) if ( face.m_li[fli] == li ) { face.m_li.Remove(fli); + // 2 Sept 2020 S. Baer (RH-59952) + // clear cached face bbox when a loop is removed + face.m_bbox.Destroy(); } } } @@ -9132,6 +9262,7 @@ ON_Brep* ON_Brep::DuplicateFaces( int face_count, const int* face_index, bool bD face_copy.m_domain[0] = face.m_domain[0]; face_copy.m_domain[1] = face.m_domain[1]; face_copy.m_per_face_color = face.m_per_face_color; + face_copy.SetMaterialChannelIndex(face.MaterialChannelIndex()); // do NOT duplicate meshes here // duplicate loops and trims diff --git a/opennurbs_brep.h b/opennurbs_brep.h index 17317554..47c31ce3 100644 --- a/opennurbs_brep.h +++ b/opennurbs_brep.h @@ -973,7 +973,8 @@ public: mutable ON_ComponentStatus m_status = ON_ComponentStatus::NoneSet; private: - ON__UINT16 m_reserved1 = 0U; + // the 4 byte pack id is stored as 2 ON__UINT16 values to prevent breaking the C++ SDK. + ON__UINT16 m_pack_id_low = 0; // PackId() = 0x10000*m_pack_id_high + m_pack_id_low; public: int m_face_index = -1; // index of face in ON_Brep.m_F[] array @@ -1176,9 +1177,70 @@ public: bool m_bRev = false; // true if face orientation is opposite // of natural surface orientation + + /* + Returns: + 0: unset pack id. + > 0: set pack id. + Remarks: + PackId values assigned to brep faces are inheritied from the PackId values + assigned to subd faces when a subd is converted into a brep. + These faces are "trivially trimmed" which means the boundary of the face + is identical to the boundary of the underlying surface. + There are two types of face packs in a subd, quad grid packs and singleton packs. + A subd quad grid pack is a set of subd quads that form a rectangular grid. + A subd singleton pack is a single face, quad or n-gon, that is not part of + a quad grid pack. + There are three types of face packs in a brep created from a subd, + grid packs, star packs and singleton packs. + A brep "grid pack" comes from a rectangular grid of subd quads. A grid pack of brep faces can + be converted into a single larger trivially trimmed brep face. + A brep "star pack" of brep faces comes from a singel subd n-gon (n = 3, 5 or more). The star pack + will have n faces with a star center vertex and shared edges radiating from the star center. + A brep "singleton" pack comes from a single subd quad that could not be grouped into a larger + subd quad grid pack. + */ + unsigned int PackId() const; + + /* + Description: + Sets PackId() to zero. + */ + void ClearPackId(); + + /* + Description: + Used by ON_SubD functions that create breps to transmit the subd face ON_SubDFace.PackId() value + to the brep face or faces generated from the subd face. + Unless you are an expert and doing something very carefully and very fancy, to not call this function. + Remarks: + PackId values assigned to brep faces are inheritied from the PackId values + assigned to subd faces when a subd is converted into a brep. + These faces are "trivially trimmed" which means the boundary of the face + is identical to the boundary of the underlying surface. + There are two types of face packs in a subd, quad grid packs and singleton packs. + A subd quad grid pack is a set of subd quads that form a rectangular grid. + A subd singleton pack is a single face, quad or n-gon, that is not part of + a quad grid pack. + There are three types of face packs in a brep created from a subd, + grid packs, star packs and singleton packs. + A brep "grid pack" comes from a rectangular grid of subd quads. A grid pack of brep faces can + be converted into a single larger trivially trimmed brep face. + A brep "star pack" of brep faces comes from a singel subd n-gon (n = 3, 5 or more). The star pack + will have n faces with a star center vertex and shared edges radiating from the star center. + A brep "singleton" pack comes from a single subd quad that could not be grouped into a larger + subd quad grid pack. + */ + void SetPackIdForExperts( + unsigned int pack_id + ); + private: ON__UINT8 m_reserved2 = 0; - ON__UINT16 m_reserved3 = 0; + +private: + // the 4 byte pack id is stored as 2 ON__UINT16 values to prevent breaking the C++ SDK. + ON__UINT16 m_pack_id_high = 0; // PackId() = 0x10000*m_pack_id_high + m_pack_id_low; public: // The application specifies a base ON_Material used to render the brep this face belongs to. @@ -1744,6 +1806,7 @@ public: int GetMesh( ON::mesh_type mesh_type, ON_SimpleArray< const ON_Mesh* >& meshes ) const; + /* Description: Create a brep from a surface. The resulting surface has an outer @@ -4042,6 +4105,13 @@ protected: bool ReadV1_LegacyLoop( ON_BinaryArchive&, ON_BrepFace& ); bool ReadV1_LegacyFaceStuff( ON_BinaryArchive& ); bool ReadV1_LegacyShellStuff( ON_BinaryArchive& ); + + + +public: + // The ON_Brep code increments ON_Brep::ErrorCount everytime something + // unexpected happens. This is useful for debugging. + static unsigned int ErrorCount; }; /////////////////////////////////////////////////////////////////////////////// @@ -4677,4 +4747,25 @@ bool ON_OrderEdgesAroundVertex(const ON_Brep& B, int vid, bool& bClosed); + + + + +#if defined(ON_COMPILING_OPENNURBS) +////////////////////////////////////////////////////////////////////////// +// +// ON_BrepIncrementErrorCount() +// +// Appears in places where the code traps error conditions. +// Putting a breakpoint on the line indicated below is an easy way +// to have the debugger break on all error conditions and inspect +// the first place something goes wrong in a complex calculation. +// +void ON_BrepIncrementErrorCount(); // defined in opennurbs_error.cpp +#define ON_BREP_ERROR(msg) (ON_BrepIncrementErrorCount(), ON_ERROR(msg)) +#define ON_BREP_RETURN_ERROR(rc) (ON_BrepIncrementErrorCount(), rc) +#define ON_BREP_RETURN_ERROR_MSG(msg, rc) \ + (ON_BrepIncrementErrorCount(), ON_ERROR(msg), rc) +#endif + #endif diff --git a/opennurbs_brep_io.cpp b/opennurbs_brep_io.cpp index 212b9ac9..41894ea6 100644 --- a/opennurbs_brep_io.cpp +++ b/opennurbs_brep_io.cpp @@ -1101,6 +1101,20 @@ bool ON_Brep::Read( ON_BinaryArchive& file ) m_is_solid = 0; } + // GBA 3-Sept-20 RH-60112 + // In V6 and earlier bounding boxes of faces were bounding box of underlying surface. + // This can result in enourmous boxes which mess up make2d + // In V7 bounding boxes use the trim pbox resulting in resonable boxes. + if (file.Archive3dmVersion() < 70 || file.ArchiveOpenNURBSVersion() < 2382395020) + { + if (!m_bbox.IsEmpty()) + { + ON_BoundingBox orig_box = m_bbox; + ClearBoundingBox(); + BoundingBox(); // compute bounding box + m_bbox.Intersection(orig_box); + } + } return rc; } diff --git a/opennurbs_brep_tools.cpp b/opennurbs_brep_tools.cpp index 5789cb77..77e98228 100644 --- a/opennurbs_brep_tools.cpp +++ b/opennurbs_brep_tools.cpp @@ -485,6 +485,9 @@ ON_BrepLoop* ON_Brep::NewOuterLoop( bool boolRev3d[4] ) { + // 2 Sept 2020 S. Baer (RH-59952) + // Destroy cached bounding box on breps when messing around with loops + m_bbox.Destroy(); m_is_solid = 0; if ( face_index < 0 || face_index >= m_F.Count() ) return nullptr; diff --git a/opennurbs_color.cpp b/opennurbs_color.cpp index 93cd9ba0..a4f97926 100644 --- a/opennurbs_color.cpp +++ b/opennurbs_color.cpp @@ -24,6 +24,65 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif + +const ON_Color ON_Color::RandomColor() +{ + return RandomColor(ON_RandomNumberGenerator::RandomSeed()); +} + + +const ON_Color ON_Color::RandomColor( + ON_Interval hue_range, + ON_Interval saturation_range, + ON_Interval value_range +) +{ + return RandomColor(ON_RandomNumberGenerator::RandomSeed(), hue_range, saturation_range, value_range); +} + +const ON_Color ON_Color::RandomColor( + ON__UINT32 seed +) +{ + return ON_Color::RandomColor(seed, ON_Interval::ZeroToTwoPi, ON_Interval::Singleton(1.0), ON_Interval::Singleton(1.0)); +} + +const ON_Color ON_Color::RandomColor( + ON__UINT32 seed, + ON_Interval hue_range, + ON_Interval saturation_range, + ON_Interval value_range +) +{ + ON_RandomNumberGenerator rg; + rg.Seed(seed + 1U); // the +1 is so the colors for seeds (0,1,2,3,4,5 are more vistually distinct) + const double h = hue_range.IsSingleton() ? hue_range.m_t[0] : rg.RandomDouble(hue_range); + double s = saturation_range.IsSingleton() ? saturation_range.m_t[0] : rg.RandomDouble(saturation_range); + if (s > ON_UNSET_VALUE && s < ON_UNSET_POSITIVE_VALUE) + { + if (s < 0.0) + s = 0.0; + else if (s > 1.0) + s = 1.0; + } + else + s = 1.0; + double v = value_range.IsSingleton() ? value_range.m_t[0] : rg.RandomDouble(value_range); + if (v > ON_UNSET_VALUE && v < ON_UNSET_POSITIVE_VALUE) + { + if (v < 0.0) + v = 0.0; + else if (v > 1.0) + v = 1.0; + } + else + v = 1.0; + ON_Color color = ON_Color::UnsetColor; + if (ON_IsValid(h) && ON_IsValid(s) && ON_IsValid(v)) + color.SetHSV( fmod(h,ON_2PI), s, v ); + return color; +} + ON_Color::ON_Color(unsigned int colorref) : m_color(colorref) { diff --git a/opennurbs_color.h b/opennurbs_color.h index d69e700c..a3d68386 100644 --- a/opennurbs_color.h +++ b/opennurbs_color.h @@ -64,6 +64,57 @@ public: kAlphaByteIndex = 3 }; + /* + Returns: + A random color. + */ + static const ON_Color RandomColor(); + + /* + Parameters: + seed - [in] + hue_range - [in] + range of hues. Use ON_Interval::ZeroToTwoPi for all hues. + saturation_range - [in] + range of saturations. Use ON_Interval::ZeroToOne for all saturations. + value_range - [in] + range of values. Use ON_Interval::ZeroToOne for all values. + Returns: + A color generated from seed. The color for a given seed will always be the same. + */ + static const ON_Color RandomColor( + ON_Interval hue_range, + ON_Interval saturation_range, + ON_Interval value_range + ); + + /* + Returns: + A color generated from seed. The color for a given seed will always be the same. + */ + static const ON_Color RandomColor( + ON__UINT32 seed + ); + + /* + Parameters: + seed - [in] + hue_range - [in] + range of hues. Use ON_Interval::ZeroToTwoPi for all hues. + saturation_range - [in] + range of saturations. Use ON_Interval::ZeroToOne for all saturations. + value_range - [in] + range of values. Use ON_Interval::ZeroToOne for all values. + Returns: + A color generated from seed. The color for a given seed will always be the same. + */ + static const ON_Color RandomColor( + ON__UINT32 seed, + ON_Interval hue_range, + ON_Interval saturation_range, + ON_Interval value_range + ); + // If you need to use shifting to convert RGBA components to and from // an unsigned int ON_COlor value and you want your code to work // on both little and big endian computers, use the RGBA_shift enum. diff --git a/opennurbs_convex_poly.cpp b/opennurbs_convex_poly.cpp index a593d262..0730efe2 100644 --- a/opennurbs_convex_poly.cpp +++ b/opennurbs_convex_poly.cpp @@ -121,6 +121,18 @@ double ON_3dSimplex::SignedVolume() const return vol; } +double ON_3dSimplex::MaximumCoordinate() const +{ + double max = 0.0; + for (int i = 0; i < Count(); i++) + { + double m = m_V[i].MaximumCoordinate(); + if (m > max) + max = m; + } + return max; +} + ON_BoundingBox ON_3dSimplex::BoundingBox() const { ON_BoundingBox box; @@ -360,17 +372,41 @@ bool ON_3dSimplex::Closest1plex(ON_4dPoint& Bary) const else { double b0 = dot / Del2; + b0 = 1 - (1 - b0); // ensure b0 + ( 1- b0) == 1.0 without rounding Bary = ON_4dPoint(1 - b0, b0, 0, 0); } } return rc; } + +// Rounds barycentric coordinates so that the coordinates sum to 1.0 in exact arithmetic. +bool ON_3dSimplex::RoundBarycentricCoordinate(ON_4dPoint& Bary) +{ + int mini = -1; + double minc = ON_UNSET_VALUE; + for (int i = 0; i < 4; i++) + { + if (Bary[i] == 0.0) continue; + Bary[i] = 1 - (1 - Bary[i]); + if (mini < 0 || Bary[i] < minc) + { + mini = i; + minc = Bary[i]; + } + } + if (mini >= 0) + { + Bary[mini] = 1.0 - (Bary[(mini + 1) % 4] + Bary[(mini + 2) % 4] + Bary[(mini + 3) % 4]); + } + return true; +} + // closest point to origin for a 2-simplex bool ON_3dSimplex::Closest2plex(ON_4dPoint& Bary) const { bool rc = false; - ON_3dVector N = FaceNormal(0); + ON_3dVector N = FaceNormal(); double N2 = N.LengthSquared(); if (N2 > 0) { @@ -401,10 +437,13 @@ bool ON_3dSimplex::Closest2plex(ON_4dPoint& Bary) const bool interior = true; for (int j = 0; interior && j < 3; j++) interior = SameSign(DetM, C3[j]); + Bary[3] = 0.0; if (interior) { for (int i = 0; i < 3; i++) Bary[i] = C3[i] / DetM; + + RoundBarycentricCoordinate(Bary); rc = true; } else @@ -468,6 +507,9 @@ bool ON_3dSimplex::Closest3plex(ON_4dPoint& Bary) const { for (int i = 0; i < 4; i++) Bary[i] = C4[i] / detM; + + RoundBarycentricCoordinate(Bary); + rc = true; } else @@ -671,8 +713,8 @@ double ON_ConvexHullPoint2::MaximumCoordinate() const return ON_MaximumCoordinate(m_Vert[0], 3, false, m_Vert.Count()); } -bool ClosestPoint(const ON_3dPoint P0, const ON_ConvexPoly& poly, - ON_4dex& dex, ON_4dPoint& Bary, double atmost ) +bool ON_ConvexPoly::GetClosestPointSeeded(ON_3dPoint P0, + ON_4dex& dex, ON_4dPoint& Bary, double atmost ) const { ON_ConvexHullRef CvxPt(&P0, 1); // Set pdex to match the support of dex @@ -682,7 +724,7 @@ bool ClosestPoint(const ON_3dPoint P0, const ON_ConvexPoly& poly, if (dex[i] >= 0) pdex[i] = 0; } - bool rc = ClosestPoint(CvxPt, poly, pdex, dex, Bary, atmost); + bool rc = GetClosestPointSeeded(CvxPt, dex, pdex, Bary, atmost); ON_ConvexPoly::Standardize(dex, Bary); return rc; } @@ -705,6 +747,85 @@ static int MatchingSupport(const ON_4dex& A, const ON_4dex& B) // Gilbert Johnson Keerthi algorithm +bool ON_ConvexPoly::GetClosestPoint(const ON_ConvexPoly& B, + ON_4dex& Adex, ON_4dex& Bdex, ON_4dPoint& bary, + double maximum_distance) const +{ + Adex = Bdex = ON_4dex::Unset; + return GetClosestPointSeeded(B, Adex, Bdex, bary, maximum_distance); +}; + + +bool ON_ConvexPoly::GetClosestPoint(ON_3dPoint P0, + ON_4dex& dex, ON_4dPoint& bary, + double maximum_distance ) const +{ + dex = ON_4dex::Unset; + return GetClosestPointSeeded(P0, dex, bary, maximum_distance); +} + +// Class for a pair of simplicies from a pair of ON_ConvexPoly's +class GJK_Simplex +{ +public: + ON_3dSimplex Simp; // Minkowski sum simplex A - B + ON_4dPoint Bary = ON_4dPoint::Zero; // represents a point in Simp + int Aind[4] = { -1,-1,-1,-1 }; + int Bind[4] = { -1,-1,-1,-1 }; + + // Append new vertex at end + bool AddVertex(const ON_3dVector& v, int aind, int bind); + bool RemoveVertex(int i); // index of vertex pair to remove + + bool Includes(int aind, int bind); // true if (aind, bind) is a vertex pair in this simplex +}; + +bool GJK_Simplex::AddVertex(const ON_3dVector& v, int aind, int bind) +{ + bool rc = false; + int n0 = Simp.Count(); + if (n0 < 4) + { + Simp.AddVertex(v); + Aind[n0] = aind; + Bind[n0] = bind; + if (n0 > 0) + Bary[n0] = 0.0; + else + Bary[n0] = 1.0; + rc = true; + } + return rc; +} + +bool GJK_Simplex::RemoveVertex(int i) +{ + bool rc = false; + int n0 = Simp.Count(); + if (i < n0) + { + Simp.RemoveVertex(i); + + for (int j = i; j < n0-1; j++) + { + Bary[j] = Bary[j + 1]; + Aind[j] = Aind[j + 1]; + Bind[j] = Bind[j + 1]; + } + Bary[n0-1] = 0.0; + Aind[n0-1] = Bind[n0-1] = -1; + } + return rc; +} + +bool GJK_Simplex::Includes(int aind, int bind) // true if (aind, bind) is a vertex pair in this simplex +{ + int n0 = Simp.Count(); + for (int i = 0; i < n0; i++) + if (Aind[i] == aind && Bind[i] == bind) + return true; + return false; +} // To supply an inital seed simplex Adex and Bdex must be valid and // have matching support specifically @@ -713,17 +834,17 @@ static int MatchingSupport(const ON_4dex& A, const ON_4dex& B) // Adex[i]>=0 for some i for some i // By satisfying this condition Adex and Bdex will define a simplex in A - B // Note the result of a ClosestPoint calculation Adex and Bdex satisfy these conditions -bool ClosestPoint(const ON_ConvexPoly& A, const ON_ConvexPoly& B, - ON_4dex& Adex, ON_4dex& Bdex, ON_4dPoint& Bary, double atmost) +bool ON_ConvexPoly::GetClosestPointSeeded(const ON_ConvexPoly& B, + ON_4dex& Adex, ON_4dex& Bdex, ON_4dPoint& Bary, + double atmost) const { + const ON_ConvexPoly& A = *this; bool rc = false; - if (A.Count() == 0 || B.Count() == 0) + if (Count() == 0 || B.Count() == 0) return false; - int Aind[4] = { -1,-1,-1,-1 }; - int Bind[4] = { -1,-1,-1,-1 }; - ON_3dSimplex Simp; - Bary = ON_4dPoint(1, 0, 0, 0); + GJK_Simplex GJK; + ON_3dVector v(0,0,0); @@ -733,32 +854,21 @@ bool ClosestPoint(const ON_ConvexPoly& A, const ON_ConvexPoly& B, bool bFirstPass = false; if (A.IsValid4Dex(Adex) && B.IsValid4Dex(Bdex) && MatchingSupport(Adex, Bdex)>0 ) { - // Set the initial condition for Aind, Bind and Simp from Adex and Bdex - int j = 0; + // Set the initial condition for GJK from Adex and Bdex int i = 0; for (i = 0; i < 4; i++) { if (Adex[i] < 0 || Bdex[i] < 0) continue; - int jj; - for (jj = 0; jj < j; jj++) - { - if (Aind[jj] == Adex[i] && Bind[jj] == Bdex[i]) - break; - } - if (jj < j) - break; - Aind[j] = Adex[i]; - Bind[j] = Bdex[i]; - ON_3dVector vert = A.Vertex(Aind[j]) - B.Vertex(Bind[j]); - Simp.AddVertex(vert); - j++; - } - if( i==4) - bFirstPass = true; - else - bFirstPass = false; + if (GJK.Includes(Adex[i], Bdex[i])) + break; + + ON_3dVector vert = Vertex(Adex[i]) - B.Vertex(Bdex[i]); + GJK.AddVertex(vert, Adex[i], Bdex[i]); + } + + bFirstPass = (i==4); } bool done = false; @@ -769,24 +879,12 @@ bool ClosestPoint(const ON_ConvexPoly& A, const ON_ConvexPoly& B, if (!bFirstPass) { // Default initial simplex is a point A.Vertex(0) - B.Vertex(0); - Aind[0] = Bind[0] = 0; - Aind[1] = Aind[2] = Aind[3] = -1; - Bind[1] = Bind[2] = Bind[3] = -1; v = A.Vertex(0) - B.Vertex(0); - vlenlast = ON_DBL_MAX; + GJK.AddVertex(v, 0, 0); + GJK.Bary[0] = 1.0; + vlenlast = ON_DBL_MAX; vlen = v.Length(); - - Simp = ON_3dSimplex(); - Simp.AddVertex(v); } - // The key to the implemetation of this algorithm is contained in Simplex::GetClosestPointToOrigin() - - // These lines are for ON_3dSimplex - const bool SimplexReindexing = true; - - // These lines are for ON_JSimplex - //const bool SimplexReindexing = false; - //ON_JSimplex Simp; double mu = 0.0; const double epsilon = 10000.0 * ON_EPSILON; @@ -810,65 +908,52 @@ bool ClosestPoint(const ON_ConvexPoly& A, const ON_ConvexPoly& B, // See RH-30343 for a case where the 2.0 factor is needed // WRONG!!! RH-30343 again. the 2.0 factor doest't work either // TODO: testing... If the support vertex is already in simplex were done - int i = 0; - for (i = 0; i < 4; i++) - { - if (Aind[i] == wA && Bind[i] == wB) break; - } + + done = GJK.Simp.Count() == 4 || GJK.Includes(wA, wB); // See 100818MatchPoints.3dm if Bary>0 then we are done since closest point is // in the interior of a Simplex - if (i < 4 || Simp.Count()==4) - done = true; - else - done = ((vlen - mu) <= 2.0* mu *epsilon) || mu > atmost || vlen >= vlenlast ;//TODO "1+" ?? got rid of it + double SimpNorm = GJK.Simp.MaximumCoordinate(); + + // RH-59237. 24-June-20 Add term ||Simp||*epsilon to allow for roundoff error in Simp evaluation + // RH-59494. 13-Aug-20 Added a factor of 4.0 to ||Simp||*epsilon term. + // RH-59494.CSXBugB 19-Aug-20 increased a factor of 20.0 to ||Simp||*epsilon term. + if(!done) + done = ((vlen - mu) <= 2.0* mu *epsilon + SimpNorm*20.0*ON_EPSILON ) || mu > atmost || vlen >= vlenlast ; } if (!done) { - // n0 is the index of the newly added vertex - int n0; + if (!bFirstPass) - { - n0 = Simp.Count(); // this is specific to ON_3dSimplex - Simp.AddVertex(W); - Aind[n0] = wA; - Bind[n0] = wB; - } - else - n0 = Simp.Count() - 1; + GJK.AddVertex(W, wA, wB); - - - if (Simp.GetClosestPointToOrigin(Bary)) + // The key to the implemetation of this algorithm is contained in Simplex::GetClosestPointToOrigin() + if (GJK.Simp.GetClosestPointToOrigin(GJK.Bary)) { bFirstPass = false; - v = Simp.Evaluate(Bary); + v = GJK.Simp.Evaluate(GJK.Bary); vlenlast = vlen; vlen = v.Length(); - int imax = 3; - if (SimplexReindexing) - imax = n0; - for (int i = imax; i >= 0; i--) - { - if (Bary[i] == 0.0) - { - Simp.RemoveVertex(i); - if (SimplexReindexing) - { - /* The JSimplex the indicies are fixed so the following step is not needed. - the ON_3dSimplex type does change the index of verticies and this piece of - code adjusts for this.*/ - for (int j = i; j < n0; j++) - { - Bary[j] = Bary[j + 1]; - Aind[j] = Aind[j + 1]; - Bind[j] = Bind[j + 1]; - } - Bary[n0] = 0.0; - n0--; - } - } - } + if (true) + { + for (int i = GJK.Simp.Count() - 1; i >= 0; i--) + { + if (GJK.Bary[i] == 0.0) + GJK.RemoveVertex(i); + } + } + else + { + /* this is error recovery code. it is currenly DISABLED + // The last step resulted in crap lets undo it and set done + int n = GJK.Simp.Count() - 1; + GJK.RemoveVertex(n); + GJK.Simp.GetClosestPointToOrigin(GJK.Bary); + v = GJK.Simp.Evaluate(GJK.Bary); + vlen = v.Length(); + done = true; + */ + } } else { @@ -892,28 +977,27 @@ bool ClosestPoint(const ON_ConvexPoly& A, const ON_ConvexPoly& B, vlen is nearly 0. but it should be 0.0 If (0,0,0) s in the interior of A-B then solution is vlen == 0.0 */ - if (Simp.Count() == 4 && Simp.Volume() > ON_SQRT_EPSILON) + if (GJK.Simp.Count() == 4 && GJK.Simp.Volume() > ON_SQRT_EPSILON) { vlen = 0.0; } rc = (vlen <= atmost); if (rc) { - if (SimplexReindexing) + if (GJK.Simp.Count() > 0) { - if (Simp.Count() > 0) + for (int i = GJK.Simp.Count(); i < 4; i++) { - for (int i = Simp.Count(); i < 4; i++) - { - Bary[i] = 0.0; - Aind[i] = Bind[i] = -1; - } + GJK.Bary[i] = 0.0; + GJK.Aind[i] = GJK.Bind[i] = -1; } } - Adex = ON_4dex(Aind[0], Aind[1], Aind[2], Aind[3]); - Bdex = ON_4dex(Bind[0], Bind[1], Bind[2], Bind[3]); + + Adex = ON_4dex(GJK.Aind[0], GJK.Aind[1], GJK.Aind[2], GJK.Aind[3]); + Bdex = ON_4dex(GJK.Bind[0], GJK.Bind[1], GJK.Bind[2], GJK.Bind[3]); + Bary = GJK.Bary; } } return rc; diff --git a/opennurbs_convex_poly.h b/opennurbs_convex_poly.h index 3635038e..296b40b3 100644 --- a/opennurbs_convex_poly.h +++ b/opennurbs_convex_poly.h @@ -101,6 +101,9 @@ public: ON_3dPoint Vertex(int i) const; ON_3dPoint& Vertex(int i); + /* Maximum absolute value of vertex coordinates*/ + double MaximumCoordinate() const; + /* Description: Get Simplex's 3d axis aligned bounding box. @@ -177,6 +180,7 @@ private: bool Closest3plex(ON_4dPoint& Bary) const; bool Closest2plex(ON_4dPoint& Bary) const; bool Closest1plex(ON_4dPoint& Bary) const; + static bool RoundBarycentricCoordinate(ON_4dPoint& Bary); }; @@ -202,9 +206,10 @@ public: /* Description: - Let K be this ON_ConvexPoly then for a non-zero vector W the support Support(W) is a point x in K that maximizes - min arg x * W + Let K be this ON_ConvexPoly then for a non-zero vector W the support Support(W) are point in K defined by + arg max x * W x \in K + This method returns one of these points in Support(W). i0 is an optional initial index seed value. It may provide a performance enhancement toward finding a minimizer. */ @@ -244,6 +249,59 @@ public: return v; }; + /* +Description: + Computes the closest point on this convex polytope from a point P0. +Parameters: + P0 - [in] Base Point for closest point + dex -[out] + bary - [out] Evaluate(dex,bary) is the closest point on this polyhedran + maximum_distance - [in ] optional upper bound on distance + +Returns: + Returns true if a closest point is found and it is within optional maximum_distance bound; + +Details: + Setting maximum_distance can speedup the calculation in cases where dist(P0, *this)>maximum_distance. +*/ + bool GetClosestPoint( ON_3dPoint P0, + ON_4dex& dex, ON_4dPoint& bary, + double maximum_distance = ON_DBL_MAX) const; + + // Expert version of GetClosestPoint. + // dex is used at input to seed search algorithm. + // the points of *this singled out by dex must define a nondegenerate simplex + bool GetClosestPointSeeded(ON_3dPoint P0, + ON_4dex& dex, ON_4dPoint& Bary, + double maximum_distance = ON_DBL_MAX) const; + + /* + Description: + Computes a pair of points on *this and BHull that achieve the minimum distance between + the two convex polytopes. + Parameters: + BHull - [in] the other convex polytope + adex, bdex -[out] Evaluate(adex,bary) is the closest point on this polyhedron + bary - [out] BHull.Evaluate(bdex,bary) is the closest point on BHull. + maximum_distance - [in ] optional upper bound on distance + +Returns: + Returns true if a closest points are found and they are within optional maximum_distance bound; + +Details: + Setting maximum_distance can speedup the calculation in cases where dist(*this, BHull)>maximum_distance. + */ + bool GetClosestPoint(const ON_ConvexPoly& BHull, + ON_4dex& Adex, ON_4dex& Bdex, ON_4dPoint& bary, + double maximum_distance = ON_DBL_MAX) const; + + // Expert version of GetClosestPoint. +// Adex and Bdex are used at input to seed search algorithm. +// the points of this-Bhull singled out by Adex and Bdex must define a nondegenerate simplex + bool GetClosestPointSeeded(const ON_ConvexPoly& BHull, + ON_4dex& Adex, ON_4dex& Bdex, ON_4dPoint& bary, + double maximum_distance = ON_DBL_MAX) const; + /* Description: This is a bound on the collection of verticies. @@ -300,7 +358,7 @@ public: int Count() const override { return m_n; } ON_3dVector Vertex(int j) const override; - // Support map O( Vertes.Count + // Support map virtual int SupportIndex(ON_3dVector W, int i0) const override; virtual double MaximumCoordinate() const override; @@ -347,35 +405,6 @@ private: }; -/* - -Computes a closest point between convex polytopes AHull and BHull. -Returns true if a closest point is found and it is within optional maximum_distance bound; - -Specifically, when true is returned parameters (Adex, ABbary) on AHull -and (Bdex, ABbary) on BHull are found such that: -if a* = AHull.Evaluate(Adex,ABbary), and - b* = BHull.Evaluate(Bdex,ABbary), -then - d = dist(a*, b*) <= maximum_distance, and -for any a in AHull and b in BHull - dist(a,b) => d. - - -Setting maximum_distance = tol can speedup the calculation in cases where d>tol - -On input Adex and Bdex are used to define an intial simplex -use Adex = Bdex = ON_4dex::Unset if you have no good initial guess -Notice the result is indepentent of the initial guess. - -*/ -ON_DECL -bool ClosestPoint(const ON_ConvexPoly& AHull, const ON_ConvexPoly& BHull, - ON_4dex& Adex, ON_4dex& Bdex, ON_4dPoint& ABbary, double maximum_distance = ON_DBL_MAX); - -ON_DECL -bool ClosestPoint(const ON_3dPoint P0, const ON_ConvexPoly& poly, - ON_4dex& dex, ON_4dPoint& Bary, double maximum_distance = ON_DBL_MAX); /* Compute Convex hull of 2d points diff --git a/opennurbs_curve.cpp b/opennurbs_curve.cpp index 9c73173b..3e82a014 100644 --- a/opennurbs_curve.cpp +++ b/opennurbs_curve.cpp @@ -1839,8 +1839,6 @@ bool ON_NurbsCurve::RepairBadKnots( double knot_tolerance, bool bRepair ) && 0 != m_cv && 0 != m_knot && m_dim > 0 && m_cv_stride >= (m_is_rat)?(m_dim+1):m_dim - && 0 != m_cv - && 0 != m_knot && m_knot[m_cv_count-1] - m_knot[m_order-2] > knot_tolerance ) { @@ -1876,26 +1874,31 @@ bool ON_NurbsCurve::RepairBadKnots( double knot_tolerance, bool bRepair ) { if ( i < m_cv_count-2 ) { + // the last span is invalid rc = true; if ( bRepair ) { - // remove extra knots but do not change end point location + // 15-June-2020 + // Remove degenerate and small spans at the end, with out changing the domain + // and trying to maintain parametric curve ( i.e, C: Domain->R^d is invarient). DestroyRuntimeCache(); - double* cv = (double*)onmalloc(sizeof_cv); - ClampEnd(2); - memcpy( cv, CV(m_cv_count-1), sizeof_cv ); - m_cv_count = i+2; - ClampEnd(2); - memcpy( CV(m_cv_count-1), cv, sizeof_cv ); - for ( i = m_cv_count-1; i < m_cv_count+m_order-2; i++ ) - m_knot[i] = domain[1]; - onfree(cv); - cv = 0; + + if (knot_tolerance > 0) + { + // Tune up knots at end with 0< spacing m_order-1 ) { + // the first span is invalid rc = true; if ( bRepair ) { - // remove extra knots but do not change end point location + // 15-June-2020 + // Remove degenerate and small spans at the end, with out changing the domain + // and trying to maintain parametric curve ( i.e, C: Domain->R^d is invarient). DestroyRuntimeCache(); - i -= (m_order-1); - double* cv = (double*)onmalloc(sizeof_cv); - ClampEnd(2); - memcpy(cv,CV(0),sizeof_cv); - for ( j0 = 0, j1 = i; j1 < m_cv_count; j0++, j1++ ) - memcpy(CV(j0),CV(j1),sizeof_cv); - for ( j0 = 0, j1 = i; j1 < m_cv_count+m_order-2; j0++, j1++ ) - m_knot[j0] = m_knot[j1]; + if (knot_tolerance > 0) + { + // Tune up knots at start with 0< spacing m_order - 2; j0--) + m_knot[j0] = m_knot[m_order - 2]; + } + + // This is a hack way of eliminating knots and CV's from the start. + // Warning!! m_cv and m_knot need to be restored before we exit this function + + i = i - (m_order - 1); + m_cv += m_cv_stride * i ; + m_knot += i; m_cv_count -= i; - ClampEnd(2); - memcpy( CV(0), cv, sizeof_cv ); - for ( i = 0; i <= m_order-2; i++ ) - m_knot[i] = domain[0]; - onfree(cv); - cv = 0; + ClampEnd(0); // ...This hack works because ClampEnd does not reallocate cv or knot arrays + m_cv -= m_cv_stride * i ; + m_knot -= i ; + + + for (j0 = 0, j1 = i; j0 < m_cv_count; j0++, j1++) + memcpy(CV(j0), CV(j1), sizeof_cv); + + for (j0 = 0, j1 = i; j0 < m_cv_count+m_order-2; j0++, j1++) + m_knot[j0] = m_knot[j1]; + } else return rc; diff --git a/opennurbs_dimension.cpp b/opennurbs_dimension.cpp index e3c1782c..141d6aa4 100644 --- a/opennurbs_dimension.cpp +++ b/opennurbs_dimension.cpp @@ -3954,6 +3954,8 @@ bool ON_DimRadial::GetAnnotationBoundingBox( const ON_TextContent* text = Text(); ON_3dPoint text_rect[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin }; + dim_box.Destroy(); + if (nullptr != text && text->GetTightBoundingBox(dim_box)) { text_rect[0].Set(dim_box.m_min.x, dim_box.m_min.y, 0.0); @@ -3962,12 +3964,11 @@ bool ON_DimRadial::GetAnnotationBoundingBox( text_rect[3].Set(dim_box.m_min.x, dim_box.m_max.y, 0.0); for (int i = 0; i < 4; i++) text_rect[i].Transform(text_xform); // Text + gap bounding rect - } - dim_box.Destroy(); - for (int i = 0; i < 4; i++) - { - dim_box.Set(text_rect[i], 0 < i ? true : false); + for (int i = 0; i < 4; i++) + { + dim_box.Set(text_rect[i], 0 < i ? true : false); + } } #define dimlinecount 9 diff --git a/opennurbs_dimensionformat.cpp b/opennurbs_dimensionformat.cpp index 0d8e9a0a..448dbe36 100644 --- a/opennurbs_dimensionformat.cpp +++ b/opennurbs_dimensionformat.cpp @@ -377,35 +377,96 @@ bool ON_NumberFormatter::FormatLength( bool ON_NumberFormatter::FormatAngleStringDMS(double angle_radians, ON_wString& formatted_string) { - bool rc = false; + return ON_NumberFormatter::FormatAngleStringDMS(angle_radians, 2, formatted_string); + //bool rc = false; - int sign = 1; - int degrees = 0; - int minutes = 0; - int seconds = 0; + //int sign = 1; + //int degrees = 0; + //int minutes = 0; + //int seconds = 0; + //formatted_string.Empty(); + + //double angle_degrees = ON_RADIANS_TO_DEGREES * angle_radians; + //if (ON_IsValid(angle_degrees)) + //{ + // double d_seconds; + + // if (angle_degrees < 0.0) + // { + // sign = -1; + // angle_degrees = -angle_degrees; + // } + + // d_seconds = angle_degrees * 3600; + // seconds = (int)(d_seconds + 0.5); + // minutes = seconds / 60; + // seconds = seconds % 60; + // degrees = minutes / 60; + // minutes = minutes % 60; + + // degrees *= sign; + // formatted_string.Format(L"%d%lc %d\' %d\"", degrees, ON_wString::DegreeSymbol, minutes, seconds); + // rc = true; + //} + //return rc; +} + +bool ON_NumberFormatter::FormatAngleStringDMS(double angle_radians, int resolution, ON_wString& formatted_string) +{ + bool rc = false; formatted_string.Empty(); + int idegrees = 0; + int iminutes = 0; + int iseconds = 0; double angle_degrees = ON_RADIANS_TO_DEGREES * angle_radians; - if (ON_IsValid(angle_degrees)) + int sign = 1; + if (angle_degrees < 0.0) { - double d_seconds; + sign = -1; + angle_degrees = -angle_degrees; + } + //double minutes = (angle_degrees - floor(angle_degrees)) * 60.0; + double minutes = (angle_degrees - floor(angle_degrees)); + minutes *= 60.0; + double seconds = (minutes - floor(minutes)); + seconds *= 60.0; - if (angle_degrees < 0.0) + if (resolution < 0) + resolution = 0; + if (resolution > 6) + resolution = 6; + + if (resolution == 0) + { + idegrees = (int)floor(angle_degrees + 0.5); + idegrees *= sign; + rc = formatted_string.Format(L"%d%lc", idegrees, ON_wString::DegreeSymbol); + } + else + { + idegrees = (int)floor(angle_degrees); + if (resolution == 1) { - sign = -1; - angle_degrees = -angle_degrees; + iminutes = (int)floor(minutes + 0.5); + rc = formatted_string.Format(L"%d%lc %d\'", idegrees, ON_wString::DegreeSymbol, iminutes); + } + else + { + iminutes = (int)floor(minutes); + if (resolution == 2) + { + iseconds = (int)floor(seconds + 0.5); + rc = formatted_string.Format(L"%d%lc %d\' %d\"", idegrees, ON_wString::DegreeSymbol, iminutes, iseconds); + } + else + { + iseconds = (int)floor(seconds); + ON_wString fmt; + fmt.Format(L"%%d%%lc %%d\' %%.%dlf\"", resolution - 2); + rc = formatted_string.Format(fmt.Array(), idegrees, ON_wString::DegreeSymbol, iminutes, seconds); + } } - - d_seconds = angle_degrees * 3600; - seconds = (int)(d_seconds + 0.5); - minutes = seconds / 60; - seconds = seconds % 60; - degrees = minutes / 60; - minutes = minutes % 60; - - degrees *= sign; - formatted_string.Format(L"%d%lc %d\' %d\"", degrees, ON_wString::DegreeSymbol, minutes, seconds); - rc = true; } return rc; } diff --git a/opennurbs_dimensionformat.h b/opennurbs_dimensionformat.h index a111f16a..b209305a 100644 --- a/opennurbs_dimensionformat.h +++ b/opennurbs_dimensionformat.h @@ -64,6 +64,11 @@ public: bool bracket_fractions, ON_wString& output); + static bool FormatAngleStringDMS( + double angle_radians, + int resolution, + ON_wString& formatted_string); + static bool FormatAngleStringDMS( double angle_degrees, ON_wString& formatted_string); diff --git a/opennurbs_dimensionstyle.cpp b/opennurbs_dimensionstyle.cpp index d593bdc2..745a9cd3 100644 --- a/opennurbs_dimensionstyle.cpp +++ b/opennurbs_dimensionstyle.cpp @@ -5209,6 +5209,11 @@ bool ON_DimStyle::HasOverrides() const return (m_field_override_parent_count > 0); } +ON__UINT32 ON_DimStyle::OverrideCount() const +{ + return m_field_override_parent_count; +} + void ON_DimStyle::OverrideFields(const ON_DimStyle& source, const ON_DimStyle& parent) { if (ParentId() != parent.Id()) diff --git a/opennurbs_dimensionstyle.h b/opennurbs_dimensionstyle.h index f5dc767e..c242a801 100644 --- a/opennurbs_dimensionstyle.h +++ b/opennurbs_dimensionstyle.h @@ -1547,6 +1547,12 @@ public: */ bool HasOverrides() const; + /* + Returns: + The number of DimStyle fields that are overridden. Name, Id and Index are not counted. + */ + ON__UINT32 OverrideCount() const; + /* Returns: The content hash of the parent dimstyle. If there is no parent dimstyle, then diff --git a/opennurbs_error.cpp b/opennurbs_error.cpp index 4ffcc36e..36e281c8 100644 --- a/opennurbs_error.cpp +++ b/opennurbs_error.cpp @@ -124,6 +124,12 @@ static void ON_IncrementWarningCount() ON_WARNING_COUNT++; } +void ON_BrepIncrementErrorCount() +{ + ON_ERROR_COUNT++; + ON_Brep::ErrorCount++; +} + void ON_SubDIncrementErrorCount() { ON_ERROR_COUNT++; diff --git a/opennurbs_error.h b/opennurbs_error.h index 30ea4af0..0272bba7 100644 --- a/opennurbs_error.h +++ b/opennurbs_error.h @@ -142,7 +142,8 @@ public: Assert = 3, // ON_ASSERT (do not use ON_ASSERT - write code that handles errors and calls ON_ERROR) Custom = 4, SubDError = 5, // call to ON_SubDIncrementErrorCount() - NotValid = 6 // call to ON_IsNotValid() + BrepError = 6, // call to ON_BrepIncrementErrorCount() + NotValid = 7 // call to ON_IsNotValid() }; static const char* TypeToString( diff --git a/opennurbs_evaluate_nurbs.cpp b/opennurbs_evaluate_nurbs.cpp index cafdf4f9..6552570b 100644 --- a/opennurbs_evaluate_nurbs.cpp +++ b/opennurbs_evaluate_nurbs.cpp @@ -727,29 +727,35 @@ bool ON_EvaluateNurbsBasis( // fail to be one by a bit or two when knot // values are large. x = 1.0-ON_SQRT_EPSILON; - if ( N[0] > x ) + if ( N[0] >= x ) { - if ( N[0] != 1.0 && N[0] < 1.0 + ON_SQRT_EPSILON ) + if ( N[0] != 1.0 && N[0] <= 1.0 + ON_SQRT_EPSILON ) { r = 1; - for ( j = 1; j <= d && r; j++ ) + for ( j = 1; j <= d; j++ ) { - if ( N[j] != 0.0 ) + if (N[j] != 0.0) + { r = 0; + break; + } } if (r) N[0] = 1.0; } } - else if ( N[d] > x ) + else if ( N[d] >= x ) { - if ( N[d] != 1.0 && N[d] < 1.0 + ON_SQRT_EPSILON ) + if ( N[d] != 1.0 && N[d] <= 1.0 + ON_SQRT_EPSILON ) { r = 1; - for ( j = 0; j < d && r; j++ ) + for ( j = 0; j < d; j++ ) { if ( N[j] != 0.0 ) + { r = 0; + break; + } } if (r) N[d] = 1.0; @@ -1320,8 +1326,8 @@ bool ON_EvaluateNurbsDeBoor( * argument. In most cases, you can avoid resetting knots * by carefully choosing the value of "side" and "mult_k". * TL_EvDeBoor() - * 0: successful - * -1: knot[order-2] == knot[order-1] + * true: successful + * false: knot[order-2] == knot[order-1] * * COMMENTS: * diff --git a/opennurbs_font.cpp b/opennurbs_font.cpp index 071709a8..b2cf58d1 100644 --- a/opennurbs_font.cpp +++ b/opennurbs_font.cpp @@ -5254,6 +5254,19 @@ void ON_Font::Internal_CopyFrom( && m_font_style == installed_font->FontStyle() ) { + if ( + ON_Font::FontType::ManagedFont == m_font_type + && m_runtime_serial_number > 0 + && 0 == m_managed_face_is_installed) + { + // When 1 == m_runtime_serial_number, this font is ON_Font::Default + // and its face is installed on this device. Otherwise + // this is a managed font being created by some other process. + // + // See RH-58472 for rare cases when this is required. (A V5 file being read at Rhino startup). + m_managed_face_is_installed = 1; + } + // Set stretch from installed font. m_font_stretch = installed_font->FontStretch(); diff --git a/opennurbs_fpoint.h b/opennurbs_fpoint.h index ab9159d6..5e147ab1 100644 --- a/opennurbs_fpoint.h +++ b/opennurbs_fpoint.h @@ -64,6 +64,14 @@ public: const ON_2fPoint& rhs ); + /* + Returns: + (A+B)/2 + Remarks: + Exact when coordinates are equal and prevents overflow. + */ + static const ON_2fPoint Midpoint(const ON_2fPoint& A, const ON_2fPoint& B); + explicit ON_2fPoint(const ON_3fPoint& ); // from 3f point explicit ON_2fPoint(const ON_4fPoint& ); // from 4f point explicit ON_2fPoint(const ON_2fVector& ); // from 2f vector @@ -239,6 +247,14 @@ public: const ON_3fPoint& rhs ); + /* + Returns: + (A+B)/2 + Remarks: + Exact when coordinates are equal and prevents overflow. + */ + static const ON_3fPoint Midpoint(const ON_3fPoint& A, const ON_3fPoint& B); + explicit ON_3fPoint(float x,float y,float z); explicit ON_3fPoint(const ON_2fPoint& ); // from 2f point explicit ON_3fPoint(const ON_4fPoint& ); // from 4f point diff --git a/opennurbs_hatch.cpp b/opennurbs_hatch.cpp index 8b2fcf79..2e5b6235 100644 --- a/opennurbs_hatch.cpp +++ b/opennurbs_hatch.cpp @@ -2341,11 +2341,7 @@ ON_GradientType ON_Hatch::GetGradientType() const void ON_Hatch::SetGradientType(ON_GradientType gt) { - ON_GradientColorData* data = ON_GradientColorData::FromObject(this); - if (nullptr == data && ON_GradientType::None == gt) - return; - - data = ON_GradientColorData::FromObject(this, true); + ON_GradientColorData* data = ON_GradientColorData::FromObject(this, true); if (data) data->m_gradient_type = gt; } diff --git a/opennurbs_internal_V2_annotation.cpp b/opennurbs_internal_V2_annotation.cpp index ec309d78..0f6dcfff 100644 --- a/opennurbs_internal_V2_annotation.cpp +++ b/opennurbs_internal_V2_annotation.cpp @@ -1262,6 +1262,35 @@ bool ON_OBSOLETE_V5_Annotation::Read( ON_BinaryArchive& file ) if (ON_UNSET_INT_INDEX != dim_style_index) this->SetV5_3dmArchiveDimStyleIndex( dim_style_index ); + // July 29, 2020 - Lowell + // If somebody has managed to make a V5 file that has text with single "\n" chars to delineate lines, + // and marks the text as "wrapped" it displays as text with various length lines. + // V5 itself uses '\r\n' to mark hard returns and '\n' to mark lines made by paragraph wrapping + // V6 and later ignore '\n' and convert '\r\n' to newlines and wraps the text to the specified + // wrapping width at runtime. + // The only example of this kind of text formatting I've seen here is from a file referenced by + // https://mcneel.myjetbrains.com/youtrack/issue/RH-59675 + // Uncommenting the following clause will convert all of the '\n' in V5 wrapped text to '\r\n' + // causeing V6 to make line breaks at those places. + // The text will no longer be "wrapped", but will have hard returns at the end of each line + // where it was wrapped in V5. + //////////////////////////////////// + //if (bInChunk && bIsText) + //{ + // const wchar_t* s0 = L"\n"; + // const wchar_t* s1 = L"\r\n"; + // const wchar_t* s2 = L"6C632E28-CF14-49D0-ADEE-DF6ACCAC74F1"; + // if (m_usertext.Find(s0) > -1) + // { + // ON_wString usertext(m_usertext); + // usertext.Replace(s1, s2); + // usertext.Replace(s0, s1); + // usertext.Replace(s2, s1); + // m_usertext = usertext; + // } + //} + //////////////////////////////////// + return rc; } diff --git a/opennurbs_internal_V5_annotation.cpp b/opennurbs_internal_V5_annotation.cpp index fbb20f37..f15af331 100644 --- a/opennurbs_internal_V5_annotation.cpp +++ b/opennurbs_internal_V5_annotation.cpp @@ -919,7 +919,7 @@ ON_Object* ON_BinaryArchive::Internal_ConvertObject( ON_Mesh* mesh = nullptr; if ( Archive3dmVersion() < 60 ) { - mesh = subd->GetControlNetMesh(nullptr); + mesh = subd->GetControlNetMesh(nullptr, ON_SubDGetControlNetMeshPriority::Geometry); } else if ( ON_Internal_UseSubDMeshProxy(*this) ) { diff --git a/opennurbs_knot.cpp b/opennurbs_knot.cpp index 8f56088a..396e494b 100644 --- a/opennurbs_knot.cpp +++ b/opennurbs_knot.cpp @@ -1144,30 +1144,35 @@ bool ON_ClampKnotVector( ) { // sets initial/final order-2 knot values to match knot[order-2]/knot[cv_count-1] - bool rc = false; + // Adjusts initial/final order many CVs so that the curve location is unchanged. + // Requires that knot[order-2]< knot[order-1] and/or knot[cv_count-2] < knot[cv_count-1] + // 17-June-2020 Improved error reporting. + bool rc = false; int i, i0; - if ( knot && order >= 2 && cv_count >= order ) { - if ( end == 0 || end == 2 ) { - if ( cv ) { - ON_EvaluateNurbsDeBoor(cv_dim,order,cv_stride,cv,knot,1,0.0,knot[order-2]); + if (cv && knot && order >= 2 && cv_count >= order && end>=0 && end<=2 ) { + rc = true; + if ( end == 0 || end == 2 ) { + if (ON_EvaluateNurbsDeBoor(cv_dim, order, cv_stride, cv, knot, 1, 0.0, knot[order - 2])) + { + for (i = 0; i < order - 2; i++) + knot[i] = knot[order - 2]; } - i0 = order-2; - for (i = 0; i < i0; i++) - knot[i] = knot[i0]; - rc = true; + else + rc = false; } if ( end == 1 || end == 2 ) { i0 = cv_count-order; knot += i0; - if ( cv ) { - cv += i0*cv_stride; - ON_EvaluateNurbsDeBoor(cv_dim,order,cv_stride,cv,knot,-1,0.0,knot[order-1]); + cv += i0*cv_stride; + if (ON_EvaluateNurbsDeBoor(cv_dim, order, cv_stride, cv, knot, -1, 0.0, knot[order - 1])) + { + i0 = order - 1; + for (i = 2 * order - 3; i > i0; i--) + knot[i] = knot[i0]; } - i0 = order-1; - for (i = 2*order-3; i > i0; i--) - knot[i] = knot[i0]; - rc = true; + else + rc = false; } } return rc; diff --git a/opennurbs_leader.cpp b/opennurbs_leader.cpp index 3d3074d3..9e855989 100644 --- a/opennurbs_leader.cpp +++ b/opennurbs_leader.cpp @@ -153,10 +153,11 @@ bool ON_Leader::GetBBox( // returns true if successful bool grow // true means grow box ) const { - return false; - // This doesn't seem to be useful and gives the wrong answer most of the time - // when the dimstyle is wrong - //return GetBBox(&ON_DimStyle::Default, 1.0, ON_3dVector::XAxis, ON_3dVector::YAxis, bbox_min, bbox_max, grow); + // Even though this doesn't apply view info properly, it has a side-effect of creating runs + // so that text query functions will work properly. + // I'm adding it back here to make Opennurbs ONx model support for reading and not displaying + // files work better wrt text queries + return GetAnnotationBoundingBox(nullptr, nullptr, 1.0, bbox_min, bbox_max, grow ? true : false); } bool ON_Leader::GetTextXform( @@ -187,6 +188,7 @@ bool ON_Leader::GetTextXform( ON_2dVector tail_dir = TailDirection(dimstyle); ON_3dVector view_x = nullptr == vp ? ON_3dVector::XAxis : vp->CameraX(); ON_3dVector view_y = nullptr == vp ? ON_3dVector::YAxis : vp->CameraY(); + ON_3dVector view_z = ON_CrossProduct(view_x, view_y); ON_Plane objectplane = Plane(); ON::TextHorizontalAlignment halign = dimstyle->LeaderTextHorizontalAlignment(); ON::TextVerticalAlignment valign = dimstyle->LeaderTextVerticalAlignment(); @@ -207,7 +209,7 @@ bool ON_Leader::GetTextXform( { const_cast(text)->SetAlignment(halign, valign); } - if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash() ) + if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash()) { ON_wString rtfstr = text->RtfText(); const_cast(text)->Create( @@ -222,158 +224,167 @@ bool ON_Leader::GetTextXform( double textblock_height = 0.0; double line_height = 1.0; + ON_2dPoint text_corners[4]; + ON_2dPoint text_center; + ON_2dPoint text_pt(0.0, 0.0); + if (text->Get2dCorners(text_corners)) // Gets unscaled 3d size { - ON_2dPoint text_corners[4]; - ON_2dPoint text_center; - ON_2dPoint text_pt(0.0, 0.0); - if (text->Get2dCorners(text_corners)) // Gets unscaled 3d size + text_center = (text_corners[0] + text_corners[2]) / 2.0; + textblock_width = (text_corners[1].x - text_corners[0].x); + textblock_height = (text_corners[3].y - text_corners[0].y); + line_height = dimstyle->TextHeight(); + + ON_2dVector text_shift; + text_shift.x = 0.0; + text_shift.y = 0.0; + + // LeaderAttachStyle - Vertical alignment of text with leader text point + ON::TextVerticalAlignment attach = dimstyle->LeaderTextVerticalAlignment(); + switch (attach) { - text_center = (text_corners[0] + text_corners[2]) / 2.0; - textblock_width = (text_corners[1].x - text_corners[0].x); - textblock_height = (text_corners[3].y - text_corners[0].y); - line_height = dimstyle->TextHeight(); - - ON_2dVector text_shift; - text_shift.x = 0.0; + case ON::TextVerticalAlignment::Top: + text_shift.y = -textblock_height / 2.0; + break; + case ON::TextVerticalAlignment::MiddleOfTop: + text_shift.y = -(textblock_height / 2.0) + (line_height / 2.0); + break; + case ON::TextVerticalAlignment::BottomOfTop: + text_shift.y = -(textblock_height / 2.0) + line_height; + break; + case ON::TextVerticalAlignment::Middle: text_shift.y = 0.0; + break; + case ON::TextVerticalAlignment::MiddleOfBottom: + text_shift.y = (textblock_height / 2.0) - (line_height / 2.0); + break; + case ON::TextVerticalAlignment::Bottom: + text_shift.y = textblock_height / 2.0; + break; + case ON::TextVerticalAlignment::BottomOfBoundingBox: + text_shift.y = (textblock_height / 2.0) + (dimstyle->TextGap()); /*(line_height / 10.0);*/ + break; + } - // LeaderAttachStyle - Vertical alignment of text with leader text point - ON::TextVerticalAlignment attach = dimstyle->LeaderTextVerticalAlignment(); - switch (attach) - { - case ON::TextVerticalAlignment::Top: - text_shift.y = -textblock_height / 2.0; - break; - case ON::TextVerticalAlignment::MiddleOfTop: - text_shift.y = -(textblock_height / 2.0) + (line_height / 2.0); - break; - case ON::TextVerticalAlignment::BottomOfTop: - text_shift.y = -(textblock_height / 2.0) + line_height; - break; - case ON::TextVerticalAlignment::Middle: - text_shift.y = 0.0; - break; - case ON::TextVerticalAlignment::MiddleOfBottom: - text_shift.y = (textblock_height / 2.0) - (line_height / 2.0); - break; - case ON::TextVerticalAlignment::Bottom: - text_shift.y = textblock_height / 2.0; - break; - case ON::TextVerticalAlignment::BottomOfBoundingBox: - text_shift.y = (textblock_height / 2.0) + (dimstyle->TextGap()); /*(line_height / 10.0);*/ - break; - } + // 2d Point at center of text but without vertical alignment shift + ON_2dPoint text_pt2(0.0, 0.0); + if (0 < m_points.Count()) + text_pt2 = m_points[m_points.Count() - 1]; - // 2d Point at center of text but without vertical alignment shift - ON_2dPoint text_pt2(0.0, 0.0); - if (0 < m_points.Count()) - text_pt2 = m_points[m_points.Count() - 1]; + double landing_length = 0.0; + if (dimstyle->LeaderHasLanding()) + landing_length = dimstyle->LeaderLandingLength(); + double text_gap = dimstyle->TextGap(); + double x_offset = dimscale * (landing_length + text_gap + textblock_width / 2.0); - double landing_length = 0.0; - if(dimstyle->LeaderHasLanding()) - landing_length = dimstyle->LeaderLandingLength(); - double text_gap = dimstyle->TextGap(); - double x_offset = dimscale * (landing_length + text_gap + textblock_width / 2.0); - - text_pt2 = text_pt2 + (tail_dir * x_offset); + text_pt2 = text_pt2 + (tail_dir * x_offset); - // Move from Origin to leader plane - const ON_Plane& leaderplane = Plane(); + // Move from Origin to leader plane + const ON_Plane& leaderplane = Plane(); - ON_2dVector text_dir(1.0, 0.0); // Horizontal to cplane - ON_DimStyle::ContentAngleStyle::Horizontal - ON_DimStyle::ContentAngleStyle textangle_style = dimstyle->LeaderContentAngleStyle(); + ON_2dVector text_dir(1.0, 0.0); // Horizontal to cplane - ON_DimStyle::ContentAngleStyle::Horizontal + ON_DimStyle::ContentAngleStyle textangle_style = dimstyle->LeaderContentAngleStyle(); - if (ON_DimStyle::ContentAngleStyle::Aligned == textangle_style) - { - text_dir = tail_dir; - if (text_dir.x < 0.0) - text_dir = -text_dir; - } - else if (ON_DimStyle::ContentAngleStyle::Rotated == textangle_style) - { - text_dir = tail_dir; // Already has rotation included - } - if (!text_dir.Unitize()) - text_dir.Set(1.0, 0.0); + if (ON_DimStyle::ContentAngleStyle::Aligned == textangle_style) + { + text_dir = tail_dir; + if (text_dir.x < 0.0) + text_dir = -text_dir; + } + else if (ON_DimStyle::ContentAngleStyle::Rotated == textangle_style) + { + text_dir = tail_dir; // Already has rotation included + } + if (!text_dir.Unitize()) + text_dir.Set(1.0, 0.0); - ON_Xform textscale_xf(ON_Xform::DiagonalTransformation(dimscale)); - ON_Xform textcenter_xf(ON_Xform::IdentityTransformation); // Centers text block at origin - ON_Xform textrotation_xf(ON_Xform::IdentityTransformation); // Text rotation around origin + ON_Xform textscale_xf(ON_Xform::DiagonalTransformation(dimscale)); + ON_Xform textcenter_xf(ON_Xform::IdentityTransformation); // Centers text block at origin + ON_Xform textrotation_xf(ON_Xform::IdentityTransformation); // Text rotation around origin - ON_Xform world_to_leader_xf(ON_Xform::IdentityTransformation); // WCS plane to leader plane rotation - world_to_leader_xf.Rotation(ON_Plane::World_xy, leaderplane); // Rotate text from starting text plane (wcs) to leader plane + ON_Xform world_to_leader_xf(ON_Xform::IdentityTransformation); // WCS plane to leader plane rotation + world_to_leader_xf.Rotation(ON_Plane::World_xy, leaderplane); // Rotate text from starting text plane (wcs) to leader plane - ON_Xform leader_to_text_pt_xf(ON_Xform::IdentityTransformation); // Leader plane to text point translation + ON_Xform leader_to_text_pt_xf(ON_Xform::IdentityTransformation); // Leader plane to text point translation + leader_to_text_pt_xf = ON_Xform::TranslationTransformation(text_pt2); - textrotation_xf.Rotation(text_dir.y, text_dir.x, ON_3dVector::ZAxis, ON_3dPoint::Origin); + textrotation_xf.Rotation(text_dir.y, text_dir.x, ON_3dVector::ZAxis, ON_3dPoint::Origin); + textcenter_xf.m_xform[0][3] = -text_center.x; + textcenter_xf.m_xform[1][3] = -text_center.y + text_shift.y; - textcenter_xf.m_xform[0][3] = -text_center.x; - textcenter_xf.m_xform[1][3] = -text_center.y + text_shift.y; - - if(ON::TextOrientation::InView == dimstyle->LeaderTextOrientation()) - { - const ON_Plane& ldrplane = Plane(); - ON_3dVector view_z = ON_CrossProduct(view_x, view_y); - ON_3dPoint text_point_3d = Plane().PointAt(text_pt2.x, text_pt2.y); - textrotation_xf.Rotation(text_point_3d, ldrplane.xaxis, ldrplane.yaxis, ldrplane.zaxis, text_point_3d, view_x, view_y, view_z); - leader_to_text_pt_xf = ON_Xform::TranslationTransformation(text_pt2); - text_xform_out = textscale_xf * textcenter_xf; - text_xform_out = leader_to_text_pt_xf * text_xform_out; - text_xform_out = world_to_leader_xf * text_xform_out; - text_xform_out = textrotation_xf * text_xform_out; - return true; - } - else - if (dimstyle->DrawForward()) - { - ON_Xform dxf(ON_Xform::IdentityTransformation); - dxf = textrotation_xf * dxf; - dxf = world_to_leader_xf * dxf; - ON_3dVector x(ON_3dVector::XAxis); - ON_3dVector y(ON_3dVector::YAxis); - //ON_3dVector x(leaderplane.xaxis); - //ON_3dVector y(leaderplane.yaxis); - x.Transform(dxf); - y.Transform(dxf); - if (nullptr != model_xform) - { - x.Transform(*model_xform); - y.Transform(*model_xform); - } - double xovx = view_x * x; - double yovy = view_y * y; - bool fx = (0.00015 >= xovx); - bool fy = (0.00015 >= yovy); - if (fx || fy) - { - double xovy = x * view_y; - if (0.99985 > xovy) - { - ON_Xform mxf(ON_Xform::IdentityTransformation); - if (fx) - { - mxf.Mirror(ON_3dPoint(text_center), ON_3dVector::XAxis); - textcenter_xf = textcenter_xf * mxf; - } - if (fy) - { - mxf.Mirror(ON_3dPoint(text_center), ON_3dVector::YAxis); - textcenter_xf.m_xform[1][3] = -text_center.y - text_shift.y; - textcenter_xf = textcenter_xf * mxf; - } - } - } - } - - leader_to_text_pt_xf = ON_Xform::TranslationTransformation(text_pt2); - + if (ON::TextOrientation::InView == dimstyle->LeaderTextOrientation()) + { + const ON_Plane& ldrplane = Plane(); + ON_3dPoint text_point_3d = Plane().PointAt(text_pt2.x, text_pt2.y); + textrotation_xf.Rotation(text_point_3d, ldrplane.xaxis, ldrplane.yaxis, ldrplane.zaxis, text_point_3d, view_x, view_y, view_z); text_xform_out = textscale_xf * textcenter_xf; - text_xform_out = textrotation_xf * text_xform_out; text_xform_out = leader_to_text_pt_xf * text_xform_out; text_xform_out = world_to_leader_xf * text_xform_out; + text_xform_out = textrotation_xf * text_xform_out; + return true; } + else if (dimstyle->DrawForward()) + { + if (dimstyle->DrawForward()) + { + // Check if the text is right-reading by comparing + // text plane x and y, rotated by text rotation angle, + // to view right and up + + ON_3dVector text_xdir = leaderplane.xaxis; + ON_3dVector text_ydir = leaderplane.yaxis; + ON_3dVector text_zdir = leaderplane.zaxis; + if (text_zdir * view_z < 0.0) + { + ON_Xform xfr = textrotation_xf.Inverse(); + text_xdir.Transform(xfr); + text_ydir.Transform(xfr); + text_zdir.Transform(xfr); + } + else + { + text_xdir.Transform(textrotation_xf); + text_ydir.Transform(textrotation_xf); + text_zdir.Transform(textrotation_xf); + } + + if (nullptr != model_xform) + { + text_xdir.Transform(*model_xform); + text_ydir.Transform(*model_xform); + text_zdir.Transform(*model_xform); + } + + bool flip_x = false; + bool flip_y = false; + const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : cos(88.0 * ON_DEGREES_TO_RADIANS); + CalcTextFlip( + text_xdir, text_ydir, text_zdir, + view_x, view_y, view_z, + model_xform, + fliptol, + flip_x, + flip_y); + + ON_Xform mxf; + if (flip_x) + { + mxf.Mirror(ON_3dPoint::Origin, ON_3dVector::XAxis); + textscale_xf = textscale_xf * mxf; + } + if (flip_y) + { + mxf.Mirror(ON_3dPoint::Origin, ON_3dVector::YAxis); + textscale_xf = textscale_xf * mxf; + } + } + } + + text_xform_out = textscale_xf * textcenter_xf; + text_xform_out = textrotation_xf * text_xform_out; + text_xform_out = leader_to_text_pt_xf * text_xform_out; + text_xform_out = world_to_leader_xf * text_xform_out; } return true; } @@ -601,14 +612,14 @@ bool ON_Leader::Create( if (nullptr != leader_text) { text = new ON_TextContent; - if (!text->Create(leader_text, Type(), dimstyle, bWrapped, rect_width, 0.0)) + if (text->Create(leader_text, Type(), dimstyle, bWrapped, rect_width, 0.0)) + SetText(text); + else { delete text; text = 0; - return false; } } - SetText(text); return true; } diff --git a/opennurbs_material.cpp b/opennurbs_material.cpp index 3b729769..a4031680 100644 --- a/opennurbs_material.cpp +++ b/opennurbs_material.cpp @@ -1408,71 +1408,68 @@ int ON_Material::CompareColorAttributes( const ON_Material& a, const ON_Material { const auto a_pbr = a.PhysicallyBased(); const auto b_pbr = b.PhysicallyBased(); - if (a_pbr.Supported() && !b_pbr.Supported()) + if (a_pbr && !b_pbr) return -1; - if (!a_pbr.Supported() && b_pbr.Supported()) + if (!a_pbr && b_pbr) return 1; - if (a_pbr.Supported() && b_pbr.Supported()) + if (a_pbr && b_pbr) { - int rc = a_pbr.BaseColor().Compare(a_pbr.BaseColor()); + int rc = a_pbr->BaseColor().Compare(a_pbr->BaseColor()); if (rc) return rc; - rc = ((int)a_pbr.BRDF()) - ((int)b_pbr.BRDF()); + rc = ((int)a_pbr->BRDF()) - ((int)b_pbr->BRDF()); if (rc) return rc; - rc = CompareDouble(a_pbr.Subsurface(), b_pbr.Subsurface()); + rc = CompareDouble(a_pbr->Subsurface(), b_pbr->Subsurface()); if (0 != rc) return rc; - rc = a_pbr.SubsurfaceScatteringColor().Compare(b_pbr.SubsurfaceScatteringColor()); + rc = a_pbr->SubsurfaceScatteringColor().Compare(b_pbr->SubsurfaceScatteringColor()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.SubsurfaceScatteringRadius(), b_pbr.SubsurfaceScatteringRadius()); + rc = CompareDouble(a_pbr->SubsurfaceScatteringRadius(), b_pbr->SubsurfaceScatteringRadius()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.Metallic(), b_pbr.Metallic()); + rc = CompareDouble(a_pbr->Metallic(), b_pbr->Metallic()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.Specular(), b_pbr.Specular()); + rc = CompareDouble(a_pbr->Specular(), b_pbr->Specular()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.SpecularTint(), b_pbr.SpecularTint()); + rc = CompareDouble(a_pbr->SpecularTint(), b_pbr->SpecularTint()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.Roughness(), b_pbr.Roughness()); + rc = CompareDouble(a_pbr->Roughness(), b_pbr->Roughness()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.Sheen(), b_pbr.Sheen()); + rc = CompareDouble(a_pbr->Sheen(), b_pbr->Sheen()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.SheenTint(), b_pbr.SheenTint()); + rc = CompareDouble(a_pbr->SheenTint(), b_pbr->SheenTint()); if (0 != rc) return rc; - //rc = CompareDouble(a_pbr.ReflectiveIOR(), b_pbr.ReflectiveIOR()); - //if (0 != rc) return rc; - - rc = CompareDouble(a_pbr.Anisotropic(), b_pbr.Anisotropic()); + rc = CompareDouble(a_pbr->Anisotropic(), b_pbr->Anisotropic()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.AnisotropicRotation(), b_pbr.AnisotropicRotation()); + rc = CompareDouble(a_pbr->AnisotropicRotation(), b_pbr->AnisotropicRotation()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.Clearcoat(), b_pbr.Clearcoat()); + rc = CompareDouble(a_pbr->Clearcoat(), b_pbr->Clearcoat()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.ClearcoatRoughness(), b_pbr.ClearcoatRoughness()); + rc = CompareDouble(a_pbr->ClearcoatRoughness(), b_pbr->ClearcoatRoughness()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.Opacity(), b_pbr.Opacity()); + rc = CompareDouble(a_pbr->Opacity(), b_pbr->Opacity()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.OpacityIOR(), b_pbr.OpacityIOR()); + rc = CompareDouble(a_pbr->OpacityIOR(), b_pbr->OpacityIOR()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.OpacityRoughness(), b_pbr.OpacityRoughness()); + rc = CompareDouble(a_pbr->OpacityRoughness(), b_pbr->OpacityRoughness()); if (0 != rc) return rc; - rc = a_pbr.Emission().Compare(b_pbr.Emission()); + rc = a_pbr->Emission().Compare(b_pbr->Emission()); return rc; } @@ -1507,38 +1504,35 @@ int ON_Material::CompareReflectionAttributes(const ON_Material& a, const ON_Mate { const auto a_pbr = a.PhysicallyBased(); const auto b_pbr = b.PhysicallyBased(); - if (a_pbr.Supported() && !b_pbr.Supported()) + if (a_pbr && !b_pbr) return -1; - if (!a_pbr.Supported() && b_pbr.Supported()) + if (!a_pbr && b_pbr) return 1; - if (a_pbr.Supported() && b_pbr.Supported()) + if (a_pbr && b_pbr) { - int rc = CompareDouble(a_pbr.Metallic(), b_pbr.Metallic()); + int rc = CompareDouble(a_pbr->Metallic(), b_pbr->Metallic()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.Specular(), b_pbr.Specular()); + rc = CompareDouble(a_pbr->Specular(), b_pbr->Specular()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.SpecularTint(), b_pbr.SpecularTint()); + rc = CompareDouble(a_pbr->SpecularTint(), b_pbr->SpecularTint()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.Roughness(), b_pbr.Roughness()); + rc = CompareDouble(a_pbr->Roughness(), b_pbr->Roughness()); if (0 != rc) return rc; - //rc = CompareDouble(a_pbr.ReflectiveIOR(), b_pbr.ReflectiveIOR()); - //if (0 != rc) return rc; - - rc = CompareDouble(a_pbr.Anisotropic(), b_pbr.Anisotropic()); + rc = CompareDouble(a_pbr->Anisotropic(), b_pbr->Anisotropic()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.AnisotropicRotation(), b_pbr.AnisotropicRotation()); + rc = CompareDouble(a_pbr->AnisotropicRotation(), b_pbr->AnisotropicRotation()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.Clearcoat(), b_pbr.Clearcoat()); + rc = CompareDouble(a_pbr->Clearcoat(), b_pbr->Clearcoat()); if (0 != rc) return rc; - rc = CompareDouble(a_pbr.ClearcoatRoughness(), b_pbr.ClearcoatRoughness()); + rc = CompareDouble(a_pbr->ClearcoatRoughness(), b_pbr->ClearcoatRoughness()); return rc; } @@ -2049,7 +2043,7 @@ ON_Color ON_Material::PreviewColor(void) const bool ON_Material::UseDiffuseTextureAlphaForObjectTransparencyTexture() const { //Physically based materials do not support alpha transparency (at the moment). - if (PhysicallyBased().Supported()) + if (IsPhysicallyBased()) return false; return m_bUseDiffuseTextureAlphaForObjectTransparencyTexture; @@ -2546,6 +2540,7 @@ ON_TextureMapping::TYPE ON_TextureMapping::TypeFromUnsigned( ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::srf_mapping_primitive); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::brep_mapping_primitive); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::ocs_mapping); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::false_colors); } ON_ERROR("Invalid type_as_unsigned value."); @@ -2566,6 +2561,7 @@ const ON_wString ON_TextureMapping::TypeToString(ON_TextureMapping::TYPE texture ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::srf_mapping_primitive); ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::brep_mapping_primitive); ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::ocs_mapping); + ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::false_colors); } ON_ERROR("Invalid texture_mapping_type value."); @@ -5316,11 +5312,20 @@ static inline bool HasSharedVertices(const ON_Mesh& mesh) return mesh.m_V.Count() < ((mesh.TriangleCount() * 3) + (mesh.QuadCount() * 4)); } - const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates( + const class ON_TextureMapping& mapping, + const class ON_Xform* mesh_xform, + bool bLazy +) +{ + return SetCachedTextureCoordinatesEx(mapping, mesh_xform, bLazy, true); +} + +const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinatesEx( const class ON_TextureMapping& mapping, - const class ON_Xform* mesh_xform, - bool bLazy + const class ON_Xform* mesh_xform, + bool bLazy, + bool bSeamCheck ) { if ( mapping.RequiresVertexNormals() && !HasVertexNormals() ) @@ -5330,9 +5335,13 @@ const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates( double two_pi_tc = 1.0; ON_SimpleArray Tside; ON_SimpleArray* Tsd = 0; - bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this); - if ( bSeamCheck ) - mp.m_uvw = ON_Xform::IdentityTransformation; + + if (bSeamCheck) + { + bSeamCheck = SeamCheckHelper(mp, two_pi_tc, Tside, Tsd) && HasSharedVertices(*this); + if (bSeamCheck) + mp.m_uvw = ON_Xform::IdentityTransformation; + } ON_TextureCoordinates* TC = 0; { @@ -5402,9 +5411,19 @@ const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates( } bool ON_Mesh::SetTextureCoordinates( + const class ON_TextureMapping& mapping, + const class ON_Xform* mesh_xform, + bool bLazy +) +{ + return SetTextureCoordinatesEx(mapping, mesh_xform, bLazy, true); +} + +bool ON_Mesh::SetTextureCoordinatesEx( const class ON_TextureMapping& mapping, const class ON_Xform* mesh_xform, - bool bLazy + bool bLazy, + bool bSeamCheck ) { if ( mapping.RequiresVertexNormals() && !HasVertexNormals() ) @@ -5418,9 +5437,12 @@ bool ON_Mesh::SetTextureCoordinates( double two_pi_tc = 1.0; - bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this); - if ( bSeamCheck ) - mp.m_uvw = ON_Xform::IdentityTransformation; + if (bSeamCheck) + { + bSeamCheck = SeamCheckHelper(mp, two_pi_tc, Tside, Tsd) && HasSharedVertices(*this); + if (bSeamCheck) + mp.m_uvw = ON_Xform::IdentityTransformation; + } // Use mp instead of mapping to call GetTextureCoordinates() // because m_uvw must be the identity if we have seams. @@ -7104,35 +7126,54 @@ ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_PhysicallyBasedM new (&_impl) Impl(*src.Implementation().material); } -ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void) +bool ON_Material::IsPhysicallyBased(void) const { - return ON_PhysicallyBasedMaterial(*this); + return nullptr != PhysicallyBased(); } -const ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void) const +std::shared_ptr ON_Material::PhysicallyBased(void) { - return ON_PhysicallyBasedMaterial(*this); + auto ptr = std::make_shared(*this); + + if (nullptr != ptr && ON_PhysicallyBasedMaterial_Supported(*ptr)) + { + return ptr; + } + + return nullptr; } -ON_Material ON_Material::ConvertToPhysicallyBased(void) const +const std::shared_ptr ON_Material::PhysicallyBased(void) const { - if (PhysicallyBased().Supported()) - return *this; + auto ptr = std::make_shared(*this); - //Copy all of the textures and the old-school parameters first. - ON_Material material(*this); - auto pbr = material.PhysicallyBased(); + if (nullptr != ptr && ON_PhysicallyBasedMaterial_Supported(*ptr)) + { + return ptr; + } + + return nullptr; +} + +void ON_Material::ToPhysicallyBased(void) +{ + if (IsPhysicallyBased()) + return; + + //This should always be valid. + auto pbr = std::make_shared(*this); + ON_ASSERT(pbr != nullptr); const bool bMetal = m_transparency < 0.01 && !m_bFresnelReflections && m_reflectivity > 0.99; const bool bGlass = m_transparency > 0.99; - pbr.SetBaseColor(bMetal ? m_reflection : bGlass ? m_transparent : m_diffuse); - pbr.SetMetallic(bMetal ? 1.0 : 0.0); - pbr.SetRoughness(bMetal ? m_reflection_glossiness : 1.0 - m_reflectivity); - pbr.SetOpacity(1.0 - m_transparency); - pbr.SetOpacityIOR(m_index_of_refraction); + pbr->SetBaseColor(bMetal ? m_reflection : bGlass ? m_transparent : m_diffuse); + pbr->SetMetallic(bMetal ? 1.0 : 0.0); + pbr->SetRoughness(bMetal ? m_reflection_glossiness : 1.0 - m_reflectivity); + pbr->SetOpacity(1.0 - m_transparency); + pbr->SetOpacityIOR(m_index_of_refraction); - return material; + ON_ASSERT(IsPhysicallyBased()); } @@ -7147,15 +7188,15 @@ ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(voi } -bool ON_PhysicallyBasedMaterial::Supported(void) const +bool ON_PhysicallyBasedMaterial_Supported(const ON_PhysicallyBasedMaterial& material) { - if (!Implementation().UserDataExists()) + if (!material.Implementation().UserDataExists()) return false; - return BaseColor().IsValid(); + return material.BaseColor().IsValid(); } -void ON_PhysicallyBasedMaterial::Destroy(void) +void ON_PhysicallyBasedMaterial::ToLegacy(void) { ON_Material& mat = *Implementation().material; Implementation().~Impl(); diff --git a/opennurbs_material.h b/opennurbs_material.h index cc57f1c4..53c49412 100644 --- a/opennurbs_material.h +++ b/opennurbs_material.h @@ -273,17 +273,22 @@ public: //the function that the layer manager uses to color the little material swatch, for example. ON_Color PreviewColor() const; + //Call this function to determine if the material should be treated as Physically Based (ie - a PBR material) + //If this function returns true, the call to PhysicallyBased will return a non-null pointer. + //If the function returns false, use the legacy interface (Diffuse etc). Conversion of a non-PBR material to PBR + //is possible by calling ConvertToPhysicallyBased. + bool IsPhysicallyBased(void) const; //Physically based material interface. Use this interface to set and get PBR parameters //and to check if this material supports PBR. - //NOTE WELL - ON_PhysicallyBasedMaterial contains a pointer to this - the scope of the ON_PhysicallyBasedMaterial - //object must not exceed the scope of the material that it originally came from. Ideal usage is material.PhysicallyBased().Function() - const ON_PhysicallyBasedMaterial PhysicallyBased(void) const; - ON_PhysicallyBasedMaterial PhysicallyBased(void); + //Note - it is very important that the lifetime of the returned pointer is the same as the ON_Material + //it was called on. Once the material is deleted, this pointer is no longer valid. + const std::shared_ptr PhysicallyBased(void) const; + std::shared_ptr PhysicallyBased(void); - //Returns a material that is the best approximation of the original, but as a physically based material. - //the returned material is guaranteed to return true to material.PhysicallyBased().IsSupported() - ON_Material ConvertToPhysicallyBased(void) const; + //Convert a legacy material to a PBR material that is the best approximation of the original. + //After calling this function, the material is guaranteed to return true to material.IsPhysicallyBased() + void ToPhysicallyBased(void); //Internal use only static ON_UUID PhysicallyBasedUserdataId(void); @@ -622,11 +627,6 @@ public: ///////////////////////////////////////////////////////////////// // Interface public: - //Call this function to determine if the material supports a PBR definition. - //A material will support PBR if the base color is set. All other values are set - //to defaults that will produce a simple plaster-like surface. - virtual bool Supported(void) const; - //Reflectance model to use. Default is GGX. Renderers do not need to support a specific //model, but certain material definitions may specify in the hope that a renderer will support. //GGX support is built into Rhino (Cycles, display) @@ -736,7 +736,7 @@ public: virtual void SynchronizeLegacyMaterial(void); //Expert function to remove all PBR data from a material - virtual void Destroy(void); + virtual void ToLegacy(void); public: class ON_CLASS ParametersNames @@ -775,6 +775,7 @@ private: ON_PhysicallyBasedMaterial& operator=(const ON_Material& src) = delete; ON_PhysicallyBasedMaterial& operator=(const ON_PhysicallyBasedMaterial& src) = delete; friend ON_Material; + friend bool ON_PhysicallyBasedMaterial_Supported(const ON_PhysicallyBasedMaterial& material); }; diff --git a/opennurbs_math.cpp b/opennurbs_math.cpp index d53b31e2..4219e4e2 100644 --- a/opennurbs_math.cpp +++ b/opennurbs_math.cpp @@ -1971,6 +1971,113 @@ ON_SolveQuadraticEquation( return 0; } +/* Find solutions of an at most cubic equation + * Solve the cubic equation a*X^3 + b*X^2 + c*X + d = 0. +*REFERENCE: +*Numerical Recipes in C, section 5.6 +*/ +int ON_SolveCubicEquation( + double a, double b, double c, double d, + double* r1, double* r2, double* r3 +) +{ + int rc = 0; + if (a == 0.0) + { + if (b == 0.0) + { + if (c == 0.0) + { + // no roots + rc = -1; + } + else + { + // linear equation + *r1 = -d / c; + rc = 1; + } + } + else + { + // quadratic equation + double rr0, rr1; + int qrc = ON_SolveQuadraticEquation(b, c, d, &rr0, &rr1); + switch (qrc) + { + case 0: + // two distinct real roots (rr0 < rr1) + *r1 = rr0; + *r2 = rr1; + rc = 2; + break; + case 1: + // one real root (rr0 = rr1) + *r1 = rr0; + *r2 = rr1; + rc = 2; + break; + case 2: + // 2: two complex conjugate roots (rr0 +/- (rr1)*sqrt(-1)) + *r1 = rr0; + *r2 = rr1; + rc = 0; + break; + } + } + } + else + { + if (a != 1.0) + { + // convert to normal form equation + b = b / a; + c = c / a; + d = d / a; + a = 1.0; + } + + double Q = (b*b - 3.0 * c) / 9.0; + double R = (2.0 * b*b*b - 9.0* b* c + 27.0 * d) / 54.0; + + if (R*R < Q*Q*Q) + { + // three real roots + double Theta = acos(R / sqrt(Q*Q*Q)); + *r1 = -2.0 * sqrt(Q) * cos(Theta / 3.0) - b / 3.0; + *r2 = -2.0 * sqrt(Q) * cos((Theta + 2.0*ON_PI) / 3.0) - b / 3.0; + *r3 = -2.0 * sqrt(Q) * cos((Theta - 2.0*ON_PI) / 3.0) - b / 3.0; + // inline bubble sort + if (*r1 > *r2) { double temp = *r1; *r1 = *r2; *r2 = temp; } + if (*r2 > *r3) { double temp = *r2; *r2 = *r3; *r3 = temp; } + if (*r1 > *r2) { double temp = *r1; *r1 = *r2; *r2 = temp; } + rc = 3; + } + else + { + double A = pow(fabs(R) + sqrt(R*R - Q * Q*Q), 1.0 / 3.0); + if (R > 0) + A = -A; + double B = 0; + if (A != 0.0) + B = Q / A; + *r1 = (A + B) - b / 3; + // the complex congate pair of roots are r2 +/- r3 i + *r2 = -(A + B) / 2.0 - b / 3; + *r3 = sqrt(3.0 / 2.0)*(A - B); + rc = 1; + } + } + return rc; +} + + + + + + + + int ON_SolveTriDiagonal( int dim, int n, double* a, const double* b, double* c, diff --git a/opennurbs_math.h b/opennurbs_math.h index f34dd232..9aa94b22 100644 --- a/opennurbs_math.h +++ b/opennurbs_math.h @@ -446,8 +446,8 @@ private: #define ON_IS_FINITE_FLOAT(x) ((x) <= 3.402823466e+38F && (x) >= -3.402823466e+38F) #define ON_IS_INFINITE_FLOAT(x) ((x) > 3.402823466e+38F || (x) < -3.402823466e+38F) -#define ON_IS_VALID(x) ((x) != ON_UNSET_VALUE && (x) != ON_UNSET_POSITIVE_VALUE && ON_IS_FINITE(x)) -#define ON_IS_VALID_FLOAT(x) ((x) != ON_UNSET_FLOAT && (x) != ON_UNSET_POSITIVE_FLOAT && ON_IS_FINITE_FLOAT(x)) +#define ON_IS_VALID(x) ((x) > ON_UNSET_VALUE && (x) < ON_UNSET_POSITIVE_VALUE) +#define ON_IS_VALID_FLOAT(x) ((x) > ON_UNSET_FLOAT && (x) < ON_UNSET_POSITIVE_FLOAT) #define ON_IS_UNSET_DOUBLE(x) (ON_UNSET_VALUE == (x) || ON_UNSET_POSITIVE_VALUE == (x)) #define ON_IS_UNSET_FLOAT(x) (ON_UNSET_FLOAT == (x) || ON_UNSET_POSITIVE_FLOAT == (x)) #define ON_IS_NAN(x) (!((x)==(x))) @@ -982,6 +982,27 @@ int ON_SolveQuadraticEquation( // solve a*X^2 + b*X + c = 0 double*, double* // roots r0 and r1 returned here ); +/* +Description: + Solve the cubic equation a*X^3 + b*X^2 + c*X + d = 0. +Inputs: + a,b,c,d, polynomial coeficients ( if a==b==c== 0) then failure is returned +Returns: + number of real roots stored with multiplicity. + specifically + -1: failure (a == b == c== 0.0 case) + 0: no real roots ( a==0 and b!=0) two complex conjugate roots (r1 +/- (r2)*sqrt(-1)) + 1: one real root (r1). Either ( a==b==0.0) or else( a!=0) and two complex conjugate + roots (r2 +/- (r3)*sqrt(-1)) + 2: two real roots (a==0.0, b!=0.0) *r1 <= *r2 + 3: three real roots (a!=0.0) *r1 <= *r2 <= *r3 +*/ +ON_DECL +int ON_SolveCubicEquation( + double a, double b, double c, double d, + double* r1, double* r2, double* r3 +); + /* Returns: 0: success diff --git a/opennurbs_mesh.cpp b/opennurbs_mesh.cpp index 60008a15..98e73490 100644 --- a/opennurbs_mesh.cpp +++ b/opennurbs_mesh.cpp @@ -1209,26 +1209,30 @@ bool ON_Mesh::IsValid( ON_TextLog* text_logx ) const } else { - //const ON_3fPoint* fV = m_V.Array(); + const ON_3fPoint* fV = m_V.Array(); for ( fi = 0; fi < facet_count; fi++ ) { - // This test was too harsh for float precision meshes - // with nearly degnerate faces after they are transformed - // by a transform with a reasonable sized translation - // component. - // See bug http://dev.mcneel.com/bugtrack/?q=87465 + // This test is considered relatively harsh for float precision meshes with nearly degnerate faces + // after they are transformed by a transform with a reasonable sized translation + // component, as in https://mcneel.myjetbrains.com/youtrack/issue/RH-10177. + // However, removing this creates unreasonable pressure on double precision meshes, because, after being + // trasformed to double precision, a wrongly valid single-precision-collapsed-edge mesh makes an + // invalid double-precision mesh altogether. This cannot be tolerated. + // The goal should be to have invalid single-precision-only meshes be treated by MeshRepair when created. + // See https://mcneel.myjetbrains.com/youtrack/issue/RH-54563 and + /// https://mcneel.myjetbrains.com/youtrack/issue/RH-30283 - //if ( !m_F[fi].IsValid( vertex_count, fV ) ) - //{ - // if ( text_log ) - // { - // if ( !m_F[fi].IsValid( vertex_count) ) - // text_log->Print("ON_Mesh.m_F[%d].vi[] has invalid vertex indices.\n",fi); - // else - // text_log->Print("ON_Mesh.m_F[%d] has degenerate float precision vertex locations.\n",fi); - // } - // return ON_MeshIsNotValid(bSilentError); - //} + if ( !m_F[fi].IsValid( vertex_count, fV ) ) + { + if ( text_log ) + { + if ( !m_F[fi].IsValid( vertex_count) ) + text_log->Print("ON_Mesh.m_F[%d].vi[] has invalid vertex indices.\n",fi); + else + text_log->Print("ON_Mesh.m_F[%d] has degenerate float precision vertex locations.\n",fi); + } + return ON_MeshIsNotValid(bSilentError); + } if ( !m_F[fi].IsValid( vertex_count ) ) { @@ -1256,6 +1260,66 @@ bool ON_Mesh::IsValid( ON_TextLog* text_logx ) const return true; } +static void Internal_PrintMeshArrayHash(ON_TextLog& text_log, ON_SHA1_Hash hash, const wchar_t* prefix, bool bNewLine) +{ + if (nullptr != prefix && 0 != prefix[0]) + text_log.Print(L"%ls ", prefix); + hash.Dump(text_log); + if (bNewLine) + text_log.PrintNewLine(); +} + +static void Internal_PrintMeshArrayHash(ON_TextLog& text_log, const ON_SimpleArray& a, const wchar_t* prefix, bool bNewLine) +{ + ON_SHA1 sha1; + ON_SHA1_Accumulate3fVectorArray(sha1,a); + const ON_SHA1_Hash hash = sha1.Hash(); + Internal_PrintMeshArrayHash(text_log, hash, prefix, bNewLine); +} + +static void Internal_PrintMeshArrayHash(ON_TextLog& text_log, const ON_SimpleArray& a, const wchar_t* prefix, bool bNewLine) +{ + ON_SHA1 sha1; + ON_SHA1_Accumulate2fPointArray(sha1,a); + const ON_SHA1_Hash hash = sha1.Hash(); + Internal_PrintMeshArrayHash(text_log, hash, prefix, bNewLine); +} + +static void Internal_PrintMeshArrayHash(ON_TextLog& text_log, const ON_SimpleArray& a, const wchar_t* prefix, bool bNewLine) +{ + ON_SHA1 sha1; + ON_SHA1_Accumulate3fPointArray(sha1,a); + const ON_SHA1_Hash hash = sha1.Hash(); + Internal_PrintMeshArrayHash(text_log, hash, prefix, bNewLine); +} + +static void Internal_PrintMeshArrayHash(ON_TextLog& text_log, const ON_SimpleArray& a, const wchar_t* prefix, bool bNewLine) +{ + ON_SHA1 sha1; + ON_SHA1_Accumulate2dPointArray(sha1,a); + const ON_SHA1_Hash hash = sha1.Hash(); + Internal_PrintMeshArrayHash(text_log, hash, prefix, bNewLine); +} + +static void Internal_PrintMeshArrayHash(ON_TextLog& text_log, const ON_SimpleArray& a, const wchar_t* prefix, bool bNewLine) +{ + ON_SHA1 sha1; + ON_SHA1_Accumulate3dPointArray(sha1,a); + const ON_SHA1_Hash hash = sha1.Hash(); + Internal_PrintMeshArrayHash(text_log, hash, prefix, bNewLine); +} + +static void Internal_PrintMeshArrayHash(ON_TextLog& text_log, const ON_SimpleArray& F, const wchar_t* prefix, bool bNewLine) +{ + ON_SHA1 sha1; + const ON_MeshFace* aa = F.Array(); + const ON__INT32* e = (nullptr != aa) ? &aa[0].vi[0] : nullptr; + const size_t count = F.UnsignedCount() * sizeof(aa[0]) / sizeof(e[0]); + sha1.AccumulateInteger32Array(count,e); + const ON_SHA1_Hash hash = sha1.Hash(); + Internal_PrintMeshArrayHash(text_log, hash, prefix, bNewLine); +} + void ON_Mesh::Dump( ON_TextLog& dump ) const { const int half_max = 8; @@ -1292,9 +1356,11 @@ void ON_Mesh::Dump( ON_TextLog& dump ) const dump.Print("%d mesh vertices:\n",m_V.Count()); { dump.PushIndent(); + Internal_PrintMeshArrayHash(dump, m_V, L"m_V array hash", true); const ON_3dPoint* D = 0; if ( bDoubles ) { + Internal_PrintMeshArrayHash(dump, m_dV, L"m_dV array hash", true); D = DoublePrecisionVertices().Array(); } for (i = 0; i < vcount; i++) @@ -1330,6 +1396,7 @@ void ON_Mesh::Dump( ON_TextLog& dump ) const dump.Print("%d mesh vertex normals:\n",m_N.Count()); { dump.PushIndent(); + Internal_PrintMeshArrayHash(dump, m_N, L"m_N array hash", true); for (i = 0; i < vcount; i++) { if ( i == half_max && 2*half_max < vcount ) @@ -1352,6 +1419,7 @@ void ON_Mesh::Dump( ON_TextLog& dump ) const dump.Print("%d mesh vertex texture coordinates:\n",m_T.Count()); { dump.PushIndent(); + Internal_PrintMeshArrayHash(dump, m_T, L"m_T array hash", true); for (i = 0; i < vcount; i++) { if ( i == half_max && 2*half_max < vcount ) @@ -1377,6 +1445,7 @@ void ON_Mesh::Dump( ON_TextLog& dump ) const dump.Print("%d mesh vertex surface parameters:\n",m_S.Count()); { dump.PushIndent(); + Internal_PrintMeshArrayHash(dump, m_S, L"m_S array hash", true); for (i = 0; i < vcount; i++) { if ( i == half_max && 2*half_max < vcount ) @@ -1397,6 +1466,7 @@ void ON_Mesh::Dump( ON_TextLog& dump ) const dump.Print("%d mesh faces:\n",m_F.Count()); { dump.PushIndent(); + Internal_PrintMeshArrayHash(dump, m_F, L"m_F array hash", true); for (i = 0; i < fcount; i++) { if ( i == half_max && 2*half_max < fcount ) @@ -5198,6 +5268,18 @@ void ON_Mesh::Append( int mesh_count, const ON_Mesh* const* meshes ) bool bHasDoubles = (0 == vcount0 || HasSynchronizedDoubleAndSinglePrecisionVertices()); bool bHasNgonMap = (NgonCount() > 0 && 0 != NgonMap()); + bool bSetMeshParameters = true; + const ON_MeshParameters* mp = nullptr; + ON_SHA1_Hash mp_hash = ON_SHA1_Hash::EmptyContentHash; + if (0 != vcount0) + { + mp = this->MeshParameters(); + if (nullptr == mp) + bSetMeshParameters = false; + else + mp_hash = mp->GeometrySettingsHash(); + } + bool bHasSurfaceDomain = bHasSurfaceParameters && (0 == vcount0 || (m_srf_domain[0].IsIncreasing() && m_srf_domain[1].IsIncreasing())); @@ -5231,6 +5313,31 @@ void ON_Mesh::Append( int mesh_count, const ON_Mesh* const* meshes ) merged_count++; + if (bSetMeshParameters) + { + const ON_MeshParameters* this_mesh_mp = m->MeshParameters(); + if (nullptr == this_mesh_mp) + bSetMeshParameters = false; + else + { + const ON_SHA1_Hash this_mesh_mp_hash = this_mesh_mp->GeometrySettingsHash(); + if (nullptr == mp) + { + // first mesh parameters. + mp = this_mesh_mp; + mp_hash = this_mesh_mp_hash; + } + else + { + if (this_mesh_mp_hash != mp_hash) + { + // variable mesh paramters - means output gets none. + bSetMeshParameters = false; + } + } + } + } + int fcount1 = m->m_F.Count(); if ( fcount1 > 0 ) fcount += fcount1; @@ -5557,6 +5664,12 @@ void ON_Mesh::Append( int mesh_count, const ON_Mesh* const* meshes ) if ( NgonCount() > 0 && bHasNgonMap ) CreateNgonMap(); + + if (bSetMeshParameters && nullptr != mp && mp != this->MeshParameters()) + { + // Appending to an empty this and all appendees have matching mesh parameters. + this->SetMeshParameters(*mp); + } } void ON_Mesh::Append( const ON_Mesh& m ) @@ -5739,16 +5852,37 @@ void ON_MeshParameters::SetSimplePlanes( Internal_SetBoolHelper(bSimplePlanes, &m_bSimplePlanes); } +ON_SubDComponentLocation ON_SubDComponentLocationFromUnsigned( + unsigned int loc_as_unsigned +) +{ + switch (loc_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentLocation::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentLocation::ControlNet); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentLocation::Surface); + } + + ON_ERROR("Invalid loc_as_unsigned parameter"); + return ON_SubDComponentLocation::Unset; +} + + void ON_MeshParameters::SetSubDDisplayParameters( const ON_SubDDisplayParameters& subd_parameters ) { - m_subd_mesh_parameters = subd_parameters.EncodeAsUnsignedChar(); + const unsigned char subd_mesh_paramters_as_char = subd_parameters.EncodeAsUnsignedChar(); + if (subd_mesh_paramters_as_char != m_subd_mesh_parameters_as_char) + { + m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest; + m_subd_mesh_parameters_as_char = subd_mesh_paramters_as_char; + } } const ON_SubDDisplayParameters ON_MeshParameters::SubDDisplayParameters() const { - return ON_SubDDisplayParameters::DecodeFromUnsignedChar(m_subd_mesh_parameters); + return ON_SubDDisplayParameters::DecodeFromUnsignedChar(m_subd_mesh_parameters_as_char); } const bool ON_MeshParameters::Refine() const @@ -6055,28 +6189,140 @@ ON_MeshParameters::ON_MeshParameters( SetRefine((density < 0.65)); SetSimplePlanes((0.0 == density)); - - unsigned int subd_display_density = ON_SubDDisplayParameters::Default.DisplayDensity(); - - if (density <= ON_ZERO_TOLERANCE) - subd_display_density = 1; - else if (density < 1.0/6.0) - subd_display_density = ON_SubDDisplayParameters::CourseDensity; - else if (density < 1.0/3.0) - subd_display_density = (ON_SubDDisplayParameters::DefaultDensity+ON_SubDDisplayParameters::CourseDensity)/2; - else if (density <= 0.75) - subd_display_density = ON_SubDDisplayParameters::DefaultDensity; - else if (density <= 1.0-ON_ZERO_TOLERANCE) - subd_display_density = (ON_SubDDisplayParameters::DefaultDensity+ON_SubDDisplayParameters::MaximumDensity)/2; - else if (density >= 1.0 - ON_ZERO_TOLERANCE) - subd_display_density = ON_SubDDisplayParameters::MaximumDensity; - - ON_SubDDisplayParameters subd_parameters(ON_SubDDisplayParameters::Default); - subd_parameters.SetDisplayDensity(subd_display_density); + ON_SubDDisplayParameters subd_parameters = ON_SubDDisplayParameters::CreateFromMeshDensity(density); SetSubDDisplayParameters(subd_parameters); } } +double ON_MeshParameters::ClampMeshDensityValue(double slider_value) +{ + // Make "fuzzy" values of 0.0, 0.5, and 1.0 exactly 0, 0.5, and 1.0 so + // siders of various int resolutions and code that uses float instead + // of double precision values behaves in the expected way. + if (slider_value >= -ON_SQRT_EPSILON && slider_value <= ON_SQRT_EPSILON) + slider_value = 0.0; + else if (slider_value >= (0.5 - ON_SQRT_EPSILON) && slider_value <= (0.5 + ON_SQRT_EPSILON)) + slider_value = 0.5; + else if (slider_value >= (1.0 - ON_SQRT_EPSILON) && slider_value <= (1.0 + ON_SQRT_EPSILON)) + slider_value = 1.0; + else if (false == (slider_value >= 0.0 && slider_value <= 1.0)) + { + // If you get this error, your user interface code has a bug. + ON_ERROR("Invalid slider_value - defaulting to 0.5"); + slider_value = 0.5; // invalid input treated as 0.5. + } + + return slider_value; +} + +const ON_wString ON_MeshParameters::Description() const +{ + ON_wString description; + const double silder_value = this->MeshDensity(); + + const ON_SHA1_Hash hash = this->GeometrySettingsHash(); + if (hash == ON_MeshParameters::FastRenderMesh.GeometrySettingsHash()) + description = ON_wString(L"Fast"); + else if (hash == ON_MeshParameters::QualityRenderMesh.GeometrySettingsHash()) + description = ON_wString(L"Quality"); + else if (silder_value >= 0.0 && silder_value <= 1.0) + description = ON_wString::FormatToString(L"Density(%g%%)", ON_MeshParameters::MeshDensityAsPercentage(silder_value)); + else if (hash == ON_MeshParameters::DefaultAnalysisMesh.GeometrySettingsHash()) + description = ON_wString(L"DefaultAnalysis"); + else if (hash == ON_MeshParameters::DefaultMesh.GeometrySettingsHash()) + description = ON_wString(L"Default"); + else + { + description = ON_wString(L"Custom("); + description += hash.ToString(true); + description += L")"; + } + + return description; +} + +const ON_MeshParameters ON_MeshParameters::CreateFromMeshDensity(double slider_value) +{ + return ON_MeshParameters(ON_MeshParameters::ClampMeshDensityValue(slider_value)); +} + +double ON_MeshParameters::MeshDensityAsPercentage(double slider_value) +{ + if (slider_value >= 0.0 && slider_value <= 1.0) + { + const double percent_fuzz_tol = 1.0e-4; + const double slider_percent = slider_value * 100.0; // percent = slider_value as a percentage. + const double n = floor(slider_percent + 0.25); + if (fabs(n - slider_percent) <= percent_fuzz_tol) + return n; // slider_percent is within fuzz of being an integer - return the integer + + const double p = 100.0*(floor(1024.0 * slider_value + 0.25) / 1024.0); + if (fabs(p - slider_percent) <= percent_fuzz_tol) + return p; // slider_percent is within fuzz of 100.0*(N/1024.0). Return 100.0*(N/1024.0). + + return slider_percent; // return percentage with no fuzz removal + } + return ON_DBL_QNAN; // not a percent +} + +double ON_MeshParameters::MeshDensity() const +{ + for (;;) + { + const double candidate_density = this->RelativeTolerance(); + if (false == (candidate_density >= 0.0 && candidate_density <= 1.0)) + break; // invalid candidate_density + + // 5 fast tests for quick rejection + if ((this->m_bSimplePlanes ? 1 : 0) != ((0.0 == candidate_density) ? 1 : 0)) + break; + if (false == (this->m_grid_angle_radians == 0.0)) + break; + if (false == (this->m_grid_amplification == 0.0)) + break; + if (false == (this->m_refine_angle_radians == 0.0)) + break; + if (this->SubDDisplayParameters().DisplayDensity() != ON_SubDDisplayParameters::CreateFromMeshDensity(candidate_density).DisplayDensity()) + break; + + // Now build one with the candidate_density slider value + ON_MeshParameters candidate_mp = ON_MeshParameters::CreateFromMeshDensity(candidate_density); + +#define ON_COPY_MESH_PARAMETERS_MEMBER(M) candidate_mp.M = this->M + // ignore these paramters do not control the mesh density. + ON_COPY_MESH_PARAMETERS_MEMBER(m_bCustomSettings); + ON_COPY_MESH_PARAMETERS_MEMBER(m_bCustomSettingsEnabled); + ON_COPY_MESH_PARAMETERS_MEMBER(m_bComputeCurvature); + ON_COPY_MESH_PARAMETERS_MEMBER(m_bDoublePrecision); + ON_COPY_MESH_PARAMETERS_MEMBER(m_bClosedObjectPostProcess); + ON_COPY_MESH_PARAMETERS_MEMBER(m_texture_range); + if (ON_nil_uuid == this->m_mesher_id) + { + // Pangolin parameters do not apply + ON_COPY_MESH_PARAMETERS_MEMBER(m_mesher_id); + ON_COPY_MESH_PARAMETERS_MEMBER(m_bEvaluatorBasedTessellation); + ON_COPY_MESH_PARAMETERS_MEMBER(m_curve_tess_min_num_segments); + ON_COPY_MESH_PARAMETERS_MEMBER(m_curve_tess_angle_tol_in_degrees); + ON_COPY_MESH_PARAMETERS_MEMBER(m_curve_tess_max_dist_between_points); + ON_COPY_MESH_PARAMETERS_MEMBER(m_curve_tess_min_parametric_ratio); + ON_COPY_MESH_PARAMETERS_MEMBER(m_surface_tess_angle_tol_in_degrees); + ON_COPY_MESH_PARAMETERS_MEMBER(m_surface_tess_max_edge_length); + ON_COPY_MESH_PARAMETERS_MEMBER(m_surface_tess_min_edge_length); + ON_COPY_MESH_PARAMETERS_MEMBER(m_surface_tess_min_edge_length_ratio_uv); + ON_COPY_MESH_PARAMETERS_MEMBER(m_surface_tess_max_aspect_ratio); + ON_COPY_MESH_PARAMETERS_MEMBER(m_smoothing_passes); + } +#undef ON_COPY_MESH_PARAMETERS_MEMBER + + if (0 != ON_MeshParameters::Compare(candidate_mp, *this)) + break; + + // These mesh parameters will create the same mesh geometry as ON_MeshParameters::CreateFromMeshDensity(). + return candidate_density; + } + return ON_DBL_QNAN; +} + double ON_MeshParameters::MinimumEdgeLengthFromTolerance( double max_edge_length, double tolerance @@ -6148,43 +6394,66 @@ bool operator!=(const ON_MeshParameters& a, const ON_MeshParameters& b) void ON_MeshParameters::Dump( ON_TextLog& text_log ) const { + const ON_wString description = this->Description(); + text_log.Print(L"Description: %ls\n", static_cast(description)); text_log.Print(L"Gridding:\n"); - text_log.PushIndent(); - text_log.Print(L"Min grid count = %d\n",m_grid_min_count); - text_log.Print(L"Max grid count = %d\n",m_grid_max_count); - text_log.Print(L"Gridding angle = %g radians (%g degrees)\n",GridAngleRadians(),GridAngleDegrees()); - text_log.Print(L"Aspect ratio = %g\n",m_grid_aspect_ratio); - text_log.Print(L"Amplification = %g\n",m_grid_amplification); - text_log.PopIndent(); + { + const ON_TextLogIndent indent1(text_log); + text_log.Print(L"Min grid count = %d\n", m_grid_min_count); + text_log.Print(L"Max grid count = %d\n", m_grid_max_count); + text_log.Print(L"Gridding angle = %g radians (%g degrees)\n", GridAngleRadians(), GridAngleDegrees()); + text_log.Print(L"Aspect ratio = %g\n", m_grid_aspect_ratio); + text_log.Print(L"Amplification = %g\n", m_grid_amplification); + } text_log.Print(L"Refining:\n"); - text_log.PushIndent(); - text_log.Print(L"Refine = %ls\n", m_bRefine? L"true" : L"false"); - text_log.Print(L"Refine angle = %g radians (%g degrees)\n",RefineAngleRadians(),RefineAngleDegrees()); - text_log.PopIndent(); + { + const ON_TextLogIndent indent1(text_log); + text_log.Print(L"Refine = %ls\n", m_bRefine ? L"true" : L"false"); + text_log.Print(L"Refine angle = %g radians (%g degrees)\n", RefineAngleRadians(), RefineAngleDegrees()); + } text_log.Print(L"Metrics:\n"); - text_log.PushIndent(); - text_log.Print(L"Tolerance from size 1 object = %g (relative tolerance = %g)\n",ON_MeshParameters::ToleranceFromObjectSize(RelativeTolerance(),1.0),RelativeTolerance()); - text_log.Print(L"Minimum tolerance = %g\n",MinimumTolerance()); - text_log.Print(L"Tolerance = %g\n",m_tolerance); - text_log.Print(L"Min edge length = %g\n",m_min_edge_length); - text_log.Print(L"Max edge length = %g\n",m_max_edge_length); - text_log.PopIndent(); + { + const ON_TextLogIndent indent1(text_log); + text_log.Print(L"Tolerance from size 1 object = %g (relative tolerance = %g)\n", ON_MeshParameters::ToleranceFromObjectSize(RelativeTolerance(), 1.0), RelativeTolerance()); + text_log.Print(L"Minimum tolerance = %g\n", MinimumTolerance()); + text_log.Print(L"Tolerance = %g\n", m_tolerance); + text_log.Print(L"Min edge length = %g\n", m_min_edge_length); + text_log.Print(L"Max edge length = %g\n", m_max_edge_length); + } + + text_log.Print(L"SubDMeshParameters:\n"); + { + const ON_TextLogIndent indent1(text_log); + this->SubDDisplayParameters().Dump(text_log); + } text_log.Print(L"Misceleanous:\n"); - text_log.PushIndent(); - text_log.Print(L"Face type = %d\n",m_face_type ); - text_log.Print(L"Compute curvature = %ls\n",m_bComputeCurvature?L"true":L"false"); - text_log.Print(L"Texture range = %d\n",m_texture_range); - text_log.Print(L"Simple planes = %ls\n",m_bSimplePlanes?L"true":L"false"); - text_log.Print(L"Jagged Seams = %ls\n",m_bJaggedSeams?L"true":L"false"); - text_log.Print(L"Double Precision = %ls\n",m_bDoublePrecision?L"true":L"false"); - text_log.Print(L"Closed object mesh healing = %ls\n",ClosedObjectPostProcess()?L"true":L"false"); - text_log.Print(L"Custom settings = %ls\n",m_bCustomSettings?L"true":L"false"); + { + const ON_TextLogIndent indent1(text_log); + text_log.Print(L"Face type = %d\n", m_face_type); + text_log.Print(L"Compute curvature = %ls\n", m_bComputeCurvature ? L"true" : L"false"); + text_log.Print(L"Texture range = %d\n", m_texture_range); + text_log.Print(L"Simple planes = %ls\n", m_bSimplePlanes ? L"true" : L"false"); + text_log.Print(L"Jagged Seams = %ls\n", m_bJaggedSeams ? L"true" : L"false"); + text_log.Print(L"Double Precision = %ls\n", m_bDoublePrecision ? L"true" : L"false"); + text_log.Print(L"Closed object mesh healing = %ls\n", ClosedObjectPostProcess() ? L"true" : L"false"); + } - - text_log.PopIndent(); + text_log.Print(L"Custom:\n"); + { + const ON_TextLogIndent indent1(text_log); + text_log.Print(L"Custom settings = %ls\n", m_bCustomSettings ? L"true" : L"false"); + text_log.Print(L"Custom settings enabled = %ls\n", m_bCustomSettingsEnabled ? L"true" : L"false"); + const ON_UUID id = MesherId(); + if (ON_UuidIsNotNil(id)) + { + text_log.Print(L"Mesher ID = "); + text_log.Print(id); + text_log.PrintNewLine(); + } + } } static double ON_MeshParameters_SHA1Double(double t, double default_value) @@ -6210,14 +6479,34 @@ int ON_MeshParameters::CompareGeometrySettings( ON_SHA1_Hash ON_MeshParameters::ContentHash() const { - // Discuss any changes with Dale Lear + // Please discuss any changes with Dale Lear + + // These values are intentionally ignored + // m_bCustomSettingsEnabled + // m_bDoublePrecision + // Use ON_MeshParameters::GeometrySettingsHash() if you want to ignore any of these values, like m_bComputeCurvature. ON_SHA1 sha1; sha1.AccumulateBool(m_bCustomSettings); sha1.AccumulateBool(m_bComputeCurvature); sha1.AccumulateUnsigned32(m_texture_range); + sha1.AccumulateUnsigned32(m_bClosedObjectPostProcess); + const ON_SHA1_Hash geometry_settings_hash = GeometrySettingsHash(); sha1.AccumulateSubHash(geometry_settings_hash); + + if (ON_UuidIsNil(m_mesher_id)) + { + // When m_mesher_id is nil, the Pangolin parameters are not included in + // GeometrySettingsHash() because they do not apply. + // Since ContentHash() is a hash of every ON_MeshParameters setting, + // the Pangolin get accumulated here. + sha1.AccumulateId(m_mesher_id); + + // Pangolin parameters + Internal_AccumulatePangolinParameters(ON_MeshParameters::DefaultMesh, sha1); + } + return sha1.Hash(); } @@ -6253,7 +6542,22 @@ void ON_MeshParameters::Internal_AccumulatePangolinParameters( ON_SHA1_Hash ON_MeshParameters::GeometrySettingsHash() const { - // Discuss any changes with Dale Lear + // Please discuss any changes with Dale Lear + + // These values are intentionally ignored. + // m_bCustomSettings + // m_bCustomSettingsEnabled + // m_bComputeCurvature + // m_bDoublePrecision + // m_bClosedObjectPostProcess + // m_texture_range + // If they are included here, Rhino will remesh when it should not because it treats + // Texture coordinates as a mutable property (const_cast widely used in Rhino). + // Curvature is added/removed from analysis mode as needed. + // Custom settings controls where the mesh settings come from and when they + // match what was used to create an existing mesh, a remesh is extremely wasteful, + // ClosedObjectPostProcess is added/removed as needed and is a hack to cover up core bugs. + if (m_geometry_settings_hash.IsZeroDigest()) { ON_SHA1 sha1; @@ -6276,16 +6580,21 @@ ON_SHA1_Hash ON_MeshParameters::GeometrySettingsHash() const sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_refine_angle_radians,0.0)); sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_grid_amplification,1.0)); sha1.AccumulateUnsigned32(m_face_type); - sha1.AccumulateBool(m_bClosedObjectPostProcess); - // The Pangolin parameters and any other, parameters we add in the future, - // contribute to the SHA1 only when they differ from default values. - // This keeps old SHA-1 values correct and prevents remeshing when openning - // old files. - sha1.AccumulateId(m_mesher_id); + // SubD meshing parameters + sha1.AccumulateBytes(&m_subd_mesh_parameters_as_char, sizeof(m_subd_mesh_parameters_as_char)); - // Pangolin parameters - Internal_AccumulatePangolinParameters(ON_MeshParameters::DefaultMesh,sha1); + if (ON_UuidIsNotNil(m_mesher_id)) + { + // The Pangolin parameters and any other, parameters we add in the future, + // contribute to the SHA1 only when they differ from default values. + // This keeps old SHA-1 values correct and prevents remeshing when openning + // old files. + sha1.AccumulateId(m_mesher_id); + + // Pangolin parameters + Internal_AccumulatePangolinParameters(ON_MeshParameters::DefaultMesh, sha1); + } m_geometry_settings_hash = sha1.Hash(); } @@ -6330,16 +6639,29 @@ const int ON_MeshParameters::GeometrySettingsDensityPercentage( int no_match_found_result ) const { - for ( int n = 0; n <= 100; n++ ) + const double slider_value = this->MeshDensity(); + if (slider_value >= 0.0 && slider_value <= 1.0) { - double density = (50 == n) ? 0.5 : (n/100.0); - ON_MeshParameters mp_at_n(density); - - mp_at_n.m_bDoublePrecision = m_bDoublePrecision; - mp_at_n.m_texture_range = m_texture_range; - - if ( 0 == ON_MeshParameters::CompareGeometrySettings(mp_at_n,*this) ) + const int n = (int)floor(100.0 * slider_value + 0.4999); + if (n >= 0 && n <= 100) return n; + + // The code BELOW is slower and less reliable. Different platforms and code convert + // user interface int values to normalized double "slider value" slightly differently. + // (Some user interface controls use int ranges that are not multiples of 10 or are greater than 100). + // The code ABOVE will insures if a "simple slider" user interface set the mesh parameters, + // then this function will return an integer between 0 and 100 that produces + // identical or nearly identical meshing parameters. + // + // NO //for (int n = 0; n <= 100; n++) + // NO //{ + // NO // double density = (50 == n) ? 0.5 : (n / 100.0); + // NO // ON_MeshParameters mp_at_n = ON_MeshParameters::CreateFromMeshDensity(density); + // NO // mp_at_n.m_bDoublePrecision = m_bDoublePrecision; + // NO // mp_at_n.m_texture_range = m_texture_range; + // NO // if (0 == ON_MeshParameters::CompareGeometrySettings(mp_at_n, *this)) + // NO // return n; + // NO //} } return no_match_found_result; } @@ -6353,7 +6675,7 @@ ON__UINT32 ON_MeshParameters::DataCRC(ON__UINT32 current_remainder) const bool ON_MeshParameters::Write( ON_BinaryArchive& file ) const { - int minor_version = 4; + int minor_version = 5; bool rc = file.Write3dmChunkVersion(1,minor_version); if (rc) { @@ -6393,6 +6715,12 @@ bool ON_MeshParameters::Write( ON_BinaryArchive& file ) const // added for chunk version 1.4 - 3 March 2011 if (rc) rc = file.WriteBool( m_bCustomSettingsEnabled ); + + if (rc) + { + // added for chunk version 1.5 - June 19, 2020 + SubDDisplayParameters().Write(file); + } } return rc; } @@ -6463,6 +6791,13 @@ bool ON_MeshParameters::Read( ON_BinaryArchive& file ) if ( rc && minor_version >= 4 ) { rc = file.ReadBool(&m_bCustomSettingsEnabled); + if (rc && minor_version >= 5) + { + ON_SubDDisplayParameters subdp = ON_SubDDisplayParameters::Default; + rc = subdp.Read(file); + if (rc) + SetSubDDisplayParameters(subdp); + } } } } @@ -6471,6 +6806,60 @@ bool ON_MeshParameters::Read( ON_BinaryArchive& file ) return rc; } +bool ON_SubDDisplayParameters::Write(class ON_BinaryArchive& archive) const +{ + if (false == archive.BeginWrite3dmAnonymousChunk(1)) + return false; + bool rc = false; + for(;;) + { + const unsigned int display_density = this->DisplayDensity(); + if (false == archive.WriteInt(display_density)) + break; + + const ON_SubDComponentLocation loc = this->MeshLocation(); + const unsigned int loc_as_unsigned = static_cast(loc); + if (false == archive.WriteInt(loc_as_unsigned)) + break; + + rc = true; + break; + } + if (false == archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + +bool ON_SubDDisplayParameters::Read(class ON_BinaryArchive& archive) +{ + *this = ON_SubDDisplayParameters::Default; + int chunk_version = 0; + if (false == archive.BeginRead3dmAnonymousChunk(&chunk_version)) + return false; + bool rc = false; + for (;;) + { + if (chunk_version <= 0) + break; + + unsigned int display_density = this->DisplayDensity(); + if (false == archive.ReadInt(&display_density)) + break; + SetDisplayDensity(display_density); + + unsigned int loc_as_unsigned = static_cast(this->MeshLocation()); + if (false == archive.ReadInt(&loc_as_unsigned)) + break; + const ON_SubDComponentLocation loc = ON_SubDComponentLocationFromUnsigned(loc_as_unsigned); + SetMeshLocation(loc); + + rc = true; + break; + } + if (false == archive.EndRead3dmChunk()) + rc = false; + return rc; +} ON_MeshCurvatureStats::ON_MeshCurvatureStats() @@ -8178,6 +8567,33 @@ void ON_Mesh::Cleanup(bool bRemoveNgons) // // ON_SurfaceCurvature // +const ON_SurfaceCurvature ON_SurfaceCurvature::CreateFromPrincipalCurvatures( + double k1, + double k2 +) +{ + ON_SurfaceCurvature k; + k.k1 = k1; + k.k2 = k2; + return k; +} + +bool ON_SurfaceCurvature::IsSet() const +{ + return (ON_UNSET_VALUE < k1&& k1 < ON_UNSET_POSITIVE_VALUE&& ON_UNSET_VALUE < k2&& k2 < ON_UNSET_POSITIVE_VALUE); +} + +bool ON_SurfaceCurvature::IsZero() const +{ + return (0.0 == k1 && 0.0 == k2); +} + +bool ON_SurfaceCurvature::IsUnset() const +{ + return IsSet() ? false : true; +} + + double ON_SurfaceCurvature::GaussianCurvature() const { return k1*k2; @@ -8212,23 +8628,6 @@ double ON_SurfaceCurvature::MaximumRadius() const return k; } -//double ON_SurfaceCurvature::NormalCurvature(const ON_3dVector& tangent) const -//{ -// double c = tangent*e1; -// double s = tangent*e2; -// return k1*c*c + k2*s*s; -//} - -//double ON_SurfaceCurvature::NormalSectionCurvature( const ON_3dVector& section_normal, const ON_3dVector& surface_normal ) const -//{ -// ON_3dVector tangent = ON_CrossProduct( section_normal, surface_normal ); -// if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.y) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON ) -// tangent.Zero(); -// else -// tangent.Unitize(); -// return NormalCurvature(tangent); -//} - ON_MeshTopology::ON_MeshTopology() : m_mesh(0) , m_memchunk(0) @@ -10978,34 +11377,112 @@ bool ON_MeshTopology::TopFaceIsHidden( int topfi ) const return m_mesh ? m_mesh->FaceIsHidden(topfi) : false; } - ON_MappingTag::ON_MappingTag() { Default(); } +const ON_Xform ON_MappingTag::Transform() const +{ + return TransformIsIdentity() ? ON_Xform::IdentityTransformation : m_mesh_xform; +} + +bool ON_MappingTag::TransformIsIdentity() const +{ + return ON_MappingTag::TransformTreatedIsIdentity(&m_mesh_xform); +} + +bool ON_MappingTag::TransformTreatedIsIdentity(const ON_Xform* xform) +{ + if (nullptr == xform) + return true; // a missing xform is treated as the idenity. + if (xform->IsIdentity(ON_ZERO_TOLERANCE)) + return true; + if (xform->IsZero()) + return true; // zero is not a valid mapping object xform + if (!xform->IsValid()) + return true; // an invalid object xform is treated as the identity + return false; // a valid non zero, non identity xform is actually used. +} + ON_MappingTag::ON_MappingTag(const ON_TextureMapping & mapping, const ON_Xform * xform) { Default(); Set(mapping); - if ( - ON_TextureMapping::TYPE::no_mapping != mapping.m_type && ON_TextureMapping::TYPE::srfp_mapping != mapping.m_type - && nullptr != xform && xform->IsValid() && false == xform->IsIdentity(ON_ZERO_TOLERANCE) && false == xform->IsZero()) + if ( false == ON_MappingTag::TransformTreatedIsIdentity(xform)) m_mesh_xform = *xform; } void ON_MappingTag::Dump( ON_TextLog& text_log ) const { - text_log.Print("Texture/color coordinates tag:\n"); - text_log.PushIndent(); - text_log.Print("mapping id: "); text_log.Print(m_mapping_id); text_log.Print("\n"); - text_log.Print("mapping crc: %08x\n",m_mapping_crc); - text_log.Print("mesh xform:\n"); - text_log.PushIndent(); text_log.Print(m_mesh_xform); text_log.PopIndent(); - text_log.PushIndent(); - text_log.Print(m_mesh_xform); - text_log.PopIndent(); - text_log.PopIndent(); + text_log.Print("Texture/color mapping tag:\n"); + if (text_log.IsTextHash()) + { + // The code is a mess with respect to mapping tags and they are + // often mutable or changed with const/cast in unpredictable ways. + text_log.Print(" ...\n"); + return; + } + + const ON_TextLogIndent indent1(text_log); + + if (0 == ON_MappingTag::CompareAll(ON_MappingTag::Unset, *this)) + { + text_log.Print("ON_MappingTag::Unset\n"); + } + else if (0 == ON_MappingTag::CompareAll(ON_MappingTag::SurfaceParameterMapping, *this)) + { + text_log.Print("ON_MappingTag::SurfaceParameterMapping\n"); + } + else + { + text_log.Print("mapping type = "); + switch (m_mapping_type) + { + case ON_TextureMapping::TYPE::no_mapping: + text_log.Print("none"); + break; + case ON_TextureMapping::TYPE::srfp_mapping: + text_log.Print("srfp"); + break; + case ON_TextureMapping::TYPE::plane_mapping: + text_log.Print("plane"); + break; + case ON_TextureMapping::TYPE::cylinder_mapping: + text_log.Print("cylinder"); + break; + case ON_TextureMapping::TYPE::sphere_mapping: + text_log.Print("sphere"); + break; + case ON_TextureMapping::TYPE::box_mapping: + text_log.Print("box"); + break; + case ON_TextureMapping::TYPE::mesh_mapping_primitive: + text_log.Print("mesh primative"); + break; + case ON_TextureMapping::TYPE::srf_mapping_primitive: + text_log.Print("srf primative"); + break; + case ON_TextureMapping::TYPE::brep_mapping_primitive: + text_log.Print("brep primative"); + break; + case ON_TextureMapping::TYPE::ocs_mapping: + text_log.Print("ocs"); + break; + } + text_log.Print("\n"); + + text_log.Print("mapping id = "); + text_log.Print(m_mapping_id); + if (m_mapping_id == ON_MappingTag::SurfaceParameterMapping.m_mapping_id) + text_log.Print(" = ON_MappingTag::SurfaceParameterMapping.m_mapping_id"); + text_log.PrintNewLine(); + + text_log.Print("mapping crc: %08x\n", m_mapping_crc); + text_log.Print("mesh xform:\n"); + const ON_TextLogIndent indent2(text_log); + text_log.Print(m_mesh_xform); + } } void ON_MappingTag::Transform( const ON_Xform& xform ) @@ -11049,6 +11526,60 @@ bool ON_MappingTag::IsDefaultSurfaceParameterMapping() const } +const ON_SHA1_Hash ON_MappingTag::Hash() const +{ + bool bHashType = true; + bool bHashIdAndCRC = false; + bool bHashXform = false; + switch (m_mapping_type) + { + case ON_TextureMapping::TYPE::no_mapping: + bHashType = false; + break; + case ON_TextureMapping::TYPE::srfp_mapping: + break; + case ON_TextureMapping::TYPE::plane_mapping: + case ON_TextureMapping::TYPE::cylinder_mapping: + case ON_TextureMapping::TYPE::sphere_mapping: + case ON_TextureMapping::TYPE::box_mapping: + case ON_TextureMapping::TYPE::mesh_mapping_primitive: + case ON_TextureMapping::TYPE::srf_mapping_primitive: + case ON_TextureMapping::TYPE::brep_mapping_primitive: + case ON_TextureMapping::TYPE::ocs_mapping: + if (IsSet()) + { + bHashIdAndCRC = true; + if (false == m_mesh_xform.IsIdentity(ON_ZERO_TOLERANCE) && false == m_mesh_xform.IsZero() && m_mesh_xform.IsValid()) + bHashXform = true; + } + else + bHashType = false; // bogus mapping - treat as unset + break; + default: + // Perhaps somebody added a value to the enum after June 2020 and failed to update this code? + ON_ERROR("Invalid m_mapping_type value."); + break; + } + + ON_SHA1 sha1; + if (bHashType) + { + const unsigned char u = (unsigned char)m_mapping_type; + sha1.AccumulateBytes(&u,1); + } + if (bHashIdAndCRC) + { + sha1.AccumulateId(m_mapping_id); + sha1.AccumulateInteger32(m_mapping_crc); + } + if (bHashXform) + { + sha1.AccumulateTransformation(m_mesh_xform); + } + return sha1.Hash(); +} + + void ON_MappingTag::Default() { memset(this,0,sizeof(*this)); @@ -11058,6 +11589,36 @@ void ON_MappingTag::Default() m_mesh_xform.m_xform[3][3] = 1.0; } + +int ON_MappingTag::CompareAll(const ON_MappingTag& lhs, const ON_MappingTag& rhs) +{ + const unsigned lhs_type = static_cast(lhs.m_mapping_type); + const unsigned rhs_type = static_cast(rhs.m_mapping_type); + if (lhs_type < rhs_type) + return -1; + if (lhs_type > rhs_type) + return 1; + int rc = ON_UuidCompare(lhs.m_mapping_id, rhs.m_mapping_id); + if (0 != rc) + return rc; + if (lhs.m_mapping_crc < rhs.m_mapping_crc) + return -1; + if (lhs.m_mapping_crc > rhs.m_mapping_crc) + return 1; + return lhs.m_mesh_xform.Compare(rhs.m_mesh_xform); +} + +int ON_MappingTag::CompareAllFromPointer(const ON_MappingTag* lhs, const ON_MappingTag* rhs) +{ + if (lhs == rhs) + return 0; + if (nullptr == lhs) + return 1; + if (nullptr == rhs) + return -1; + return ON_MappingTag::CompareAll(*lhs, *rhs); +} + int ON_MappingTag::Compare( const ON_MappingTag& other, bool bCompareId, @@ -11072,7 +11633,10 @@ int ON_MappingTag::Compare( } if ( !rc && bCompareCRC ) { - rc = ((int)m_mapping_crc) - ((int)other.m_mapping_crc); + if (m_mapping_crc < other.m_mapping_crc) + return -1; + if (m_mapping_crc > other.m_mapping_crc) + return 1; } if ( !rc && bCompareXform ) { @@ -11381,6 +11945,104 @@ bool ON_V5_MeshDoubleVertices::Transform( const ON_Xform& xform ) return true; } +ON_Mesh* ON_Mesh::OffsetMesh(const double distance, const ON_3dVector& direction) const +{ + if (0.0 == distance) + return nullptr; + + ON_3fVector VN; + ON_3dVector N; + ON_3dPoint P; + + int k, tvi, vi; + + //Make the topology for the old mesh in the event that it does not already exist. + Topology(); + + ON_Mesh* new_mesh = Duplicate(); + if (nullptr == new_mesh) + return nullptr; + + if (!new_mesh->HasVertexNormals()) + new_mesh->ComputeVertexNormals(); + + // D = double precision vertices + ON_3dPoint* D = 0; + if (new_mesh->HasDoublePrecisionVertices()) + { + if (new_mesh->m_dV.Count() > 0) + D = new_mesh->m_dV.Array(); + else + new_mesh->DestroyDoublePrecisionVertices(); + } + + const ON_MeshTopology& top = new_mesh->Topology(); + + if (false == top.IsValid() || false == new_mesh->HasVertexNormals()) + { + delete new_mesh; + return nullptr; + } + + for (tvi = 0; tvi < top.m_topv.Count(); tvi++) + { + N = ON_3dVector::ZeroVector; + const ON_MeshTopologyVertex& topv = top.m_topv[tvi]; + if (direction == ON_3dVector::UnsetVector) + { + // Per-vertex offset direction + for (k = 0; k < topv.m_v_count; k++) + { + vi = topv.m_vi[k]; + VN = new_mesh->m_N[vi]; + N.x += VN.x; N.y += VN.y; N.z += VN.z; + } + } + else + { + // Single offset direction for all vertices + N = direction; + } + N.Unitize(); + N = distance * N; + for (k = 0; k < topv.m_v_count; k++) + { + vi = topv.m_vi[k]; + if (0 != D) + { + // double precision calculation + P = D[vi]; + P = P + N; + D[vi] = P; + } + else + { + // single precision calculation + P = new_mesh->m_V[vi]; + P = P + N; + new_mesh->m_V[vi] = P; + } + } + } + + if (0 != D) + { + new_mesh->UpdateSinglePrecisionVertices(); + } + + new_mesh->DestroyPartition(); + new_mesh->DestroyRuntimeCache(); + new_mesh->DestroyTopology(); + new_mesh->InvalidateVertexBoundingBox(); + new_mesh->InvalidateVertexNormalBoundingBox(); + new_mesh->InvalidateCurvatureStats(); + + return new_mesh; +} + + + + bool ON_Mesh::HasSynchronizedDoubleAndSinglePrecisionVertices() const { const unsigned int vertex_count = VertexUnsignedCount(); @@ -14090,6 +14752,17 @@ ON_MeshRef& ON_MeshRef::operator=(ON_MeshRef&& src) } #endif +bool ON_MeshRef::IsEmpty() const +{ + const ON_Mesh* mesh = m_mesh_sp.get(); + return (nullptr == mesh); +} + +bool ON_MeshRef::IsNotEmpty() const +{ + return (false == IsEmpty()); +} + const class ON_Mesh& ON_MeshRef::Mesh() const { const ON_Mesh* mesh = m_mesh_sp.get(); diff --git a/opennurbs_mesh.h b/opennurbs_mesh.h index eeababfb..a0ba1024 100644 --- a/opennurbs_mesh.h +++ b/opennurbs_mesh.h @@ -28,21 +28,28 @@ enum class ON_SubDComponentLocation : unsigned char { /// /// Not a valid component location and used to indicate the value is not initialized. + /// Note well: This value is saved in 3dm archives and cannot be changed. /// Unset = 0, /// /// The component's location in the SubD control net. + /// Note well: This value is saved in 3dm archives and cannot be changed. /// ControlNet = 1, /// /// The component's location in the SubD limit surface. + /// Note well: This value is saved in 3dm archives and cannot be changed. /// Surface = 2 }; #pragma endregion +ON_SubDComponentLocation ON_SubDComponentLocationFromUnsigned( + unsigned int loc_as_unsigned +); + ////////////////////////////////////////////////////////////////////////// // // ON_SubDDisplayParameters @@ -59,23 +66,89 @@ public: ON_SubDDisplayParameters(const ON_SubDDisplayParameters&) = default; ON_SubDDisplayParameters& operator=(const ON_SubDDisplayParameters&) = default; +public: + void Dump(class ON_TextLog&) const; + public: enum : unsigned int { - CourseDensity = 2, // (2^2 = 4, 4x4 = 16 quads per SubD quad) - DefaultDensity = 4, // (2^4 = 16, 16x16 = 256 quads per SubD quad) - MaximumDensity = 6 // (2^6 = 64, 64x64 = 4096 quads per SubD quad) + /// + /// Each SubD quad will generate 4 mesh quads in a 2x2 grid. + /// A simple meshing density of 0% corresponds to ExtraCoarseDensity. + /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. + /// + ExtraCoarseDensity = 1, + MinimumDensity = 1, + + /// + /// Each SubD quad will generate 16 mesh quads in a 4x4 grid. + /// A simple meshing density of 1% to 19% corresponds to CoarseDensity. + /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. + /// + CoarseDensity = 2, + + /// + /// Each SubD quad will generate 64 mesh quads in an 8x8 grid. + /// A simple meshing density of 20% to 34% corresponds to MediumDensity. + /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. + /// This density is too low to produce acceptable results on the SubD Rhino Logo and many other models. + /// + MediumDensity = 3, + + /// + /// Each SubD quad will generate 256 mesh quads in a 16x16 grid. + /// A simple meshing density of 35% to 75% corresponds to FineDensity. + /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. + /// This is the default value for creating mesh approximations of SubD surface. + /// It produces acceptable results on the SubD Rhino Logo and most other models. + /// + FineDensity = 4, + DefaultDensity = 4, + + /// + /// Each SubD quad will generate 1024 mesh quads in a 32x32 grid. + /// A simple meshing density of 76% to 99% corresponds to ExtraFineDensity. + /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. + /// + ExtraFineDensity = 5, + + /// + /// Each SubD quad will generate 4096 mesh quads in a 64x64 grid. + /// A simple meshing density of 100% corresponds to MaximumDensity. + /// SubD display density values may never exceed 6 and core code assumes these values are <= 6. + /// Use ON_SubDDisplayParameters::CreateFromMeshDensity() to convert normalized meshing density settings to ON_SubDDisplayParameters density enum values. + /// This value creates ridiculously dense meshes and should generally be avoided. + /// + MaximumDensity = 6 }; public: static const ON_SubDDisplayParameters Empty; - // Parameters for the default limit surface display mesh. - // m_display_density = ON_SubDMesh::DefaultDisplayDensity - static const ON_SubDDisplayParameters Course; + // Parameters for a course limit surface display mesh. + // m_display_density = ON_SubDDisplayParameters::ExtraCoarseDensity + static const ON_SubDDisplayParameters ExtraCoarse; + + // Parameters for a course limit surface display mesh. + // m_display_density = ON_SubDDisplayParameters::CoarseDensity + static const ON_SubDDisplayParameters Coarse; + + // Parameters for a medium limit surface display mesh. + // To crude for a quality rendering of the SubD Rhino logo. + // m_display_density = ON_SubDDisplayParameters::MediumDensity + static const ON_SubDDisplayParameters Medium; // Parameters for the default limit surface display mesh. - // m_display_density = ON_SubDMesh::DefaultDisplayDensity + // Produces and acceptable rendering of the SubD Rhino logo. + // m_display_density = ON_SubDDisplayParameters::FineDensity (default) + static const ON_SubDDisplayParameters Fine; + + // Parameters for an extra fine limit surface display mesh. + // m_display_density = ON_SubDDisplayParameters::ExtraFineDensity + static const ON_SubDDisplayParameters ExtraFine; + + // Parameters for the default limit surface display mesh. + // m_display_density = ON_SubDDisplayParameters::DefaultDensity static const ON_SubDDisplayParameters Default; /* @@ -83,19 +156,47 @@ public: In most applications, the caller sets the mesh_density and leaves the other parameters set to the default values. + Parameters: + subd_display_density - [in] + A value between ON_SubDDisplayParameters::MinimumDensity and ON_SubDDisplayParameters::MaximumDensity. */ static const ON_SubDDisplayParameters CreateFromDisplayDensity( - unsigned int display_density + unsigned int subd_display_density ); - // TODO - add functional interface and hide implementation + /* + Description: + This function creates ON_SubDDisplayParameters from a user interface + "slider" like Rhino's simple mesh controls. + Parameters: + normalized_mesh_density - [in] + A double between 0.0 and 1.0 + + The table below shows the correpondence between normalized_density and subd display density. + + Mesh density percentage / normalized_mesh_density / subd display density + 0% -> [0.0, ON_ZERO_TOLERANCE] -> 1 = MinimumDensity + 0% to 19% -> (ON_ZERO_TOLERANCE, 0.20) -> 2 = CoarseDensity + 20% to 34% -> [0.20, 0.35) -> 3 = MediumDensity + 35% to 75% -> [0.35, 0.75] -> 4 = FineDensity + 76% to 99% -> (0.75, 1 - ON_ZERO_TOLERANCE) -> 5 = ExtraFineDensity + 100% -> [1 - ON_ZERO_TOLERANCE, 1.0] -> 6 = MaximumDensity + + Invalid input -> ON_SubDDisplayParameters::DefaultDensity; + + Returns: + A valid ON_SubDDisplayParameters with the specified subd display denstity. + */ + static const ON_SubDDisplayParameters CreateFromMeshDensity( + double normalized_mesh_density + ); public: // 0 <= m_display_density <= ON_SubDDisplayParameters::MaximumDensity // If n = m_display_density, then each SubD quad face will have // a grid of 2^n x 2^n mesh quads for a total of 2^(2n) mesh quads. - // n grid size total number of mesh faces per SubD quad - // 0 1 x 1 1 + // n grid size total number of mesh quad faces per SubD quad face + // 0 1 x 1 1 (avoid 0 - it does not work for n-gons n != 4) // 1 2 x 2 4 // 2 4 x 4 16 // 3 8 x 8 64 @@ -125,22 +226,6 @@ public: */ void SetMeshLocation(ON_SubDComponentLocation mesh_location); - // By default, limit surface mesh quads for each subd face are grouped into - // a single ON_Mesh n-gon. - // If you don't want n-gons, then set m_bSkipMeshNgons = true; - bool AddNgons() const; - void SetAddNgons( - bool bAddNgons - ); - - // By default, limit surface mesh quads for each subd face are grouped into - // a single ON_Mesh n-gon. - // If you don't want n-gons, then set m_bSkipMeshNgons = true; - bool AddFakeEvaluationParameters() const; - void SetAddFakeEvaluationParameters( - bool bAddFakeEvaluationParameters - ); - unsigned char EncodeAsUnsignedChar() const; static const ON_SubDDisplayParameters DecodeFromUnsignedChar( unsigned char encoded_parameters @@ -153,8 +238,8 @@ private: // and the value of m_subd_mesh_parameters is saved in 3dm archives. subd_mesh_density_mask = 0x07, subd_mesh_location_bit = 0x08, - subd_mesh_skip_ngons_bit = 0x10, - subd_mesh_skip_fakeevalparams_bit = 0x20, + + // If this bit set, then the settings are not current defaults. subd_mesh_nondefault_bit = 0x80 }; @@ -176,31 +261,52 @@ private: // If m_bControlNetMesh is true, a mesh of the subdivided control net is produced. bool m_bControlNetMesh = false; - // By default, limit surface mesh quads for each subd face are grouped into - // a single ON_Mesh n-gon. - // If you don't want n-gons, then set m_bSkipMeshNgons = true; - bool m_bSkipMeshNgons = false; +public: + enum class Context : unsigned char + { + /// + /// Unknown, unspecified, or unset context. This is typical. + /// + Unknown = 0, - // By default, fake evaluation parameters are assigned to mesh - // vertex locations and used to set normalized texture coordinates. - // If you don't want fake evaluation parameters, then set m_bSkipFakeEvaluationParameters = true; - bool m_bSkipFakeEvaluationParameters = false; + /// + /// These parameters are being used to generate a quad mesh approximations of an ON_SubD. + /// Low level meshing code copies input paramters and specifies this context when appropriate. + /// + SubDToMesh = 1, + + /// + /// These parameters are being used to generate NURBS surface approximations of an ON_SubD. + /// Low level conversion to NURB code copies input paramters and specifies this context when appropriate. + /// + SubDToNURBS = 2 + }; + + + /* + Description: + Low level mesh creation and ON_SubD to NURBS conversion code + occasional looks at the context. Typically it is set in a local + copy and no user of the top level ON_SubD SDK needs to be concerned + about the context setting. + This setting is not saved in 3dm archives and is ingnored by all compare functions. + */ + ON_SubDDisplayParameters::Context ContextForExperts() const; + + void SetContextForExperts(ON_SubDDisplayParameters::Context context); private: + ON_SubDDisplayParameters::Context m_context = ON_SubDDisplayParameters::Context::Unknown; + unsigned char m_reserved2 = 0; - unsigned int m_reserved3 = 0; + unsigned char m_reserved3 = 0; unsigned int m_reserved4 = 0; - ON__UINT_PTR m_reserved5 = 0; - double m_reserved6 = 0.0; + unsigned int m_reserved5 = 0; + ON__UINT_PTR m_reserved6 = 0; + double m_reserved7 = 0.0; - - // TODO - split this class into two - what's above and one derived from that with what's below. public: - bool UseMultipleThreads() const; - void SetUseMultipleThreads( - bool bUseMultipleThreads - ); - + // TODO - split this class into two - what's above and one derived from that with what's below. ON_Terminator* Terminator() const; void SetTerminator( ON_Terminator* terminator @@ -213,8 +319,9 @@ public: ON_Interval progress_reporter_interval ); -private: - bool m_bUseMultipleThreads = false; +public: + bool Write(class ON_BinaryArchive& archive) const; + bool Read(class ON_BinaryArchive& archive); private: ON_Terminator* m_terminator = nullptr; @@ -362,6 +469,76 @@ public: static const ON_MeshParameters DefaultAnalysisMesh; + /* + Returns: + "Fast" if this and ON_MeshParameters::FastRenderMesh have the same geometry settings. + "Quality" if this and ON_MeshParameters::QualityRenderMesh have the same geometry settings. + "Density(p%)" if this and ON_MeshParameters::CreateFromMeshDensity(p/100.0) have the same geometry settings. + "Default" if this and ON_MeshParameters::DefaultMesh have the same geometry settings. + "DefaultAnalysis" if this and ON_MeshParameters::DefaultAnalysis have the same geometry settings. + Otherwise, "Custom(SHA1)" where SHA1 = this->GeometryHash(); + */ + const ON_wString Description() const; + + + /* + Description: + This function creates ON_MeshParameters from a user interface + "slider" like Rhino's simple mesh controls. + Parameters: + normalized_density - [in] + A double between 0.0 and 1.0. + 0.0 creates meshes with fewer faces than 1.0. + + Invalid input is treated as 0.5. + Returns: + A valid ON_MeshParameters with the specified subd display denstity. + */ + static const ON_MeshParameters CreateFromMeshDensity( + double normalized_density + ); + + /* + Returns: + If these mesh parameters were created from ON_MeshParameters::CreateFromMeshDensity(normalized_mesh_density), + then normalized_mesh_density is returned. + Otherwise, ON_DBL_QNAN is returned. + Remarks: + The values of m_bDoublePrecision, m_bClosedObjectPostProcess, and m_texture_range can be arbitrary + because they do not determine geometry of the resulting mesh and are typically ingored. + You must compare these properties if they matter in your particular context. + */ + double MeshDensity() const; + + /* + Description: + Convert a mesh density value to a percentage with finite precision fuzz removed. + Parameters: + normalized_density - [in] + valid input is 0.0 <= normalized_density <= 1.0 + Returns: + If normalized_density is valid, 100*normalized_density with fuzz cleaned up is returned. + Otherwise ON_DBL_QNAN is returned. + */ + static double MeshDensityAsPercentage(double normalized_density); + + /* + Description: + This function cleans up normalized_density used in + ON_MeshParameters::CreateFromMeshDensity() + and ON_SubDDisplayParameters CreateFromMeshDensity(). + Parameters: + normalized_density - [in] + should be close to being between 0 and 1. + Returns: + if normalized_density is between 0.0 and 1.0, that value is returned. + If normalized_density is a hair smaller than 0.0, then 0.0 is returned. + If normalized_density is a hair bigger than 1.0, then 1.0 is returned. + Otherwise 0.5 is returned. + */ + static double ClampMeshDensityValue(double normalized_density); + + /* Description: Get a value to use for tolerance based on the relative_tolerance @@ -402,17 +579,23 @@ public: /* Description: - Tool for provding a simple slider interface. + Tool for provding a simple "slider" interface. Parameters: - density - [in] 0.0 <= density <= 1.0 + mesh_density - [in] 0.0 <= density <= 1.0 0 quickly creates coarse meshes. 1 slowly creates dense meshes. min_edge_length - [in] > 0.0 custom value ON_UNSET_VALUE: for default (0.0001) + Remarks: + If you are using a user interface "slider" to set mesh parameters, + then you are strongly encouraged to call ON_MeshParameters::CreateFromMeshDensity() + instead of using this constructor. + ON_MeshParameters::CreateFromMeshDensity() handles out of bounds input + in a predictable way and is easier to search for when examining code. */ ON_MeshParameters( - double density, + double mesh_density, double min_edge_length = ON_UNSET_VALUE ); @@ -441,7 +624,23 @@ public: const ON_MeshParameters& b ); + /* + Returns: + A hash of every ON_MeshParameters setting. + Remarks: + The hash intentionally ignores m_bCustomSettingsEnabled or m_bDoublePrecision. + */ ON_SHA1_Hash ContentHash() const; + + /* + Returns: + A hash of values that control mesh geometry. + Remarks: + Teh has intentionally ignores + m_bCustomSettings, m_bCustomSettingsEnabled, m_bComputeCurvature, + m_bDoublePrecision, m_bClosedObjectPostProcess, m_texture_range. + If you need to include those values, call ContentHash(). + */ ON_SHA1_Hash GeometrySettingsHash() const; ON_UUID MesherId() const; @@ -449,7 +648,6 @@ public: ON_UUID ); - /* Returns: ON_MeshParameters::render_mesh_fast @@ -472,6 +670,10 @@ public: (0 == ON_MeshParameters::CompareGeometrySettings(*this,ON_MeshParameters(n/100.0)) no_match_found_result: otherwise + Remarks: + This is a legacy function with roots dating back to Rhino 1.0. + High quality code should use ON_MeshParameters::MeshDensity() or + ON_MeshParameters::MeshDensityAsPercentage(ON_MeshParameters::MeshDensity()). */ const int GeometrySettingsDensityPercentage( int no_match_found_result @@ -790,7 +992,7 @@ private: // Uses ON_SubDDisplayParameters::EncodeAsUnsignedChar() / ON_SubDDisplayParameters::DecodeFromUnsignedChar() // to save ON_SubDDisplayParameters settings in this class. // (Done this way to avoid breaking the C++ public SDK.) - unsigned char m_subd_mesh_parameters = 0; + unsigned char m_subd_mesh_parameters_as_char = 0; int m_grid_min_count = 0; int m_grid_max_count = 0; @@ -845,10 +1047,15 @@ private: // ////////////////////////////////////////////////////////// private: + // This value is perminantly reserved for use by ON_SubD core code + // that is part of public opennurbs. unsigned int m_subd_stuff_reserved5 = 0; private: - ON__UINT_PTR m_reserved6 = 0; + // NOTE WELL: This value cannot become a managed pointer. + // Lots of legacy code manages ON_MeshParameter values in ways + // that are incompatible with this class having a managed pointer. + ON__UINT64 m_reserved6 = 0; }; ON_DECL @@ -1711,6 +1918,35 @@ public: ON_SimpleArray& ngon_vi ); + /* + Description: + Get a list of vertices that form the boundary of a set of faces. + Parameters: + mesh_vertex_list - [in] + mesh_face_list - [in] + vertex_face_map - [in] + null or a vertex map made from the information in + mesh_vertex_list and mesh_face_list. + ngon_fi_count - [in] + length of ngon_fi[] array + ngon_fi - [in] + An array of length ngon_fi_count that contains the indices + of the faces that form the ngon. + ngon_vi - [out] + An array of vertex indices that make the ngon boundary. + Returns: + Number of vertices in the ngon outer boundary or 0 if the input is + not valid. + */ + static unsigned int FindNgonOuterBoundary( + const class ON_3dPointListRef& mesh_vertex_list, + const class ON_MeshFaceList& mesh_face_list, + ON_MeshVertexFaceMap* vertex_face_map, + size_t ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray& ngon_vi + ); + /* Description: Get a list of vertices that form any boundary of a set of faces. @@ -1741,6 +1977,36 @@ Returns: ON_SimpleArray& ngon_vi ); + /* + Description: + Get a list of vertices that form any boundary of a set of faces. + This includes inner boundaries. + Parameters: + mesh_vertex_list - [in] + mesh_face_list - [in] + vertex_face_map - [in] + null or a vertex map made from the information in + mesh_vertex_list and mesh_face_list. + ngon_fi_count - [in] + length of ngon_fi[] array + ngon_fi - [in] + An array of length ngon_fi_count that contains the indices + of the faces that form the ngon. + ngon_vi - [out] + An array of vertex indices that make the ngon boundary. + Returns: + Number of vertices in the ngon outer boundary or 0 if the input is + not valid. + */ + static unsigned int FindNgonBoundary( + const class ON_3dPointListRef& mesh_vertex_list, + const class ON_MeshFaceList& mesh_face_list, + ON_MeshVertexFaceMap* vertex_face_map, + size_t ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray& ngon_vi + ); + /* Description: Create an ngon pointer that contains a triangle (3-gon) @@ -2463,6 +2729,9 @@ public: bool bCompareXform = true ) const; + static int CompareAll(const ON_MappingTag& lhs, const ON_MappingTag& rhs); + static int CompareAllFromPointer(const ON_MappingTag* lhs, const ON_MappingTag* rhs); + /* Returns: True if the mapping tag is set. @@ -2487,8 +2756,33 @@ public: // ON_UUID m_mapping_id = ON_nil_uuid; // ON_TextureMapping::m_mapping_id ON_TextureMapping::TYPE m_mapping_type = ON_TextureMapping::TYPE::no_mapping; // ON_TextureMapping::m_type - ON__UINT32 m_mapping_crc = 0; // ON_TextureMapping::MappingCRC() + ON__UINT32 m_mapping_crc = 0; // ON_TextureMapping::MappingCRC() (from decades ago - a sha1 would be better when SDK can break) ON_Xform m_mesh_xform = ON_Xform::IdentityTransformation; + + /* + Returns: + World space transformation to apply to the object when using this mapping. + */ + const ON_Xform Transform() const; + + /* + Returns: + True if Transform() will return the identity transformation. + */ + bool TransformIsIdentity() const; + + + /* + Returns: + True if ON_MappingTag will consider xform to be the identity transformation. + */ + static bool TransformTreatedIsIdentity(const ON_Xform* xform); + + /* + Returns: + A sha1 hash the identifies the mapping tag. + */ + const ON_SHA1_Hash Hash() const; }; class ON_CLASS ON_TextureCoordinates @@ -3213,10 +3507,39 @@ Returns: */ bool SetTextureCoordinates( const class ON_TextureMapping& mapping, - const class ON_Xform* mesh_xform = 0, + const class ON_Xform* mesh_xform = 0, bool bLazy = true ); + /* + Description: + Use a texture mapping function to set the m_T[] values. + Parameters: + mapping - [in] + mesh_xform - [in] + If not nullptr, the mapping calculation is performed as + if the mesh were transformed by mesh_xform; the + location of the mesh is not changed. + bLazy - [in] + If true and the m_T[] values were set using the same + mapping parameters, then no calculation is performed. + bSeamCheck - [in] + If true then some mesh edges might be unwelded to better + represent UV discontinuities in the texture mapping. + This only happens for the following mappings: + Box, Sphere, Cylinder. + Returns: + True if successful. + See Also: + ON_TextureMapping::GetTextureCoordinates + */ + bool SetTextureCoordinatesEx( + const class ON_TextureMapping& mapping, + const class ON_Xform* mesh_xform = 0, + bool bLazy = true, + bool bSeamCheck = true + ); + bool HasCachedTextureCoordinates() const; const ON_TextureCoordinates* CachedTextureCoordinates( @@ -3225,10 +3548,17 @@ Returns: const ON_TextureCoordinates* SetCachedTextureCoordinates( const class ON_TextureMapping& mapping, - const class ON_Xform* mesh_xform = 0, + const class ON_Xform* mesh_xform = 0, bool bLazy = true ); + const ON_TextureCoordinates* SetCachedTextureCoordinatesEx( + const class ON_TextureMapping& mapping, + const class ON_Xform* mesh_xform = 0, + bool bLazy = true, + bool bSeamCheck = true + ); + bool EvaluateMeshGeometry( const ON_Surface& ); // evaluate surface at tcoords // to set mesh geometry @@ -3813,6 +4143,27 @@ Returns: ON_SimpleArray* components ) const; + ///////////////////////////////////////////////////////////////// + // + // Mesh offset + // + + /* + Description: + Offsets a mesh by the input distance + + distance - [in] + Distance to offset + direction - [in] + If this parameter is ON_3dVector::UnsetVector, offset each vertex in the normal direction + orherwise, offset every vertex in the input direction + Returns: + New mesh that is an offset of a duplicate of this mesh + Or nullptr if the input was invalid or the mesh could not be duplicated or offset + Caller manages memory of new mesh + */ + ON_Mesh* OffsetMesh(const double distance, const ON_3dVector& direction) const; + ///////////////////////////////////////////////////////////////// // @@ -4079,6 +4430,35 @@ Returns: bool bPermitHoles ); + /* + Description: + Add a new ngon to the mesh. + WARNING! The usage of this particular overload is discouraged unless + its usage is critical for performance. If vertexFaceMap is set to something + other than nullptr, then the value will be filled and can be re-fed to the + function. + Parameters: + Fcount - [in] + Number of face that make up the ngon. + ngon_fi[] + An array of N distinct ON_Mesh.m_F[] face indices referencing + a set of connected faces. + bPermitHoles + If true, also ngons that contain inner boundaries are allowed. + faceVertexMap + If nullptr, this will be set to a new faceVertexMap. Must be freed with onfree(). + It is responsability of the user to call onfree() on the created object. + Returns: + index of the new n-gon. + -1: If input information is not valid. +*/ + int AddNgon_Expert( + unsigned int Fcount, + const unsigned int* ngon_fi, + bool bPermitHoles, + ON_MeshVertexFaceMap* vertexFaceMap + ); + /* Description: @@ -4832,6 +5212,29 @@ public: ON_MeshRef& operator=( ON_MeshRef&& ); #endif + /* + Returns: + True if no ON_Mesh is being managed by this ON_MeshRef. + Remarks: + It is alwasy the case that exactly one of IsEmpty() and IsNotEmpty() is true. + Both are provided so code using ON_MeshRef can be clean and easily read. + */ + bool IsEmpty() const; + + /* + Returns: + True if an ON_Mesh is being managed by this ON_MeshRef. + Remarks: + It is alwasy the case that exactly one of IsEmpty() and IsNotEmpty() is true. + Both are provided so code using ON_MeshRef can be clean and easily read. + */ + bool IsNotEmpty() const; + + /* + Returns: + The mesh being managed by this ON_MeshRef. + If this ON_MeshRef is not managing an ON_Mesh, then ON_Mesh::Empty is returned. + */ const class ON_Mesh& Mesh() const; /* diff --git a/opennurbs_mesh_ngon.cpp b/opennurbs_mesh_ngon.cpp index bf397b2d..ad8ae61c 100644 --- a/opennurbs_mesh_ngon.cpp +++ b/opennurbs_mesh_ngon.cpp @@ -1197,6 +1197,17 @@ int ON_Mesh::AddNgon( const unsigned int* ngon_fi, bool bPermitHoles ) +{ + ON_MeshVertexFaceMap* vertexFaceMap = nullptr; + return AddNgon_Expert(Fcount, ngon_fi, bPermitHoles, vertexFaceMap); +} + +int ON_Mesh::AddNgon_Expert( + unsigned int Fcount, + const unsigned int* ngon_fi, + bool bPermitHoles, + ON_MeshVertexFaceMap* vertexFaceMap +) { unsigned int ngon_index = ON_UNSET_UINT_INDEX; if (Fcount < 1 || nullptr == ngon_fi) @@ -1205,7 +1216,6 @@ int ON_Mesh::AddNgon( ON_SimpleArray ngon_vi; const ON_3dPointListRef mesh_vertex_list(this); const ON_MeshFaceList& mesh_face_list(this); - const unsigned int *const* vertex_face_map = nullptr; const int face_count = m_F.Count(); const unsigned int ngon_count0 = HasNgons() ? NgonUnsignedCount() : 0U; @@ -1224,7 +1234,7 @@ int ON_Mesh::AddNgon( vi_count = ON_MeshNgon::FindNgonBoundary( mesh_vertex_list, mesh_face_list, - vertex_face_map, + vertexFaceMap, Fcount, ngon_fi, ngon_vi @@ -1233,7 +1243,7 @@ int ON_Mesh::AddNgon( vi_count = ON_MeshNgon::FindNgonOuterBoundary( mesh_vertex_list, mesh_face_list, - vertex_face_map, + vertexFaceMap, Fcount, ngon_fi, ngon_vi @@ -3444,6 +3454,7 @@ static unsigned int SetFaceNeighborMap( unsigned int mesh_vertex_count, const ON_MeshFaceList& mesh_face_list, const unsigned int *const* vertex_face_map, + ON_MeshVertexFaceMap* vertex_face_map_obj, unsigned int face_index_count, const unsigned int* face_index_list, struct NgonNeighbors* face_nbr_map @@ -3463,15 +3474,30 @@ static unsigned int SetFaceNeighborMap( if ( face_index_count <= 0 || 0 == face_index_list || 0 == face_nbr_map ) return 0; - ON_MeshVertexFaceMap vf_map; - if ( nullptr == vertex_face_map ) + ON_MeshVertexFaceMap vf_tmp; + ON_MeshVertexFaceMap* vf_map = &vf_tmp; + + const unsigned int* const* localVertexFaceMap = vertex_face_map; + if (nullptr == localVertexFaceMap) { - if ( !vf_map.SetFromFaceList(mesh_vertex_count,mesh_face_list,false) ) + if (vertex_face_map_obj != nullptr) + { + localVertexFaceMap = vertex_face_map_obj->VertexFaceMap(); + } + } + if (vertex_face_map_obj != nullptr && vertex_face_map == nullptr) + { + vf_map = vertex_face_map_obj; + } + + if (nullptr == localVertexFaceMap) + { + if ( !vf_map->SetFromFaceList(mesh_vertex_count,mesh_face_list,false) ) return 0; - vertex_face_map = vf_map.VertexFaceMap(); - if ( 0 == vertex_face_map ) + localVertexFaceMap = vf_map->VertexFaceMap(); + if ( 0 == localVertexFaceMap) return 0; - mesh_vertex_count = vf_map.VertexCount(); + mesh_vertex_count = vf_map->VertexCount(); } memset(face_nbr_map,0,face_index_count*sizeof(face_nbr_map[0])); @@ -3483,7 +3509,7 @@ static unsigned int SetFaceNeighborMap( mesh_face_list.QuadFvi(face_index,Fvi); viB = Fvi[0]; - face_listB = ( viB <= mesh_vertex_count ) ? vertex_face_map[viB] : 0; + face_listB = ( viB <= mesh_vertex_count ) ? localVertexFaceMap[viB] : 0; if ( 0 != face_listB && face_listB[0] <= 1 ) face_listB = 0; @@ -3497,7 +3523,7 @@ static unsigned int SetFaceNeighborMap( boundary_count++; face_listA = face_listB; - face_listB = ( viB <= mesh_vertex_count ) ? vertex_face_map[viB] : 0; + face_listB = ( viB <= mesh_vertex_count ) ? localVertexFaceMap[viB] : 0; if ( 0 == face_listB ) continue; if ( face_listB[0] <= 1 ) @@ -4015,6 +4041,7 @@ static unsigned int FindNgonBoundary_Helper( const ON_3dPointListRef& mesh_vertex_list, const ON_MeshFaceList& mesh_face_list, const unsigned int *const* vertex_face_map, + ON_MeshVertexFaceMap* vertex_face_map_obj, size_t ngon_fi_count, const unsigned int* ngon_fi, ON_SimpleArray& ngon_vi, @@ -4041,6 +4068,7 @@ static unsigned int FindNgonBoundary_Helper( mesh_vertex_count, mesh_face_list, vertex_face_map, + vertex_face_map_obj, (unsigned int)ngon_fi_count, ngon_fi, ngon_nbr_map.Array() @@ -4087,6 +4115,28 @@ unsigned int ON_MeshNgon::FindNgonOuterBoundary( mesh_vertex_list, mesh_face_list, vertex_face_map, + nullptr, + ngon_fi_count, + ngon_fi, + ngon_vi, + true + ); +} + +unsigned int ON_MeshNgon::FindNgonOuterBoundary( + const ON_3dPointListRef& mesh_vertex_list, + const ON_MeshFaceList& mesh_face_list, + ON_MeshVertexFaceMap* vertex_face_map, + size_t ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray& ngon_vi +) +{ + return FindNgonBoundary_Helper( + mesh_vertex_list, + mesh_face_list, + nullptr, + vertex_face_map, ngon_fi_count, ngon_fi, ngon_vi, @@ -4107,6 +4157,28 @@ unsigned int ON_MeshNgon::FindNgonBoundary( mesh_vertex_list, mesh_face_list, vertex_face_map, + nullptr, + ngon_fi_count, + ngon_fi, + ngon_vi, + false + ); +} + +unsigned int ON_MeshNgon::FindNgonBoundary( + const ON_3dPointListRef& mesh_vertex_list, + const ON_MeshFaceList& mesh_face_list, + ON_MeshVertexFaceMap* vertex_face_map, + size_t ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray& ngon_vi +) +{ + return FindNgonBoundary_Helper( + mesh_vertex_list, + mesh_face_list, + nullptr, + vertex_face_map, ngon_fi_count, ngon_fi, ngon_vi, @@ -4142,6 +4214,7 @@ unsigned int ON_MeshNgon::GetBoundarySides( mesh_vertex_count, mesh_face_list, vertex_face_map, + nullptr, ngon_fi_count, ngon_fi, ngon_nbr_map.Array() @@ -4193,7 +4266,7 @@ unsigned int ON_Mesh::GetNgonOuterBoundary( ON_MeshFaceList mesh_face_list; mesh_vertex_list.SetFromMesh(this); mesh_face_list.SetFromMesh(this); - return ON_MeshNgon::FindNgonOuterBoundary(mesh_vertex_list,mesh_face_list,0,ngon_fi_count,ngon_fi,ngon_vi); + return ON_MeshNgon::FindNgonOuterBoundary(mesh_vertex_list,mesh_face_list,(ON_MeshVertexFaceMap*)nullptr,ngon_fi_count,ngon_fi,ngon_vi); } static diff --git a/opennurbs_nurbscurve.h b/opennurbs_nurbscurve.h index 6952f343..fff46b8f 100644 --- a/opennurbs_nurbscurve.h +++ b/opennurbs_nurbscurve.h @@ -982,7 +982,8 @@ public: bool ZeroCVs(); // zeros control vertices and, if rational, sets weights to 1 // Description: - // Clamp end knots. Does not modify control points. + // Clamp end knots. Does not modify the curve location, but can modify + // knots and control verticies near the ends. // Parameters: // end - [in] 0 = clamp start, 1 = clamp end, 2 = clamp start and end // Returns: diff --git a/opennurbs_nurbssurface.cpp b/opennurbs_nurbssurface.cpp index cb98740c..cabb73ed 100644 --- a/opennurbs_nurbssurface.cpp +++ b/opennurbs_nurbssurface.cpp @@ -2370,10 +2370,8 @@ bool ON_NurbsSurface::IsNatural( return bIsNatural; } -static void ConvertToCurve( const ON_NurbsSurface& srf, int dir, ON_NurbsCurve& crv ) -{ - // DO NOT MAKE THIS FUNCTION PUBLIC - IT IS DELICATE AND DEDICATED TO USE IN THIS FILE - +void ON_NurbsSurface::ON_Internal_ConvertToCurve(const ON_NurbsSurface& srf, + int dir, ON_NurbsCurve& crv) { crv.DestroyCurveTree(); if (dir) dir = 1; @@ -2423,10 +2421,8 @@ static void ConvertToCurve( const ON_NurbsSurface& srf, int dir, ON_NurbsCurve& } } -static void ConvertFromCurve( ON_NurbsCurve& crv, int dir, ON_NurbsSurface& srf ) -{ - // DO NOT MAKE THIS FUNCTION PUBLIC - IT IS DELICATE AND DEDICATED TO USE IN THIS FILE - +void ON_NurbsSurface::ON_Internal_ConvertFromCurve(ON_NurbsCurve& crv, int dir, + ON_NurbsSurface& srf) { crv.DestroyCurveTree(); srf.DestroySurfaceTree(); if (dir) @@ -2494,9 +2490,9 @@ bool ON_NurbsSurface::ClampEnd( dir = 1; ON_NurbsCurve crv; crv.m_knot = m_knot[dir]; - ConvertToCurve(*this,dir,crv); + ON_Internal_ConvertToCurve(*this,dir,crv); bool rc = crv.ClampEnd(end); - ConvertFromCurve(crv,dir,*this); + ON_Internal_ConvertFromCurve(crv,dir,*this); return rc; } @@ -2526,9 +2522,9 @@ bool ON_NurbsSurface::InsertKnot( m_knot_capacity[dir] = 0; crv.ReserveKnotCapacity(CVCount(dir)+knot_multiplicity); - ConvertToCurve(*this,dir,crv); - rc = crv.InsertKnot(knot_value,knot_multiplicity); - ConvertFromCurve(crv,dir,*this); + ON_Internal_ConvertToCurve(*this, dir, crv); + rc = crv.InsertKnot(knot_value, knot_multiplicity); + ON_Internal_ConvertFromCurve(crv, dir, *this); } } @@ -2680,9 +2676,9 @@ bool ON_NurbsSurface::IncreaseDegree( m_knot[dir] = 0; m_knot_capacity[dir] = 0; - ConvertToCurve(*this,dir,crv); + ON_Internal_ConvertToCurve(*this, dir, crv); rc = crv.IncreaseDegree(desired_degree); - ConvertFromCurve(crv,dir,*this); + ON_Internal_ConvertFromCurve(crv, dir, *this); } } @@ -2819,8 +2815,7 @@ bool ON_MakeDomainsCompatible( return rc; } -static -bool ON_MakeKnotVectorsCompatible( +bool ON_NurbsSurface::ON_Internal_MakeKnotVectorsCompatible( ON_NurbsCurve& nurbs_curveA, ON_NurbsCurve& nurbs_curveB ) @@ -2975,7 +2970,7 @@ int ON_NurbsSurface::CreateRuledSurface( if ( rcB<=0 ) return 0; - if ( !ON_MakeKnotVectorsCompatible( nurbs_curveA, nurbs_curveB ) ) + if ( !ON_Internal_MakeKnotVectorsCompatible( nurbs_curveA, nurbs_curveB ) ) return false; if ( nurbs_curveA.m_cv_count != nurbs_curveB.m_cv_count ) diff --git a/opennurbs_nurbssurface.h b/opennurbs_nurbssurface.h index 77d9ebb2..b989715e 100644 --- a/opennurbs_nurbssurface.h +++ b/opennurbs_nurbssurface.h @@ -1086,6 +1086,19 @@ public: const ON_ClassArray>& v_Tangents, const ON_ClassArray>& TwistVectors, class ON_NurbsSurface* hermite_surface = 0); + + + +public: +#if defined(ON_COMPILING_OPENNURBS) + + static void ON_Internal_ConvertToCurve(const ON_NurbsSurface& srf, int dir, + ON_NurbsCurve& crv); + static void ON_Internal_ConvertFromCurve(ON_NurbsCurve& crv, int dir, + ON_NurbsSurface& srf); + static bool ON_Internal_MakeKnotVectorsCompatible( + ON_NurbsCurve& nurbs_curveA, ON_NurbsCurve& nurbs_curveB); +#endif // ON_COMPILING_OPENNURBS ///////////////////////////////////////////////////////////////// diff --git a/opennurbs_object_history.cpp b/opennurbs_object_history.cpp index b10dc802..e05a7438 100644 --- a/opennurbs_object_history.cpp +++ b/opennurbs_object_history.cpp @@ -1567,7 +1567,7 @@ bool ON_SubDEdgeChainHistoryValue::WriteHelper(ON_BinaryArchive& archive) const // virtual bool ON_SubDEdgeChainHistoryValue::ReportHelper(ON_TextLog& text_log) const { - text_log.Print("subd edge chain value\n"); + text_log.Print("SubD edge chain value\n"); text_log.PushIndent(); int i, count = m_value.Count(); for (i = 0; i < count; i++) @@ -1626,7 +1626,7 @@ ON_Value* ON_Value::CreateValue( int value_type ) value = new ON_ObjRefValue(); break; case geometry_value: - value = new ON_PolyEdgeHistoryValue(); + value = new ON_GeometryValue(); break; case uuid_value: value = new ON_UuidValue(); diff --git a/opennurbs_point.cpp b/opennurbs_point.cpp index 4c3ebfbf..2712fdc7 100644 --- a/opennurbs_point.cpp +++ b/opennurbs_point.cpp @@ -299,6 +299,45 @@ int ON_3dPoint::Compare( return Internal_DoubleArrayCompare(3, &lhs.x, &rhs.x); } +const ON_2dPoint ON_2dPoint::Midpoint(const ON_2dPoint& A, const ON_2dPoint& B) +{ + // avoids overflow and exact when coordinates are equal + return ON_2dPoint( + A.x == B.x ? A.x : (0.5 * A.x + 0.5 * B.x), + A.y == B.y ? A.y : (0.5 * A.y + 0.5 * B.y) + ); +} + +const ON_3dPoint ON_3dPoint::Midpoint(const ON_3dPoint& A, const ON_3dPoint& B) +{ + // avoids overflow and exact when coordinates are equal + return ON_3dPoint( + A.x == B.x ? A.x : (0.5 * A.x + 0.5 * B.x), + A.y == B.y ? A.y : (0.5 * A.y + 0.5 * B.y), + A.z == B.z ? A.z : (0.5 * A.z + 0.5 * B.z) + ); +} + + +const ON_2fPoint ON_2fPoint::Midpoint(const ON_2fPoint& A, const ON_2fPoint& B) +{ + // avoids overflow and exact when coordinates are equal + return ON_2fPoint( + A.x == B.x ? A.x : (0.5f * A.x + 0.5f * B.x), + A.y == B.y ? A.y : (0.5f * A.y + 0.5f * B.y) + ); +} + +const ON_3fPoint ON_3fPoint::Midpoint(const ON_3fPoint& A, const ON_3fPoint& B) +{ + // avoids overflow and exact when coordinates are equal + return ON_3fPoint( + A.x == B.x ? A.x : (0.5f * A.x + 0.5f * B.x), + A.y == B.y ? A.y : (0.5f * A.y + 0.5f * B.y), + A.z == B.z ? A.z : (0.5f * A.z + 0.5f * B.z) + ); +} + int ON_4dPoint::ProjectiveCompare( const ON_4dPoint& lhs, const ON_4dPoint& rhs @@ -515,6 +554,12 @@ ON_Interval::Min() const return ON_DBL_QNAN; } +const ON_Interval ON_Interval::Singleton(double t) +{ + return ON_Interval(t, t); +} + + void ON_Interval::Destroy() { *this = ON_Interval::EmptyInterval; @@ -669,6 +714,21 @@ ON_Interval::Includes( const ON_Interval& other, bool bProperSubSet ) const return rc; } +int +ON_Interval::Clamp(double& v) const +{ + int rval = 0; + if (v < m_t[0]) { + v = m_t[0]; + rval = -1; + } + else if (v > m_t[1]) { + v = m_t[1]; + rval = 1; + } + return rval; +} + bool ON_Interval::IntervalsOverlap(const ON_Interval& A, const ON_Interval& B) @@ -937,6 +997,13 @@ bool ON_Interval::Union( // this = union of two args } +bool ON_Interval::Expand(double delta) +{ + m_t[0] -= delta; + m_t[1] += delta; + return IsIncreasing(); +} + bool ON_3dVector::Decompose( // Computes a, b, c such that this vector = a*X + b*Y + c*Z // // If X,Y,Z is known to be an orthonormal frame, @@ -6457,11 +6524,13 @@ bool ON_3dVector::IsZero() const bool ON_3dVector::IsNotZero() const { - // the && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE) insures no coordinate is a Nan. + // the UNSET tests also insure x, y, and z are not nans. return (x != 0.0 || y != 0.0 || z != 0.0) - && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE) - && (x != ON_UNSET_POSITIVE_VALUE && y != ON_UNSET_POSITIVE_VALUE && z != ON_UNSET_POSITIVE_VALUE); + && x > ON_UNSET_VALUE && x < ON_UNSET_POSITIVE_VALUE + && y > ON_UNSET_VALUE && y < ON_UNSET_POSITIVE_VALUE + && z > ON_UNSET_VALUE && z < ON_UNSET_POSITIVE_VALUE + ; } bool ON_3dVector::IsUnitVector() const diff --git a/opennurbs_point.h b/opennurbs_point.h index 231757d1..b02557ff 100644 --- a/opennurbs_point.h +++ b/opennurbs_point.h @@ -60,6 +60,12 @@ public: ON_Interval(double t0,double t1); + /* + Returns: + ON_Interval(t,t). + */ + static const ON_Interval Singleton(double t); + public: // Interval = (m_t[0], m_t[1]) double m_t[2]; @@ -203,6 +209,20 @@ public: bool bProperSubSet = false ) const; + /* + Description: + value is clamped to the range of this interval. + Assumes this interval is not Decreasing(). + Parameters: + value -[in/out] value is set to x in interval + such that |x - value|<= |x - y| for all y in this interval + Returns: + -1 if valueMax() + */ + int Clamp(double& value) const; + /* Description: Test a pair of interval to see if they have a non-empty intersection. @@ -282,6 +302,13 @@ public: const ON_Interval&, const ON_Interval& ); + + ///////////////////////// + // Expand the interval by adding delta to m_t[1] and subtracting + // it from m_t[0]. So, when delta is positive and the interval is + // increasing this function expands the interval on each side. + // returns true if the result is increasing. + bool Expand(double delta); }; @@ -316,6 +343,15 @@ public: const ON_2dPoint& rhs ); + /* + Returns: + (A+B)/2 + Remarks: + Exact when coordinates are equal and prevents overflow. + */ + static const ON_2dPoint Midpoint(const ON_2dPoint& A, const ON_2dPoint& B); + + explicit ON_2dPoint(double x,double y); #if defined(OPENNURBS_WALL) // Goal is to eventually have all constructors that discard informtion be explicit. @@ -516,6 +552,14 @@ public: const ON_3dPoint& rhs ); + /* + Returns: + (A+B)/2 + Remarks: + Exact when coordinates are equal and prevents overflow. + */ + static const ON_3dPoint Midpoint(const ON_3dPoint& A, const ON_3dPoint& B); + public: explicit ON_3dPoint(double x,double y,double z); #if defined(OPENNURBS_WALL) @@ -2054,9 +2098,24 @@ double ON_MaximumCoordinate(const double* data, int dim, bool is_rat, int count) // class ON_CLASS ON_SurfaceCurvature { +public: + static const ON_SurfaceCurvature CreateFromPrincipalCurvatures( + double k1, + double k2 + ); + + static const ON_SurfaceCurvature Nan; + static const ON_SurfaceCurvature Zero; + public: double k1, k2; // principal curvatures +public: + bool IsSet() const; + bool IsZero() const; + bool IsUnset() const; + +public: double GaussianCurvature() const; double MeanCurvature() const; double MinimumRadius() const; @@ -3568,11 +3627,5 @@ Returns a point in dom. */ ON_2dPoint ON_DECL ON_LiftInverse(ON_2dPoint P, ON_Interval dom[2], bool closed[2]); - - - - - - #endif diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index c7ea5eeb..26289a3c 100644 --- a/opennurbs_public_version.h +++ b/opennurbs_public_version.h @@ -14,10 +14,10 @@ // first step in each build. // #define RMA_VERSION_YEAR 2020 -#define RMA_VERSION_MONTH 4 -#define RMA_VERSION_DATE 28 -#define RMA_VERSION_HOUR 13 -#define RMA_VERSION_MINUTE 30 +#define RMA_VERSION_MONTH 9 +#define RMA_VERSION_DATE 11 +#define RMA_VERSION_HOUR 14 +#define RMA_VERSION_MINUTE 20 //////////////////////////////////////////////////////////////// // @@ -35,8 +35,8 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 7,0,20119,13300 -#define VERSION_WITH_PERIODS 7.0.20119.13300 +#define VERSION_WITH_COMMAS 7,0,20255,14200 +#define VERSION_WITH_PERIODS 7.0.20255.14200 #define COPYRIGHT "Copyright (C) 1993-2020, Robert McNeel & Associates. All Rights Reserved." #define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library." @@ -47,8 +47,8 @@ #define RMA_VERSION_NUMBER_SR_STRING "SR0" #define RMA_VERSION_NUMBER_SR_WSTRING L"SR0" -#define RMA_VERSION_WITH_PERIODS_STRING "7.0.20119.13300" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"7.0.20119.13300" +#define RMA_VERSION_WITH_PERIODS_STRING "7.0.20255.14200" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"7.0.20255.14200" diff --git a/opennurbs_rand.cpp b/opennurbs_rand.cpp index 59db7172..ac358282 100644 --- a/opennurbs_rand.cpp +++ b/opennurbs_rand.cpp @@ -264,6 +264,14 @@ ON_RandomNumberGenerator::ON_RandomNumberGenerator() m_rand_context.mti = 0xFFFFFFFF; } + +ON__UINT32 ON_RandomNumberGenerator::RandomSeed() +{ + ON_UUID id; + ON_CreateUuid(id); + return ON_CRC32(0, sizeof(id), &id); +} + void ON_RandomNumberGenerator::Seed( ON__UINT32 s ) { on_random_number_seed(s,&m_rand_context); @@ -271,10 +279,7 @@ void ON_RandomNumberGenerator::Seed( ON__UINT32 s ) void ON_RandomNumberGenerator::Seed() { - ON_UUID id; - ON_CreateUuid(id) - ; - Seed(ON_CRC32(0, sizeof(id),&id)); + Seed(ON_RandomNumberGenerator::RandomSeed()); } @@ -294,6 +299,11 @@ double ON_RandomNumberGenerator::RandomDouble(double t0, double t1) return ((1.0-s)*t0 + s*t1); } +double ON_RandomNumberGenerator::RandomDouble(const class ON_Interval& range) +{ + return RandomDouble(range.m_t[0], range.m_t[1]); +} + static void Swap1(size_t count, unsigned char* a, unsigned char* b) { unsigned char t; diff --git a/opennurbs_rand.h b/opennurbs_rand.h index d3910d45..2bda1388 100644 --- a/opennurbs_rand.h +++ b/opennurbs_rand.h @@ -111,6 +111,13 @@ class ON_CLASS ON_RandomNumberGenerator public: ON_RandomNumberGenerator(); + /* + Returns: + A upredictable seed value for a random number generator. + This function is much slower than ON_RandomNumberGenerator::RandomNumber(). + */ + static ON__UINT32 RandomSeed(); + /* Description: Seed the random number generator. @@ -143,6 +150,8 @@ public: */ double RandomDouble(double t0, double t1); + double RandomDouble(const class ON_Interval& range); + /* Description: Perform a random permuation on an array. diff --git a/opennurbs_sha1.cpp b/opennurbs_sha1.cpp index c437ea1d..dca2853d 100644 --- a/opennurbs_sha1.cpp +++ b/opennurbs_sha1.cpp @@ -59,6 +59,15 @@ const ON_wString ON_SHA1_Hash::ToString( { return ON_wString::HexadecimalFromBytes(m_digest, sizeof(m_digest),bUpperCaseHexadecimalDigits,false); } + +const ON_wString ON_SHA1_Hash::ToStringEx(bool bUpperCaseHexadecimalDigits) const +{ + if (this->IsEmptyContentHash()) + return ON_wString(L"EmptyContentSHA1"); + if (this->IsZeroDigest()) + return ON_wString(L"ZeroSHA1"); + return ToString(bUpperCaseHexadecimalDigits); +} bool ON_SHA1_Hash::Read( class ON_BinaryArchive& archive @@ -635,6 +644,138 @@ void ON_SHA1::AccumulateDoubleArray( } } +void ON_SHA1::AccumulateFloat( + float x +) +{ + const float v = (0.0f == x ? 0.0f : x); + Internal_SwapBigEndianUpdate(&v, sizeof(v)); +} + + +void ON_SHA1::AccumulateFloatArray( + size_t count, + const float* a +) +{ + if (count > 0 && nullptr != a) + { + float x, v; + const float* a1 = a + count; + while (a < a1) + { + x = *a++; + v = (0.0f == x ? 0.0f : x); + Internal_SwapBigEndianUpdate(&v, sizeof(v)); + } + } +} + +void ON_SHA1::AccumulateInteger32Array(size_t count, const ON__INT32* a) +{ + if (count > 0 && nullptr != a) + { + const ON__INT32* a1 = a + count; + ON__INT32 i; + while (a < a1) + { + i = *a++; + Internal_SwapBigEndianUpdate(&i, sizeof(i)); + } + } +} + +void ON_SHA1::Accumulate2fPoint( + const class ON_2fPoint& point +) +{ + AccumulateFloatArray(2, &point.x); +} + +void ON_SHA1_Accumulate2fPointArray( + class ON_SHA1& sha1, + const ON_SimpleArray& a +) +{ + const ON_2fPoint* aa = a.Array(); + const float* f = (nullptr != aa) ? &aa[0].x : nullptr; + const size_t count = a.UnsignedCount() * sizeof(aa[0]) / sizeof(f[0]); + sha1.AccumulateFloatArray(count, f); +} + +void ON_SHA1::Accumulate3fPoint( + const class ON_3fPoint& point +) +{ + AccumulateFloatArray(3, &point.x); +} + +void ON_SHA1_Accumulate3fPointArray( + class ON_SHA1& sha1, + const ON_SimpleArray& a +) +{ + const ON_3fPoint* aa = a.Array(); + const float* f = (nullptr != aa) ? &aa[0].x : nullptr; + const size_t count = a.UnsignedCount() * sizeof(aa[0]) / sizeof(f[0]); + sha1.AccumulateFloatArray(count, f); +} + +void ON_SHA1::Accumulate4fPoint( + const class ON_4fPoint& point +) +{ + AccumulateFloatArray(4, &point.x); +} + +void ON_SHA1_Accumulate4fPointArray( + class ON_SHA1& sha1, + const ON_SimpleArray& a +) +{ + const ON_4fPoint* aa = a.Array(); + const float* f = (nullptr != aa) ? &aa[0].x : nullptr; + const size_t count = a.UnsignedCount() * sizeof(aa[0]) / sizeof(f[0]); + sha1.AccumulateFloatArray(count, f); +} + +void ON_SHA1::Accumulate2fVector( + const class ON_2fVector& vector +) +{ + AccumulateFloatArray(2, &vector.x); +} + +void ON_SHA1_Accumulate2fVectorArray( + class ON_SHA1& sha1, + const ON_SimpleArray& a +) +{ + const ON_2fVector* aa = a.Array(); + const float* f = (nullptr != aa) ? &aa[0].x : nullptr; + const size_t count = a.UnsignedCount() * sizeof(aa[0]) / sizeof(f[0]); + sha1.AccumulateFloatArray(count, f); +} + +void ON_SHA1::Accumulate3fVector( + const class ON_3fVector& vector +) +{ + AccumulateFloatArray(3, &vector.x); +} + +void ON_SHA1_Accumulate3fVectorArray( + class ON_SHA1& sha1, + const ON_SimpleArray& a +) +{ + const ON_3fVector* aa = a.Array(); + const float* f = (nullptr != aa) ? &aa[0].x : nullptr; + const size_t count = a.UnsignedCount() * sizeof(aa[0]) / sizeof(f[0]); + sha1.AccumulateFloatArray(count, f); +} + + void ON_SHA1::Accumulate2dPoint( const ON_2dPoint& point ) @@ -642,6 +783,17 @@ void ON_SHA1::Accumulate2dPoint( AccumulateDoubleArray(2,&point.x); } +void ON_SHA1_Accumulate2dPointArray( + class ON_SHA1& sha1, + const ON_SimpleArray& a +) +{ + const ON_2dPoint* aa = a.Array(); + const double* d = (nullptr != aa) ? &aa[0].x : nullptr; + const size_t count = a.UnsignedCount() * sizeof(aa[0]) / sizeof(d[0]); + sha1.AccumulateDoubleArray(count, d); +} + void ON_SHA1::Accumulate3dPoint( const ON_3dPoint& point ) @@ -649,6 +801,17 @@ void ON_SHA1::Accumulate3dPoint( AccumulateDoubleArray(3,&point.x); } +void ON_SHA1_Accumulate3dPointArray( + class ON_SHA1& sha1, + const ON_SimpleArray& a +) +{ + const ON_3dPoint* aa = a.Array(); + const double* d = (nullptr != aa) ? &aa[0].x : nullptr; + const size_t count = a.UnsignedCount() * sizeof(aa[0]) / sizeof(d[0]); + sha1.AccumulateDoubleArray(count, d); +} + void ON_SHA1::Accumulate4dPoint( const ON_4dPoint& point ) @@ -656,6 +819,17 @@ void ON_SHA1::Accumulate4dPoint( AccumulateDoubleArray(4,&point.x); } +void ON_SHA1_Accumulate4dPointArray( + class ON_SHA1& sha1, + const ON_SimpleArray& a +) +{ + const ON_4dPoint* aa = a.Array(); + const double* d = (nullptr != aa) ? &aa[0].x : nullptr; + const size_t count = a.UnsignedCount() * sizeof(aa[0]) / sizeof(d[0]); + sha1.AccumulateDoubleArray(count, d); +} + void ON_SHA1::Accumulate2dVector( const ON_2dVector& vector ) @@ -663,6 +837,17 @@ void ON_SHA1::Accumulate2dVector( AccumulateDoubleArray(2,&vector.x); } +void ON_SHA1_Accumulate2dVectorArray( + class ON_SHA1& sha1, + const ON_SimpleArray& a +) +{ + const ON_2dVector* aa = a.Array(); + const double* d = (nullptr != aa) ? &aa[0].x : nullptr; + const size_t count = a.UnsignedCount() * sizeof(aa[0]) / sizeof(d[0]); + sha1.AccumulateDoubleArray(count, d); +} + void ON_SHA1::Accumulate3dVector( const ON_3dVector& vector ) @@ -670,6 +855,17 @@ void ON_SHA1::Accumulate3dVector( AccumulateDoubleArray(3,&vector.x); } +void ON_SHA1_Accumulate3dVectorArray( + class ON_SHA1& sha1, + const ON_SimpleArray& a +) +{ + const ON_3dVector* aa = a.Array(); + const double* d = (nullptr != aa) ? &aa[0].x : nullptr; + const size_t count = a.UnsignedCount() * sizeof(aa[0]) / sizeof(d[0]); + sha1.AccumulateDoubleArray(count, d); +} + void ON_SHA1::AccumulateUnitSystem ( const class ON_UnitSystem& unit_system diff --git a/opennurbs_sha1.h b/opennurbs_sha1.h index 29c7918c..70252dbc 100644 --- a/opennurbs_sha1.h +++ b/opennurbs_sha1.h @@ -197,7 +197,15 @@ public: */ const ON_wString ToString( bool bUpperCaseHexadecimalDigits - ) const; + ) const; + + /* + Description: + Same as ToString but prints EmptyContentSHA1 or ZeroSHA1 for those two special cases. + */ + const ON_wString ToStringEx( + bool bUpperCaseHexadecimalDigits + ) const; bool Read( class ON_BinaryArchive& archive @@ -301,13 +309,54 @@ public: const double* a ); + /* + Description: + Add the double value to the SHA1 in a manner that + -0.0 and +0.0 will generate identical SHA-1 values + and the result is independent of endian byte order. + */ + void AccumulateFloat( + float x + ); + + /* + Description: + Add the double value to the SHA1 in a manner that + -0.0 and +0.0 will generate identical SHA-1 values + and the result is independent of endian byte order. + */ + void AccumulateFloatArray( + size_t count, + const float* a + ); + + void Accumulate2fPoint( + const class ON_2fPoint& point + ); + + void Accumulate3fPoint( + const class ON_3fPoint& point + ); + + void Accumulate4fPoint( + const class ON_4fPoint& point + ); + + void Accumulate2fVector( + const class ON_2fVector& vector + ); + + void Accumulate3fVector( + const class ON_3fVector& vector + ); + void Accumulate2dPoint( const class ON_2dPoint& point - ); + ); void Accumulate3dPoint( const class ON_3dPoint& point - ); + ); void Accumulate4dPoint( const class ON_4dPoint& point @@ -315,11 +364,11 @@ public: void Accumulate2dVector( const class ON_2dVector& vector - ); + ); void Accumulate3dVector( const class ON_3dVector& vector - ); + ); void AccumulateBoundingBox( const class ON_BoundingBox& bbox @@ -364,6 +413,11 @@ public: void AccumulateInteger32( ON__INT32 i ); + + void AccumulateInteger32Array( + size_t count, + const ON__INT32* a + ); void AccumulateUnsigned32( ON__UINT32 u diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp index 9c7d8785..66928336 100644 --- a/opennurbs_statics.cpp +++ b/opennurbs_statics.cpp @@ -109,11 +109,29 @@ std::atomic ON_ModelComponent::Internal_RuntimeSerialNumberGenerator std::atomic ON_SubDimple::Internal_RuntimeSerialNumberGenerator; -ON_SubDComponentLocation ON_SubD::DefaultSubDAppearance = ON_SubDComponentLocation::Surface; +const ON_SubDComponentLocation ON_SubD::DefaultSubDAppearance = ON_SubDComponentLocation::Surface; + +// The default type must be packed, unpacked, zero, or nan and should be packed or upacked. +const ON_SubDTextureCoordinateType ON_SubD::DefaultTextureCoordinateType = ON_SubDTextureCoordinateType::Packed; + const double ON_SubDSectorType::MinimumCornerAngleRadians = (2.0*ON_PI)/((double)(ON_SubDSectorType::MaximumCornerAngleIndex)); const double ON_SubDSectorType::MaximumCornerAngleRadians = 2.0*ON_PI - ON_SubDSectorType::MinimumCornerAngleRadians; +const ON_SubDSectorId ON_SubDSectorId::Zero ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDSectorId); +const ON_SubDSectorId ON_SubDSectorId::Invalid = ON_SubDSectorId::Create(nullptr, nullptr); + +const ON_SubDToBrepParameters Internal_SubDToBrepParameters(bool bPackedFaces) +{ + ON_SubDToBrepParameters p; + p.SetPackFaces(bPackedFaces); + return p; +} + +const ON_SubDToBrepParameters ON_SubDToBrepParameters::Default ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDToBrepParameters); +const ON_SubDToBrepParameters ON_SubDToBrepParameters::DefaultUnpacked = Internal_SubDToBrepParameters(false); +const ON_SubDToBrepParameters ON_SubDToBrepParameters::DefaultPacked = Internal_SubDToBrepParameters(true); + ON_ClassId* ON_ClassId::m_p0 = 0; // static pointer to first id in list ON_ClassId* ON_ClassId::m_p1 = 0; // static pointer to last id in list @@ -601,6 +619,8 @@ const ON_Xform ON_Xform::Zero4x4 = ON_Xform_Init(0.0, false); const ON_Xform ON_Xform::Unset = ON_Xform_Init(ON_UNSET_VALUE, false); const ON_Xform ON_Xform::Nan = ON_Xform_Init(ON_DBL_QNAN, false); +const ON_SurfaceCurvature ON_SurfaceCurvature::Nan = ON_SurfaceCurvature::CreateFromPrincipalCurvatures(ON_DBL_QNAN, ON_DBL_QNAN); +const ON_SurfaceCurvature ON_SurfaceCurvature::Zero = ON_SurfaceCurvature::CreateFromPrincipalCurvatures(0.0, 0.0); const ON_2dPoint ON_2dPoint::Origin(0.0, 0.0); const ON_2dPoint ON_2dPoint::UnsetPoint(ON_UNSET_VALUE, ON_UNSET_VALUE); @@ -901,10 +921,13 @@ const ON_Plane ON_Plane::NanPlane(ON_Plane_NanPlane()); // ON_SubDDisplayParameters statics before ON_MeshParamters statics const ON_SubDDisplayParameters ON_SubDDisplayParameters::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDDisplayParameters); -const ON_SubDDisplayParameters ON_SubDDisplayParameters::Course = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::CourseDensity); +const ON_SubDDisplayParameters ON_SubDDisplayParameters::ExtraCoarse = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::ExtraCoarseDensity); +const ON_SubDDisplayParameters ON_SubDDisplayParameters::Coarse = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::CoarseDensity); +const ON_SubDDisplayParameters ON_SubDDisplayParameters::Medium = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::MediumDensity); +const ON_SubDDisplayParameters ON_SubDDisplayParameters::Fine = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::FineDensity); +const ON_SubDDisplayParameters ON_SubDDisplayParameters::ExtraFine = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::ExtraFineDensity); const ON_SubDDisplayParameters ON_SubDDisplayParameters::Default = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::DefaultDensity); - // {F15F67AA-4AF9-4B25-A3B8-517CEDDAB134} const ON_UUID ON_MeshParameters::RhinoLegacyMesherId = { 0xf15f67aa, 0x4af9, 0x4b25,{ 0xa3, 0xb8, 0x51, 0x7c, 0xed, 0xda, 0xb1, 0x34 } }; @@ -1486,7 +1509,7 @@ const ON_TextureMapping ON_TextureMapping::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(O static ON_TextureMapping SurfaceParameterTextureMappingInitializer() { //// {B988A6C2-61A6-45a7-AAEE-9AED7EF4E316} - static const ON_UUID srfp_mapping_id = { 0xb988a6c2, 0x61a6, 0x45a7,{ 0xaa, 0xee, 0x9a, 0xed, 0x7e, 0xf4, 0xe3, 0x16 } }; + const ON_UUID srfp_mapping_id = { 0xb988a6c2, 0x61a6, 0x45a7,{ 0xaa, 0xee, 0x9a, 0xed, 0x7e, 0xf4, 0xe3, 0x16 } }; ON_TextureMapping tm; tm.SetId(srfp_mapping_id); @@ -2343,6 +2366,7 @@ const ON_Mesh ON_Mesh::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Mesh); const ON_MeshRef ON_MeshRef::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_MeshRef); unsigned int ON_SubD::ErrorCount = 0; +unsigned int ON_Brep::ErrorCount = 0; const bool ON_SubD::AutomaticRhino5BoxModeTSplineToSubDDefault = true; const bool ON_SubD::AutomaticFBXMeshWithDivisionLevelsToSubDDefault = false; @@ -2607,26 +2631,24 @@ const ON_SubDSectorType ON_SubDSectorType::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(O const ON_SubDMatrix ON_SubDMatrix::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDMatrix); const ON_SubDComponentRef ON_SubDComponentRef::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDComponentRef); -static ON_SubDFromMeshParameters ON_SubDCreaseParameters_CreaseAt( - ON_SubDFromMeshParameters::InteriorCreaseOption crease_type - ) +static ON_SubDFromMeshParameters Internal_InteriorCreases() { ON_SubDFromMeshParameters cp; - cp.SetInteriorCreaseOption(crease_type); + cp.SetInteriorCreaseOption(ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshDoubleEdge); return cp; } -static ON_SubDFromMeshParameters ON_SubDCreaseParameters_ConvexCorners() +static ON_SubDFromMeshParameters Internal_ConvexCornersAndInteriorCreases() { ON_SubDFromMeshParameters cp; + cp.SetInteriorCreaseOption(ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshDoubleEdge); cp.SetConvexCornerOption(ON_SubDFromMeshParameters::ConvexCornerOption::AtMeshCorner); return cp; } const ON_SubDFromMeshParameters ON_SubDFromMeshParameters::Smooth ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDFromMeshParameters); -const ON_SubDFromMeshParameters ON_SubDFromMeshParameters::InteriorCreaseAtMeshCrease = ON_SubDCreaseParameters_CreaseAt(ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshCrease); -const ON_SubDFromMeshParameters ON_SubDFromMeshParameters::InteriorCreaseAtMeshEdge = ON_SubDCreaseParameters_CreaseAt(ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshEdge); -const ON_SubDFromMeshParameters ON_SubDFromMeshParameters::ConvexCornerAtMeshCorner = ON_SubDCreaseParameters_ConvexCorners(); +const ON_SubDFromMeshParameters ON_SubDFromMeshParameters::InteriorCreases = Internal_InteriorCreases(); +const ON_SubDFromMeshParameters ON_SubDFromMeshParameters::ConvexCornersAndInteriorCreases = Internal_ConvexCornersAndInteriorCreases(); const ON_SubDFromSurfaceParameters ON_SubDFromSurfaceParameters::Default ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDFromSurfaceParameters); diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp index 6097498f..1ba9d70e 100644 --- a/opennurbs_subd.cpp +++ b/opennurbs_subd.cpp @@ -25,6 +25,92 @@ //////////////////////////////////////////////////////////////// */ + +ON_SubDToBrepParameters::VertexProcess ON_SubDToBrepParameters::VertexProcessFromUnsigned( + unsigned int vertex_process_as_unsigned +) +{ + switch (vertex_process_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDToBrepParameters::VertexProcess::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDToBrepParameters::VertexProcess::LocalG1); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDToBrepParameters::VertexProcess::LocalG2); + ////ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDToBrepParameters::VertexProcess::GlobalG1); + } + return ON_SUBD_RETURN_ERROR(ON_SubDToBrepParameters::VertexProcess::None); +} + +ON_SubDToBrepParameters::VertexProcess ON_SubDToBrepParameters::ExtraordinaryVertexProcess() const +{ + return m_extraordinary_vertex_process; +} + + +int ON_SubDToBrepParameters::Compare( + const ON_SubDToBrepParameters& lhs, + const ON_SubDToBrepParameters& rhs +) +{ + unsigned int a = lhs.m_bPackFaces ? 1 : 0; + unsigned int b = rhs.m_bPackFaces ? 1 : 0; + if (a < b) + return -1; + if (a > b) + return 1; + + a = static_cast(lhs.m_extraordinary_vertex_process); + b = static_cast(rhs.m_extraordinary_vertex_process); + if (a < b) + return -1; + if (a > b) + return 1; + + return 0; +} + +int ON_SubDToBrepParameters::CompareFromPointers( + const ON_SubDToBrepParameters* lhs, + const ON_SubDToBrepParameters* rhs +) +{ + if (lhs == rhs) + return 0; + if (nullptr == lhs) + return 1; + if (nullptr == rhs) + return -1; + return ON_SubDToBrepParameters::Compare(*lhs, *rhs); +} + +bool operator==(const ON_SubDToBrepParameters& lhs, const ON_SubDToBrepParameters& rhs) +{ + return 0 == ON_SubDToBrepParameters::Compare(lhs, rhs); +} + +bool operator!=(const ON_SubDToBrepParameters& lhs, const ON_SubDToBrepParameters& rhs) +{ + return (0 != ON_SubDToBrepParameters::Compare(lhs, rhs)); +} + +void ON_SubDToBrepParameters::SetExtraordinaryVertexProcess( + ON_SubDToBrepParameters::VertexProcess ev_process +) +{ + m_extraordinary_vertex_process = ev_process; +} + +bool ON_SubDToBrepParameters::PackFaces() const +{ + return m_bPackFaces; +} + +void ON_SubDToBrepParameters::SetPackFaces( + bool bPackFaces +) +{ + m_bPackFaces = bPackFaces ? true : false; +} + ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentPtrTypeFromUnsigned( unsigned int element_pointer_type_as_unsigned ) @@ -39,59 +125,175 @@ ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentPtrTypeFromUnsigned( return ON_SUBD_RETURN_ERROR(ON_SubDComponentPtr::Type::Unset); } -ON_SubD::VertexTag ON_SubD::VertexTagFromUnsigned( +ON_SubDVertexTag ON_SubD::VertexTagFromUnsigned( unsigned int vertex_tag_as_unsigned ) { switch (vertex_tag_as_unsigned) { - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Unset); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Smooth); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Crease); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Corner); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Dart); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDVertexTag::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDVertexTag::Smooth); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDVertexTag::Crease); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDVertexTag::Corner); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDVertexTag::Dart); } - return ON_SUBD_RETURN_ERROR(ON_SubD::VertexTag::Unset); + return ON_SUBD_RETURN_ERROR(ON_SubDVertexTag::Unset); +} + +const ON_wString ON_SubD::VertexTagToString( + ON_SubDVertexTag vertex_tag, + bool bVerbose +) +{ + const wchar_t* tag_name; + switch (vertex_tag) + { + case ON_SubDVertexTag::Unset: + tag_name = L"Unset"; + break; + case ON_SubDVertexTag::Smooth: + tag_name = L"Smooth"; + break; + case ON_SubDVertexTag::Crease: + tag_name = L"Crease"; + break; + case ON_SubDVertexTag::Corner: + tag_name = L"Corner"; + break; + case ON_SubDVertexTag::Dart: + tag_name = L"Dart"; + break; + default: + tag_name = L"invalid"; + break; + } + return bVerbose ? ON_wString::FormatToString(L"ON_SubDVertexTag::%ls", tag_name) : ON_wString(tag_name); +} + +const ON_wString ON_SubDVertex::ToString( + bool bIncludeControlNetPoint, + bool bIncludeTopology +) const +{ + const ON_wString vtag = ON_SubD::VertexTagToString(m_vertex_tag,false); + ON_wString v + = bIncludeControlNetPoint + ? ON_wString::FormatToString(L"v%u %ls (%g,%g,%g)", m_id, static_cast(vtag), m_P[0], m_P[1], m_P[2]) + : ON_wString::FormatToString(L"v%u %ls", m_id, static_cast(vtag)) + ; + if (bIncludeTopology) + { + ON_wString elist = ON_wString::FormatToString(L" Edges[%u]", EdgeCount()); + if (nullptr != m_edges && m_edge_count > 0) + { + elist += L"={"; + for (unsigned short vei = 0; vei < m_edge_count; ++vei) + { + const ON_SubDEdgePtr eptr = m_edges[vei]; + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr); + if (vei > 0) + elist += L", "; + if (nullptr == e) + elist += L"Null"; + else + { + elist += (0 == ON_SUBD_EDGE_DIRECTION(eptr.m_ptr)) ? ON_wString::FormatToString(L"+e%u", e->m_id) : ON_wString::FormatToString(L"-e%u", e->m_id); + } + } + elist += L"}"; + } + v += elist; + + ON_wString flist = ON_wString::FormatToString(L" Faces[%u]", FaceCount()); + if (nullptr != m_faces && m_face_count > 0) + { + flist += L"={"; + for (unsigned short vfi = 0; vfi < m_face_count; ++vfi) + { + const ON_SubDFace* f = m_faces[vfi]; + if (vfi > 0) + flist += L", "; + if (nullptr == f) + flist += L"Null"; + else + { + flist += ON_wString::FormatToString(L"f%u", f->m_id); + } + } + flist += L"}"; + } + v += flist; + } + return v; } bool ON_SubD::VertexTagIsSet( - ON_SubD::VertexTag vertex_tag + ON_SubDVertexTag vertex_tag ) { return ( - ON_SubD::VertexTag::Smooth == vertex_tag - || ON_SubD::VertexTag::Crease == vertex_tag - || ON_SubD::VertexTag::Corner == vertex_tag - || ON_SubD::VertexTag::Dart == vertex_tag + ON_SubDVertexTag::Smooth == vertex_tag + || ON_SubDVertexTag::Crease == vertex_tag + || ON_SubDVertexTag::Corner == vertex_tag + || ON_SubDVertexTag::Dart == vertex_tag ); } -ON_SubD::EdgeTag ON_SubD::EdgeTagFromUnsigned( +ON_SubDEdgeTag ON_SubD::EdgeTagFromUnsigned( unsigned int edge_tag_as_unsigned ) { switch (edge_tag_as_unsigned) { - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Unset); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Smooth); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Crease); - //ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Sharp); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::SmoothX); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEdgeTag::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEdgeTag::Smooth); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEdgeTag::Crease); + //ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEdgeTag::Sharp); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEdgeTag::SmoothX); } - return ON_SUBD_RETURN_ERROR(ON_SubD::EdgeTag::Unset); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgeTag::Unset); } + +const ON_wString ON_SubD::EdgeTagToString( + ON_SubDEdgeTag edge_tag, + bool bVerbose +) +{ + const wchar_t* tag_name; + switch (edge_tag) + { + case ON_SubDEdgeTag::Unset: + tag_name = L"Unset"; + break; + case ON_SubDEdgeTag::Smooth: + tag_name = L"Smooth"; + break; + case ON_SubDEdgeTag::Crease: + tag_name = L"Crease"; + break; + case ON_SubDEdgeTag::SmoothX: + tag_name = L"SmoothX"; + break; + default: + tag_name = L"invalid"; + break; + } + return bVerbose ? ON_wString::FormatToString(L"ON_SubDEdgeTag::%ls", tag_name) : ON_wString(tag_name); +} + + bool ON_SubD::EdgeTagIsSet( - ON_SubD::EdgeTag edge_tag + ON_SubDEdgeTag edge_tag ) { return ( - ON_SubD::EdgeTag::Smooth == edge_tag - || ON_SubD::EdgeTag::Crease == edge_tag - //|| ON_SubD::EdgeTag::Sharp == edge_tag - || ON_SubD::EdgeTag::SmoothX == edge_tag + ON_SubDEdgeTag::Smooth == edge_tag + || ON_SubDEdgeTag::Crease == edge_tag + //|| ON_SubDEdgeTag::Sharp == edge_tag + || ON_SubDEdgeTag::SmoothX == edge_tag ); } @@ -111,19 +313,19 @@ ON_SubD::VertexFacetType ON_SubD::VertexFacetTypeFromUnsigned( } unsigned int ON_SubDSectorType::SectorPointRingCountFromEdgeCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_edge_count ) { if (sector_edge_count >= ON_SubDSectorType::MinimumSectorEdgeCount(vertex_tag) && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount) { - if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) + if (ON_SubDVertexTag::Smooth == vertex_tag || ON_SubDVertexTag::Dart == vertex_tag) { // interior vertex return (2 * sector_edge_count + 1); } - if (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag) + if (ON_SubDVertexTag::Crease == vertex_tag || ON_SubDVertexTag::Corner == vertex_tag) { // boundary vertex return (2 * sector_edge_count); @@ -133,7 +335,7 @@ unsigned int ON_SubDSectorType::SectorPointRingCountFromEdgeCount( } unsigned int ON_SubDSectorType::SectorPointRingCountFromFaceCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_face_count ) { @@ -144,14 +346,14 @@ unsigned int ON_SubDSectorType::SectorPointRingCountFromFaceCount( } unsigned int ON_SubDSectorType::SectorFaceCountFromEdgeCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_edge_count ) { if (sector_edge_count >= 2 && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount) { unsigned int sector_face_count - = (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag) + = (ON_SubDVertexTag::Crease == vertex_tag || ON_SubDVertexTag::Corner == vertex_tag) ? sector_edge_count-1 : sector_edge_count; return sector_face_count; @@ -160,14 +362,14 @@ unsigned int ON_SubDSectorType::SectorFaceCountFromEdgeCount( } unsigned int ON_SubDSectorType::SectorEdgeCountFromFaceCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_face_count ) { if (sector_face_count > 0 && sector_face_count <= ON_SubDVertex::MaximumFaceCount) { unsigned int sector_edge_count - = (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag) + = (ON_SubDVertexTag::Crease == vertex_tag || ON_SubDVertexTag::Corner == vertex_tag) ? sector_face_count+1 : sector_face_count; return sector_edge_count; @@ -176,46 +378,46 @@ unsigned int ON_SubDSectorType::SectorEdgeCountFromFaceCount( } unsigned int ON_SubDSectorType::MinimumSectorEdgeCount( - ON_SubD::VertexTag vertex_tag + ON_SubDVertexTag vertex_tag ) { - if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) + if (ON_SubDVertexTag::Smooth == vertex_tag || ON_SubDVertexTag::Dart == vertex_tag) return ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag); - if (ON_SubD::VertexTag::Corner == vertex_tag || ON_SubD::VertexTag::Crease == vertex_tag) + if (ON_SubDVertexTag::Corner == vertex_tag || ON_SubDVertexTag::Crease == vertex_tag) return ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag)+1; return ON_UNSET_UINT_INDEX; } unsigned int ON_SubDSectorType::MinimumSectorFaceCount( - ON_SubD::VertexTag vertex_tag + ON_SubDVertexTag vertex_tag ) { unsigned int minimum_sector_face_count; switch (vertex_tag) { - case ON_SubD::VertexTag::Unset: + case ON_SubDVertexTag::Unset: ON_SUBD_ERROR("Unset tag."); minimum_sector_face_count = ON_UNSET_UINT_INDEX; break; - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() // for more details on how this case is handled. minimum_sector_face_count = 2; // 3 without special case handling break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: // A "wire" crease can have zero faces - this is the minimum when faces exist minimum_sector_face_count = 1; break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: // A "wire" corner can have zero faces - this is the minimum when faces exist minimum_sector_face_count = 1; break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() // for more details on how this case is handled. @@ -232,7 +434,7 @@ unsigned int ON_SubDSectorType::MinimumSectorFaceCount( } bool ON_SubD::IsValidSectorEdgeCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_edge_count ) { @@ -240,7 +442,7 @@ bool ON_SubD::IsValidSectorEdgeCount( } bool ON_SubD::IsValidSectorFaceCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_face_count ) { @@ -902,6 +1104,25 @@ ON__UINT8 ON_SubDComponentPtr::ClearMarkBits() const return (nullptr != c) ? c->m_status.SetMarkBits(0) : false; } +unsigned int ON_SubDComponentPtr::GroupId(unsigned int null_component_value) const +{ + const ON_SubDComponentBase* c = this->ComponentBase(); + return (nullptr != c) ? c->m_group_id : null_component_value; +} + +bool ON_SubDComponentPtr::SetGroupId( + unsigned int group_id +) +{ + const ON_SubDComponentBase* c = this->ComponentBase(); + if (nullptr != c) + { + c->m_group_id = group_id; + return true; + } + return false; +} + bool ON_SubDVertexPtr::Mark() const { @@ -1347,6 +1568,184 @@ int ON_SubDComponentPtr::CompareComponentAndDirection( return rc; } +static const bool Internal_FirstIsPartOfSecond( + const ON_SubDComponentPoint& first, + const ON_SubDComponentPoint& second +) +{ + // returns true if first is topologically part of second (vertex on edge, vertex on face, or edge on face). + const ON_SubDComponentPtr::Type first_type = first.m_component_ptr.ComponentType(); + const ON_SubDComponentPtr::Type second_type = second.m_component_ptr.ComponentType(); + if (first_type != second_type) + { + if (ON_SubDComponentPtr::Type::Vertex == first_type) + { + const ON_SubDVertex* v = first.m_component_ptr.Vertex(); + if (nullptr != v) + { + if (ON_SubDComponentPtr::Type::Edge == second_type) + { + const ON_SubDEdge* e = second.m_component_ptr.Edge(); + if (nullptr != e && (v == e->m_vertex[0] || v == e->m_vertex[1])) + return true; + } + else if (ON_SubDComponentPtr::Type::Face == second_type) + { + const ON_SubDFace* f = second.m_component_ptr.Face(); + if (nullptr != f && f->VertexIndex(v) < ON_UNSET_UINT_INDEX) + return true; + } + } + } + else if (ON_SubDComponentPtr::Type::Edge == first_type) + { + if (ON_SubDComponentPtr::Type::Face == second_type) + { + const ON_SubDEdge* e = first.m_component_ptr.Edge(); + const ON_SubDFace* f = second.m_component_ptr.Face(); + if (nullptr != e && nullptr != f && e->FaceArrayIndex(f) < ON_UNSET_UINT_INDEX) + return true; + } + } + } + return false; +} + +const ON_SubDComponentPoint ON_SubDComponentPoint::BestPickPoint( + ON_PickType pick_type, + double vertex_depth_bias, + double edge_depth_bias, + const ON_SubDComponentPoint& A, + const ON_SubDComponentPoint& B +) +{ + // If you're working on a bug where somebody isn't able to pick the SubD component + // they thinked they clicked on, then you're in the right place. + // Mikko has done lots of work in this area and be sure to retest RH-52172 and RH-52173 (fixed 3 May 2019). + // Also, retest RH-59666 (fixed August 2020). + // + // This function will be a work in progress for many years. + + // unbiased_compare ignores SubD component types and relative topological relationships. + // If unbiased_compare == 1, then A is "best" from this "unbiased" point of views. + const int unbiased_compare = (ON_PickPoint::Compare(A.m_pick_point, B.m_pick_point)>=0) ? 1 : -1; + + const ON_SubDComponentPtr::Type A_component_type = A.m_component_ptr.ComponentType(); + const ON_SubDComponentPtr::Type B_component_type = B.m_component_ptr.ComponentType(); + if (A_component_type == B_component_type) + { + // A and B are the same type of component + return (unbiased_compare >= 0) ? A : B; + } + if (ON_SubDComponentPtr::Type::Unset == A_component_type) + return B; + if (ON_SubDComponentPtr::Type::Unset == B_component_type) + return A; + + // If we get this far, then A and B are pick events on different types of SubD components. + + // type_bias = 1 if A is a vertex and B is an edge/face or A is an edge and B is a face. + // type_bias = -1 if B is a vertex and A is an edge/face or B is an edge and A is a face. + const int type_bias = (ON_SubDComponentPtr::CompareComponentPtrType(A_component_type, B_component_type)) <= 0 ? 1 : -1; + + if ( + ON_PickType::PointPick == pick_type + && ((type_bias >= 0) ? Internal_FirstIsPartOfSecond(A, B) : Internal_FirstIsPartOfSecond(B, A)) + ) + { + // A point pick is occuring and best is a vertex on an edge/face or best is an edge on a face. + // Bias towards the vertex/edge. + // Users can pick the middle of an edge/face if they want the "bigger" component. + ON_SubDComponentPoint best = (type_bias >= 0) ? A : B; + best.m_pick_point.m_distance = ON_Min(A.m_pick_point.m_distance, B.m_pick_point.m_distance); + best.m_pick_point.m_depth = ON_Max(A.m_pick_point.m_depth, B.m_pick_point.m_depth); + return best; + } + + ON_PickPoint biased_A(A.m_pick_point); + ON_PickPoint biased_B(B.m_pick_point); + + // distance bias applied to point picks on vertices and edges + // Events with smaller distances are "better" because they are visually are closer to the picked point. + if (ON_PickType::PointPick == pick_type) + { + // 18 Aug 2020 Dale Lear: + // The pick distance for a point pick is normalized so 1.0 = pick frustum radius. + // Reasonable values for distance_tol are 0.25 to 1.0. + // 0.5 fixes RH-52172 + // 1.0 seems to fix bugs like RH-59666 (0.5 is too small to fix RH-59666). + const double distance_tol = 1.0; + + if ( type_bias > 0 && biased_A.m_distance > 0.0 && biased_A.m_distance <= distance_tol) + biased_A.m_distance = 0.0; // consider pick A to be on the vertex/edge + + if ( type_bias < 0 && biased_B.m_distance > 0.0 && biased_B.m_distance <= distance_tol ) + biased_B.m_distance = 0.0; // consider pick B to be on the vertex/edge + } + + // Apply a depth bias when we have a relative topological relationship between the components + // (vertex on edge, vertex on face, or edge on face). + // Events with larger depths are "better" because they are in front of smaller depths. + double depth_bias = 0.0; + const bool bHaveVertex = ON_SubDComponentPtr::Type::Vertex == A_component_type || ON_SubDComponentPtr::Type::Vertex == B_component_type; + const bool bHaveEdge = ON_SubDComponentPtr::Type::Edge == A_component_type || ON_SubDComponentPtr::Type::Edge == B_component_type; + const bool bHaveFace = ON_SubDComponentPtr::Type::Face == A_component_type || ON_SubDComponentPtr::Type::Face == B_component_type; + if ( bHaveVertex && vertex_depth_bias > 0.0 && vertex_depth_bias < ON_UNSET_POSITIVE_VALUE ) + { + // One event is a vertex, the other event is an edge or face, and a vertex bias was supplied. + // Test to see if the vertex is on the edge or face. + if (Internal_FirstIsPartOfSecond(A,B)) + { + // B is an edge/face and A is a vertex on the edge/face + depth_bias = vertex_depth_bias; + biased_A.m_depth += depth_bias; // bias vertex closer + } + else if (Internal_FirstIsPartOfSecond(B, A)) + { + // A is an edge/face and B is a vertex on the edge/face + depth_bias = vertex_depth_bias; + biased_B.m_depth += depth_bias; // bias vertex closer + } + } + else if ( bHaveEdge && bHaveFace && edge_depth_bias > 0.0 && edge_depth_bias < ON_UNSET_POSITIVE_VALUE ) + { + // One event is an edge, the other event is a face, and an edge bias was supplied. + // Test to see if the edge is on the face. + if (Internal_FirstIsPartOfSecond(A, B)) + { + // B is a face and A is an edge on the face + depth_bias = edge_depth_bias; + biased_A.m_depth += depth_bias; // bias edge closer + } + else if (Internal_FirstIsPartOfSecond(B, A)) + { + // A is a face and B is an edge on the face + depth_bias = edge_depth_bias; + biased_B.m_depth += depth_bias; // bias edge closer + } + } + + // biased_compare adds a bias towards vertices and edges based on SubD component types and relative topological relationships. + // If biased_compare >= 0, then A is "best" from this biased point of views. + const int biased_compare = (ON_PickPoint::Compare(biased_A, biased_B)>=0) ? 1 : -1; + + ON_SubDComponentPoint best = (biased_compare >= 0) ? A : B; + + const ON_SubDComponentPtr::Type best_type = best.m_component_ptr.ComponentType(); + if ( ON_SubDComponentPtr::Type::Vertex == best_type || (ON_SubDComponentPtr::Type::Edge == best_type && bHaveFace) ) + { + // Either a vertex was better than an edge/face or an edge was better than a face. + if (fabs(A.m_pick_point.m_depth - B.m_pick_point.m_depth) <= depth_bias) + { + // The two pick points were "close" in depth. + // In these cases, strengthen "best" by using the "best" depth and distance values from A and B. + best.m_pick_point.m_distance = ON_Min(A.m_pick_point.m_distance, B.m_pick_point.m_distance); + best.m_pick_point.m_depth = ON_Max(A.m_pick_point.m_depth, B.m_pick_point.m_depth); + } + } + + return best; +} int ON_SubDComponentPoint::CompareComponentAndDirection( const ON_SubDComponentPoint* a, @@ -1516,22 +1915,19 @@ bool ON_SubDComponentPtrPair::BothAreNotNull() const // ON_SubDFromMeshParameters // +// Convex corner options + ON_SubDFromMeshParameters::ConvexCornerOption ON_SubDFromMeshParameters::ConvexCornerOptionFromUnsigned( unsigned int convex_corner_option_as_unsigned - ) +) { switch (convex_corner_option_as_unsigned) { - case (unsigned int)ON_SubDFromMeshParameters::ConvexCornerOption::Unset: - return ON_SubDFromMeshParameters::ConvexCornerOption::Unset; - case (unsigned int)ON_SubDFromMeshParameters::ConvexCornerOption::None: - return ON_SubDFromMeshParameters::ConvexCornerOption::None; - case (unsigned int)ON_SubDFromMeshParameters::ConvexCornerOption::AtMeshCorner: - return ON_SubDFromMeshParameters::ConvexCornerOption::AtMeshCorner; - default: - break; + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::ConvexCornerOption::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::ConvexCornerOption::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::ConvexCornerOption::AtMeshCorner); } - return ON_SubDFromMeshParameters::ConvexCornerOption::Unset; + return ON_SUBD_RETURN_ERROR(ON_SubDFromMeshParameters::ConvexCornerOption::Unset); } void ON_SubDFromMeshParameters::SetConvexCornerOption( @@ -1541,7 +1937,7 @@ void ON_SubDFromMeshParameters::SetConvexCornerOption( m_convex_corner_option = ON_SubDFromMeshParameters::ConvexCornerOptionFromUnsigned((unsigned int)convex_corner_option); } -ON_SubDFromMeshParameters::ConvexCornerOption ON_SubDFromMeshParameters::ConvexCornerTest() const +ON_SubDFromMeshParameters::ConvexCornerOption ON_SubDFromMeshParameters::GetConvexCornerOption() const { switch (m_convex_corner_option) { @@ -1592,12 +1988,92 @@ ON_SubDFromMeshParameters::ConvexCornerOption ON_SubDFromMeshParameters::CopyCon ON_SubDFromMeshParameters source_parameters ) { - SetConvexCornerOption(source_parameters.ConvexCornerTest()); + SetConvexCornerOption(source_parameters.GetConvexCornerOption()); SetMaximumConvexCornerEdgeCount(source_parameters.MaximumConvexCornerEdgeCount()); SetMaximumConvexCornerAngleRadians(source_parameters.MaximumConvexCornerAngleRadians()); - return ConvexCornerTest(); + return GetConvexCornerOption(); } +// concave corner options + +ON_SubDFromMeshParameters::ConcaveCornerOption ON_SubDFromMeshParameters::ConcaveCornerOptionFromUnsigned( + unsigned int concave_corner_option_as_unsigned +) +{ + switch (concave_corner_option_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::ConcaveCornerOption::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::ConcaveCornerOption::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::ConcaveCornerOption::AtMeshCorner); + } + return ON_SUBD_RETURN_ERROR(ON_SubDFromMeshParameters::ConcaveCornerOption::Unset); +} + +void ON_SubDFromMeshParameters::SetConcaveCornerOption( + ON_SubDFromMeshParameters::ConcaveCornerOption concave_corner_option +) +{ + m_concave_corner_option = ON_SubDFromMeshParameters::ConcaveCornerOptionFromUnsigned((unsigned int)concave_corner_option); +} + +ON_SubDFromMeshParameters::ConcaveCornerOption ON_SubDFromMeshParameters::GetConcaveCornerOption() const +{ + switch (m_concave_corner_option) + { + case ON_SubDFromMeshParameters::ConcaveCornerOption::Unset: + case ON_SubDFromMeshParameters::ConcaveCornerOption::None: + return m_concave_corner_option; + + case ON_SubDFromMeshParameters::ConcaveCornerOption::AtMeshCorner: + if (m_minimum_concave_corner_edge_count >= 2 + && m_minimum_concave_corner_edge_count <= ON_SubDVertex::MaximumEdgeCount + && m_minimum_concave_corner_angle_radians > ON_PI + && m_minimum_concave_corner_angle_radians <= ON_2PI + ) + return m_concave_corner_option; + break; + } + + return ON_SubDFromMeshParameters::ConcaveCornerOption::Unset; +} + +void ON_SubDFromMeshParameters::SetMinimumConcaveCornerEdgeCount( + unsigned int minimum_concave_corner_edge_count +) +{ + if (minimum_concave_corner_edge_count >= 2 && minimum_concave_corner_edge_count <= ON_SubDVertex::MaximumEdgeCount) + m_minimum_concave_corner_edge_count = (unsigned short)minimum_concave_corner_edge_count; +} + +unsigned int ON_SubDFromMeshParameters::MinimumConcaveCornerEdgeCount() const +{ + return m_minimum_concave_corner_edge_count; +} + +void ON_SubDFromMeshParameters::SetMinimumConcaveCornerAngleRadians( + double minimum_concave_corner_angle_radians +) +{ + if (minimum_concave_corner_angle_radians > ON_PI && minimum_concave_corner_angle_radians <= ON_2PI) + m_minimum_concave_corner_angle_radians = minimum_concave_corner_angle_radians; +} + +double ON_SubDFromMeshParameters::MinimumConcaveCornerAngleRadians() const +{ + return m_minimum_concave_corner_angle_radians; +} + +ON_SubDFromMeshParameters::ConcaveCornerOption ON_SubDFromMeshParameters::CopyConcaveCornerTest( + ON_SubDFromMeshParameters source_parameters +) +{ + SetConcaveCornerOption(source_parameters.GetConcaveCornerOption()); + SetMinimumConcaveCornerEdgeCount(source_parameters.MinimumConcaveCornerEdgeCount()); + SetMinimumConcaveCornerAngleRadians(source_parameters.MinimumConcaveCornerAngleRadians()); + return GetConcaveCornerOption(); +} + + bool ON_SubDFromMeshParameters::InterpolateMeshVertices() const { return m_bInterpolateMeshVertices; @@ -1611,6 +2087,38 @@ void ON_SubDFromMeshParameters::SetInterpolateMeshVertices( m_bInterpolateMeshVertices = false; } +ON_SubDFromMeshParameters::TextureCoordinatesOption ON_SubDFromMeshParameters::TextureCoordinatesOptionFromUnsigned( + unsigned int texture_coordinate_option_as_unsigned +) +{ + switch (texture_coordinate_option_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::TextureCoordinatesOption::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::TextureCoordinatesOption::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::TextureCoordinatesOption::Automatic); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::TextureCoordinatesOption::Packed); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::TextureCoordinatesOption::CopyMapping); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::TextureCoordinatesOption::CopyCoordinates); + default: + break; + } + return ON_SUBD_RETURN_ERROR(ON_SubDFromMeshParameters::TextureCoordinatesOption::Unset); +} + + +void ON_SubDFromMeshParameters::SetTextureCoordinatesOption( + ON_SubDFromMeshParameters::TextureCoordinatesOption texture_coorindates_option +) +{ + // use TextureCoordinatesOptionFromUnsigned to trap invalid input. + m_texture_coordinates_option = ON_SubDFromMeshParameters::TextureCoordinatesOptionFromUnsigned(static_cast(texture_coorindates_option)); +} + +ON_SubDFromMeshParameters::TextureCoordinatesOption ON_SubDFromMeshParameters::GetTextureCoordinatesOption() const +{ + return m_texture_coordinates_option; +} + bool ON_SubDFromMeshParameters::MergeColinearBoundaryEdges() const { // clear bit means true, set bit means false @@ -1653,32 +2161,17 @@ void ON_SubDFromMeshParameters::SetInteriorCreaseOption( m_interior_crease_option = ON_SubDFromMeshParameters::InteriorCreaseOptionFromUnsigned((unsigned int)interior_crease_option); } -ON_SubDFromMeshParameters::InteriorCreaseOption ON_SubDFromMeshParameters::InteriorCreaseTest() const +ON_SubDFromMeshParameters::InteriorCreaseOption ON_SubDFromMeshParameters::GetInteriorCreaseOption() const { return m_interior_crease_option; } -void ON_SubDFromMeshParameters::SetMinimumCreaseAngleRadians( - double minimum_crease_angle_radians - ) -{ - if (minimum_crease_angle_radians >= 0.0 && minimum_crease_angle_radians < ON_PI) - m_minimum_crease_angle_radians = minimum_crease_angle_radians; -} - - -double ON_SubDFromMeshParameters::MinimumCreaseAngleRadians() const -{ - return m_minimum_crease_angle_radians; -} - ON_SubDFromMeshParameters::InteriorCreaseOption ON_SubDFromMeshParameters::CopyInteriorCreaseTest( ON_SubDFromMeshParameters source_parameters ) { - SetInteriorCreaseOption(source_parameters.InteriorCreaseTest()); - SetMinimumCreaseAngleRadians(source_parameters.MinimumCreaseAngleRadians()); - return InteriorCreaseTest(); + SetInteriorCreaseOption(source_parameters.GetInteriorCreaseOption()); + return GetInteriorCreaseOption(); } ON_SubDFromMeshParameters::InteriorCreaseOption ON_SubDFromMeshParameters::InteriorCreaseOptionFromUnsigned( @@ -1687,23 +2180,11 @@ ON_SubDFromMeshParameters::InteriorCreaseOption ON_SubDFromMeshParameters::Inter { switch (interior_crease_option_as_unsigned) { - case (unsigned int)ON_SubDFromMeshParameters::InteriorCreaseOption::Unset: - return ON_SubDFromMeshParameters::InteriorCreaseOption::Unset; - break; - case (unsigned int)ON_SubDFromMeshParameters::InteriorCreaseOption::None: - return ON_SubDFromMeshParameters::InteriorCreaseOption::None; - break; - case (unsigned int)ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshCrease: - return ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshCrease; - break; - case (unsigned int)ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshEdge: - return ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshEdge; - break; - default: - break; + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::InteriorCreaseOption::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::InteriorCreaseOption::None); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshDoubleEdge); } - - return ON_SubDFromMeshParameters::InteriorCreaseOption::Unset; + return ON_SUBD_RETURN_ERROR(ON_SubDFromMeshParameters::InteriorCreaseOption::Unset); } @@ -1764,7 +2245,7 @@ unsigned int ON_SubDVertex::EdgeCount() const } unsigned int ON_SubDVertex::EdgeCount( - ON_SubD::EdgeTag edge_tag + ON_SubDEdgeTag edge_tag ) const { if (nullptr != m_edges) @@ -1997,35 +2478,44 @@ const ON_3dPoint ON_SubDVertex::ControlNetPoint() const bool ON_SubDVertex::IsSmooth() const { - return (ON_SubD::VertexTag::Smooth == m_vertex_tag); + return (ON_SubDVertexTag::Smooth == m_vertex_tag); } bool ON_SubDVertex::IsCrease() const { - return (ON_SubD::VertexTag::Crease == m_vertex_tag); + return (ON_SubDVertexTag::Crease == m_vertex_tag); } bool ON_SubDVertex::IsCorner() const { - return (ON_SubD::VertexTag::Corner == m_vertex_tag); + return (ON_SubDVertexTag::Corner == m_vertex_tag); } bool ON_SubDVertex::IsDart() const { - return (ON_SubD::VertexTag::Dart == m_vertex_tag); + return (ON_SubDVertexTag::Dart == m_vertex_tag); } bool ON_SubDVertex::IsCreaseOrCorner() const { - return (ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag); + return (ON_SubDVertexTag::Crease == m_vertex_tag || ON_SubDVertexTag::Corner == m_vertex_tag); } bool ON_SubDVertex::IsDartOrCreaseOrCorner() const { return ( - ON_SubD::VertexTag::Dart == m_vertex_tag - || ON_SubD::VertexTag::Crease == m_vertex_tag - || ON_SubD::VertexTag::Corner == m_vertex_tag + ON_SubDVertexTag::Dart == m_vertex_tag + || ON_SubDVertexTag::Crease == m_vertex_tag + || ON_SubDVertexTag::Corner == m_vertex_tag + ); +} + + +bool ON_SubDVertex::IsDartOrCrease() const +{ + return ( + ON_SubDVertexTag::Dart == m_vertex_tag + || ON_SubDVertexTag::Crease == m_vertex_tag ); } @@ -2033,12 +2523,12 @@ bool ON_SubDVertex::IsDartOrCreaseOrCorner() const bool ON_SubDVertex::IsSmoothOrDart() const { - return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag); + return (ON_SubDVertexTag::Smooth == m_vertex_tag || ON_SubDVertexTag::Dart == m_vertex_tag); } bool ON_SubDVertex::IsSmoothOrCrease() const { - return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Crease == m_vertex_tag); + return (ON_SubDVertexTag::Smooth == m_vertex_tag || ON_SubDVertexTag::Crease == m_vertex_tag); } bool ON_SubDVertex::GetBoundaryVertexEdges( @@ -2147,7 +2637,7 @@ const ON_SubDVertexEdgeProperties ON_SubDVertex::EdgeProperties() const bool ON_SubDEdge::IsCrease() const { - return (ON_SubD::EdgeTag::Crease == m_edge_tag) ? true : false; + return (ON_SubDEdgeTag::Crease == m_edge_tag) ? true : false; } @@ -2155,7 +2645,7 @@ bool ON_SubDEdge::IsHardCrease() const { return ( - ON_SubD::EdgeTag::Crease == m_edge_tag + ON_SubDEdgeTag::Crease == m_edge_tag && nullptr != m_vertex[0] && nullptr != m_vertex[1] && m_vertex[0]->IsCreaseOrCorner() @@ -2168,7 +2658,7 @@ bool ON_SubDEdge::IsHardCrease() const bool ON_SubDEdge::IsDartCrease() const { return - (ON_SubD::EdgeTag::Crease == m_edge_tag && DartCount() > 0 ) + (ON_SubDEdgeTag::Crease == m_edge_tag && DartCount() > 0 ) ? true : false; } @@ -2176,26 +2666,26 @@ bool ON_SubDEdge::IsDartCrease() const unsigned int ON_SubDEdge::DartCount() const { unsigned int dart_count = 0; - if (nullptr != m_vertex[0] && ON_SubD::VertexTag::Dart == m_vertex[0]->m_vertex_tag) + if (nullptr != m_vertex[0] && ON_SubDVertexTag::Dart == m_vertex[0]->m_vertex_tag) dart_count++; - if (nullptr != m_vertex[1] && ON_SubD::VertexTag::Dart == m_vertex[1]->m_vertex_tag) + if (nullptr != m_vertex[1] && ON_SubDVertexTag::Dart == m_vertex[1]->m_vertex_tag) dart_count++; return dart_count; } bool ON_SubDEdge::IsSmooth() const { - return (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::SmoothX == m_edge_tag) ? true : false; + return (ON_SubDEdgeTag::Smooth == m_edge_tag || ON_SubDEdgeTag::SmoothX == m_edge_tag) ? true : false; } bool ON_SubDEdge::IsSmoothNotX() const { - return (ON_SubD::EdgeTag::Smooth == m_edge_tag) ? true : false; + return (ON_SubDEdgeTag::Smooth == m_edge_tag) ? true : false; } bool ON_SubDEdge::IsSmoothX() const { - return (ON_SubD::EdgeTag::SmoothX == m_edge_tag) ? true : false; + return (ON_SubDEdgeTag::SmoothX == m_edge_tag) ? true : false; } bool ON_SubDVertex::IsSingleSectorVertex() const @@ -2232,12 +2722,12 @@ bool ON_SubDVertex::IsSingleSectorVertex() const continue; } } - else if (ON_SubD::EdgeTag::Crease == e->m_edge_tag) + else if (ON_SubDEdgeTag::Crease == e->m_edge_tag) { if (2 == e->m_face_count) { ++interior_crease_count; - if (ON_SubD::VertexTag::Dart == m_vertex_tag && 1 == interior_crease_count) + if (ON_SubDVertexTag::Dart == m_vertex_tag && 1 == interior_crease_count) continue; } else if (1 == e->m_face_count) @@ -2256,12 +2746,12 @@ bool ON_SubDVertex::IsSingleSectorVertex() const if (2 == boundary_crease_count && 2+interior_smooth_count == m_edge_count) return true; } - else if (ON_SubD::VertexTag::Dart == m_vertex_tag) + else if (ON_SubDVertexTag::Dart == m_vertex_tag) { if (1 == interior_crease_count && 1+interior_smooth_count == m_edge_count) return true; } - else if (ON_SubD::VertexTag::Smooth == m_vertex_tag) + else if (ON_SubDVertexTag::Smooth == m_vertex_tag) { if (interior_smooth_count == m_edge_count) return true; @@ -2356,7 +2846,7 @@ const ON_SubDComponentPtrPair ON_SubDVertex::CreasedEdgePair(bool bInteriorEdges continue; if (bInteriorEdgesOnly && false == e->HasInteriorEdgeTopology(false)) continue; - if (ON_SubD::EdgeTag::Crease == e->m_edge_tag) + if (ON_SubDEdgeTag::Crease == e->m_edge_tag) { if (e == ON_SUBD_EDGE_POINTER(creased_eptr_pair.m_pair[0].m_ptr) || e == ON_SUBD_EDGE_POINTER(creased_eptr_pair.m_pair[1].m_ptr)) { @@ -2387,7 +2877,7 @@ const ON_SubDEdgePtr ON_SubDVertex::CreasedEdge(bool bInteriorEdgesOnly) const continue; if (bInteriorEdgesOnly && false == e->HasInteriorEdgeTopology(false)) continue; - if (ON_SubD::EdgeTag::Crease == e->m_edge_tag) + if (ON_SubDEdgeTag::Crease == e->m_edge_tag) { if (creased_eptr.IsNull()) creased_eptr = m_edges[vei]; @@ -2399,6 +2889,44 @@ const ON_SubDEdgePtr ON_SubDVertex::CreasedEdge(bool bInteriorEdgesOnly) const return creased_eptr; } +const unsigned int ON_SubDVertex::CreasedEdgeCount( + bool bCountInteriorCreases, + bool bCountBoundaryCreases, + bool bCountNonmanifoldCreases, + bool bCountWireCreases +) const +{ + unsigned creased_edge_count = 0; + for (unsigned short vei = 0; vei < m_edge_count; ++vei) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); + if (nullptr == e) + continue; + if (ON_SubDEdgeTag::Crease != e->m_edge_tag) + continue; + switch (e->m_face_count) + { + case 0: + if (bCountWireCreases) + ++creased_edge_count; + break; + case 1: + if (bCountBoundaryCreases) + ++creased_edge_count; + break; + case 2: + if (bCountInteriorCreases) + ++creased_edge_count; + break; + default: + if (bCountNonmanifoldCreases) + ++creased_edge_count; + break; + } + } + return creased_edge_count; +} + bool ON_SubDVertexEdgeProperties::HasInteriorVertexTopology() const { return @@ -2455,30 +2983,30 @@ bool ON_SubDVertex::IsStandard() const bool bTaggedVertex = false; switch (m_vertex_tag) { - case ON_SubD::VertexTag::Unset: + case ON_SubDVertexTag::Unset: return false; break; - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: if (edge_count != face_count) return false; break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: if (edge_count != face_count+1) return false; crease_edge_face_count = 1; bTaggedVertex = true; break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: if (edge_count != face_count+1) return false; crease_edge_face_count = 1; bTaggedVertex = true; break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: if (edge_count != face_count) return false; crease_edge_face_count = 2; @@ -2521,7 +3049,7 @@ bool ON_SubDVertex::IsStandard() const if (nullptr == other_vertex) return false; - if (ON_SubD::EdgeTag::Smooth == e->m_edge_tag) + if (ON_SubDEdgeTag::Smooth == e->m_edge_tag) { if (2 != e->m_face_count) return false; @@ -2536,7 +3064,7 @@ bool ON_SubDVertex::IsStandard() const if (!(sector_weight == e->m_sector_coefficient[evi])) return false; - if (ON_SubD::VertexTag::Smooth == other_vertex->m_vertex_tag) + if (ON_SubDVertexTag::Smooth == other_vertex->m_vertex_tag) { if ( !(0.0 == e->m_sector_coefficient[1-evi]) ) return false; @@ -2549,7 +3077,7 @@ bool ON_SubDVertex::IsStandard() const return false; } } - else if (ON_SubD::EdgeTag::Crease == e->m_edge_tag) + else if (ON_SubDEdgeTag::Crease == e->m_edge_tag) { if (crease_edge_face_count != e->m_face_count) return false; @@ -2879,6 +3407,22 @@ unsigned ON_SubDEdge::VertexCount() const ; } +unsigned int ON_SubDEdge::VertexId( + unsigned evi +) const +{ + const ON_SubDVertex* v = Vertex(evi); + return (nullptr != v) ? v->m_id : 0U; +} + +const ON_SubDVertex* ON_SubDEdge::Vertex( + unsigned evi +) const +{ + return (evi >= 0 && evi <= 1) ? m_vertex[evi] : nullptr; +} + + unsigned int ON_SubDEdge::FaceCount() const { return m_face_count; @@ -3071,7 +3615,7 @@ const ON_SubDFace* ON_SubDEdge::NeighborFace( if ( nullptr == face || 2 != m_face_count ) return nullptr; // Do not stop at x tags - if (bStopAtCrease && ON_SubD::EdgeTag::Crease == m_edge_tag) + if (bStopAtCrease && ON_SubDEdgeTag::Crease == m_edge_tag) return nullptr; const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) }; if (nullptr == f[0] || nullptr == f[1] ) @@ -3097,7 +3641,7 @@ const ON_SubDFacePtr ON_SubDEdge::NeighborFacePtr( if ( nullptr == face || 2 != m_face_count ) return ON_SubDFacePtr::Null; // Do not stop at x tags - if (bStopAtCrease && ON_SubD::EdgeTag::Crease == m_edge_tag) + if (bStopAtCrease && ON_SubDEdgeTag::Crease == m_edge_tag) return ON_SubDFacePtr::Null; const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) }; if (nullptr == f[0] || nullptr == f[1] ) @@ -3154,14 +3698,6 @@ const ON_SubDEdge* ON_SubDEdge::AdjacentEdge( return ON_SUBD_EDGE_POINTER(AdjacentEdgePtr(edge_vertex_index, i).m_ptr); } -const class ON_SubDVertex* ON_SubDEdge::Vertex( - unsigned int i - ) const -{ - return (i <= 1) ? m_vertex[i] : nullptr; -} - - const ON_SubDVertex* ON_SubDEdge::OtherEndVertex( const ON_SubDVertex* vertex ) const @@ -3270,11 +3806,11 @@ const ON_3dVector ON_SubDEdge::ControlNetDirectionFrom( // { // ON__UINT_PTR eptr = m_edge4[fei].m_ptr; // const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr); -// if (nullptr == e || 2 != e->m_face_count || ON_SubD::EdgeTag::Smooth != e->m_edge_tag) +// if (nullptr == e || 2 != e->m_face_count || ON_SubDEdgeTag::Smooth != e->m_edge_tag) // return false; // ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(eptr); // const ON_SubDVertex* v = e->m_vertex[edir]; -// if (nullptr == v || false == v->IsOrdinary(subdivision_type,ON_SubD::VertexTag::Unset,bTestFaces)) +// if (nullptr == v || false == v->IsOrdinary(subdivision_type,ON_SubDVertexTag::Unset,bTestFaces)) // return false; // } // return true; @@ -3318,12 +3854,33 @@ void ON_SubDFace::CopyFrom( m_edge_count = 0; } - // RH-56133 need to copy texture coordinate information that was recently added. - m_texture_coordinate_origin[0] = src->m_texture_coordinate_origin[0]; - m_texture_coordinate_origin[1] = src->m_texture_coordinate_origin[1]; - m_texture_coordinate_delta[0] = src->m_texture_coordinate_delta[0]; - m_texture_coordinate_delta[1] = src->m_texture_coordinate_delta[1]; - m_texture_coordinate_bits = src->m_texture_coordinate_bits; + // RH-56133 need to copy packed coordinate information. + m_pack_id = src->m_pack_id; + m_pack_rect_origin[0] = src->m_pack_rect_origin[0]; + m_pack_rect_origin[1] = src->m_pack_rect_origin[1]; + m_pack_rect_size[0] = src->m_pack_rect_size[0]; + m_pack_rect_size[1] = src->m_pack_rect_size[1]; + m_pack_status_bits = src->m_pack_status_bits; + + // and need to copy texture coordinates + m_texture_status_bits = src->m_texture_status_bits; + const unsigned this_texture_point_capacity = this->TexturePointsCapacity(); + const unsigned texture_point_count = (src->TexturePointsAreSet() && this_texture_point_capacity >= this->EdgeCount()) ? this->EdgeCount() : 0; + if (texture_point_count >= 3) + { + // copy texture points + for (unsigned i = 0; i < texture_point_count; ++i) + m_texture_points[i] = src->m_texture_points[i]; + const unsigned texture_point_capacity = this->TexturePointsCapacity(); + for (unsigned i = texture_point_count; i < texture_point_capacity; ++i) + m_texture_points[i] = ON_3dPoint::NanPoint; + this->m_texture_status_bits |= ON_SubDFace::TextureStatusBits::TexturePointsSet; + } + else + { + // whatever created the face failed to allocate texture point memory. + this->m_texture_status_bits &= ON_SubDFace::TextureStatusBits::NotTexturePointsBitsMask; + } } const ON_SubDEdgePtr ON_SubDFace::EdgePtr( @@ -3664,7 +4221,7 @@ bool ON_SubDVertex::RemoveFaceFromArray(const ON_SubDFace * f) return true; } -ON_SubD::VertexTag ON_SubDVertex::SuggestedVertexTag( +ON_SubDVertexTag ON_SubDVertex::SuggestedVertexTag( bool bApplyInputTagBias, bool bReturnBestGuessWhenInvalid ) const @@ -3677,7 +4234,7 @@ ON_SubD::VertexTag ON_SubDVertex::SuggestedVertexTag( const unsigned edge_count = (nullptr != m_edges ? m_edge_count : 0U); if ( edge_count < 2) - return ON_SubD::VertexTag::Corner; + return ON_SubDVertexTag::Corner; for (unsigned vei = 0; vei < edge_count; ++vei) { @@ -3696,56 +4253,56 @@ ON_SubD::VertexTag ON_SubDVertex::SuggestedVertexTag( break; case 2: ++interior_count; - if (ON_SubD::EdgeTag::Crease == e->m_edge_tag) + if (ON_SubDEdgeTag::Crease == e->m_edge_tag) ++crease_count; break; default: // nonmanifold edge - return ON_SubD::VertexTag::Corner; + return ON_SubDVertexTag::Corner; break; } } if (crease_count >= 3) - return ON_SubD::VertexTag::Corner; + return ON_SubDVertexTag::Corner; if (wire_count > 0) { if (2 == wire_count && 0 == boundary_count && 0 == interior_count) - return (bApplyInputTagBias && ON_SubD::VertexTag::Corner == m_vertex_tag) ? ON_SubD::VertexTag::Corner : ON_SubD::VertexTag::Crease; - return ON_SubD::VertexTag::Corner; + return (bApplyInputTagBias && ON_SubDVertexTag::Corner == m_vertex_tag) ? ON_SubDVertexTag::Corner : ON_SubDVertexTag::Crease; + return ON_SubDVertexTag::Corner; } - ON_SubD::VertexTag best_guess_tag = ON_SubD::VertexTag::Unset; + ON_SubDVertexTag best_guess_tag = ON_SubDVertexTag::Unset; // crease_count >= 3 handled above switch (crease_count) { case 0: if (interior_count >= 2) - return ON_SubD::VertexTag::Smooth; + return ON_SubDVertexTag::Smooth; if (bReturnBestGuessWhenInvalid) { // can occure when there is a nullptr edge - best_guess_tag = ON_SubD::VertexTag::Smooth; + best_guess_tag = ON_SubDVertexTag::Smooth; } break; case 1: if (0 == boundary_count && interior_count >= 2) - return ON_SubD::VertexTag::Dart; + return ON_SubDVertexTag::Dart; if (bReturnBestGuessWhenInvalid) { // topology is far from valid and dart evaluation is very delicate. // We need more boundary edges, but using corner will at least give a well defined vertex surface point. - best_guess_tag = ON_SubD::VertexTag::Corner; + best_guess_tag = ON_SubDVertexTag::Corner; } break; case 2: if( (0 == boundary_count && interior_count >= 2) || (2 == boundary_count) ) - return (bApplyInputTagBias && ON_SubD::VertexTag::Corner == m_vertex_tag) ? ON_SubD::VertexTag::Corner : ON_SubD::VertexTag::Crease; + return (bApplyInputTagBias && ON_SubDVertexTag::Corner == m_vertex_tag) ? ON_SubDVertexTag::Corner : ON_SubDVertexTag::Crease; if (bReturnBestGuessWhenInvalid) { // topology is far from valid and dart evaluation is very delicate. // We need more boundary edges, but using corner will at least give a well defined vertex surface point. - best_guess_tag = ON_SubD::VertexTag::Corner; + best_guess_tag = ON_SubDVertexTag::Corner; } break; } @@ -3792,7 +4349,7 @@ bool ON_SubDEdge::RemoveFaceFromArray( } for (i = 2; i < m_face_count; i++) { - if (f == ON_SUBD_FACE_POINTER(m_facex[i - 4].m_ptr)) + if (f == ON_SUBD_FACE_POINTER(m_facex[i - 2].m_ptr)) { for (i++; i < m_face_count; i++) m_facex[i - 3] = m_facex[i - 2]; @@ -3916,17 +4473,15 @@ bool ON_SubDFace::RemoveEdgeFromArray( unsigned int j = i+1; - while (j < 4 && j < count ) - m_edge4[i++] = m_edge4[j++]; - - if (count > 4) + while (j < count) { - m_edge4[3] = m_edgex[0]; - i = 0; - j = 1; - count -= 4; - while (j < count ) - m_edgex[i++] = m_edgex[j++]; + const ON_SubDEdgePtr& edge_j = j < 4 ? m_edge4[j] : m_edgex[j - 4]; + if (i < 4) + m_edge4[i] = edge_j; + else + m_edgex[i - 4] = edge_j; + i++; + j++; } m_edge_count--; @@ -4149,11 +4704,16 @@ ON__UINT64 ON_SubD::RuntimeSerialNumber() const return (nullptr != subdimple) ? subdimple->RuntimeSerialNumber : 0; } - -ON__UINT64 ON_SubD::ContentSerialNumber() const +ON__UINT64 ON_SubD::GeometryContentSerialNumber() const { - ON_SubDimple* subdimple = m_subdimple_sp.get(); - return (nullptr != subdimple) ? subdimple->ContentSerialNumber() : 0; + const ON_SubDimple* subdimple = m_subdimple_sp.get(); + return (nullptr != subdimple) ? subdimple->GeometryContentSerialNumber() : 0; +} + +ON__UINT64 ON_SubD::RenderContentSerialNumber() const +{ + const ON_SubDimple* subdimple = m_subdimple_sp.get(); + return (nullptr != subdimple) ? subdimple->RenderContentSerialNumber() : 0; } ON__UINT64 ON_SubD::ComponentStatusSerialNumber() const @@ -4162,14 +4722,24 @@ ON__UINT64 ON_SubD::ComponentStatusSerialNumber() const return (nullptr != subdimple) ? subdimple->ComponentStatusSerialNumber() : 0; } -ON__UINT64 ON_SubD::ChangeContentSerialNumberForExperts( +ON__UINT64 ON_SubD::ChangeGeometryContentSerialNumberForExperts( bool bChangePreservesSymmetry ) { + // changes both ON_SubD::ContentSerialNumber and ON_SubD::RenderContentSerialNumber(). if (this == &ON_SubD::Empty) return 0; ON_SubDimple* subdimple = m_subdimple_sp.get(); - return (nullptr != subdimple) ? subdimple->ChangeContentSerialNumber(bChangePreservesSymmetry) : 0; + return (nullptr != subdimple) ? subdimple->ChangeGeometryContentSerialNumber(bChangePreservesSymmetry) : 0; +} + + +ON__UINT64 ON_SubD::ChangeRenderContentSerialNumber() const +{ + if (this == &ON_SubD::Empty) + return 0; + const ON_SubDimple* subdimple = m_subdimple_sp.get(); + return (nullptr != subdimple) ? subdimple->ChangeRenderContentSerialNumber() : 0; } ON_SubDComponentLocation ON_SubD::ToggleSubDAppearanceValue(ON_SubDComponentLocation subd_appearance) @@ -4291,8 +4861,8 @@ static bool IsValidVertexEdgeLink( if ( edge->IsSmooth() ) { - // edge->m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX - if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag) + // edge->m_edge_tag is ON_SubDEdgeTag::Smooth or ON_SubDEdgeTag::SmoothX + if (ON_SubDVertexTag::Smooth == vertex->m_vertex_tag) { if (false == (0.0 == edge->m_sector_coefficient[end_index])) return ON_SubDIsNotValid(bSilentError); @@ -4300,7 +4870,7 @@ static bool IsValidVertexEdgeLink( else { const unsigned int tagged_end_index = edge->TaggedEndIndex(); - if (ON_SubD::EdgeTag::SmoothX == edge->m_edge_tag) + if (ON_SubDEdgeTag::SmoothX == edge->m_edge_tag) { if (2 != tagged_end_index) return ON_SubDIsNotValid(bSilentError); @@ -4323,16 +4893,16 @@ static bool IsValidVertexEdgeLink( return ON_SubDIsNotValid(bSilentError); } } - else if(ON_SubD::EdgeTag::Crease == edge->m_edge_tag) + else if(ON_SubDEdgeTag::Crease == edge->m_edge_tag) { // crease edge if (!(0.0 == edge->m_sector_coefficient[end_index])) return ON_SubDIsNotValid(bSilentError); - if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag) + if (ON_SubDVertexTag::Smooth == vertex->m_vertex_tag) return ON_SubDIsNotValid(bSilentError); - if (ON_SubD::VertexTag::Unset == vertex->m_vertex_tag) + if (ON_SubDVertexTag::Unset == vertex->m_vertex_tag) return ON_SubDIsNotValid(bSilentError); } else @@ -4529,11 +5099,11 @@ static bool IsValidSubDVertexTag( switch (vertex->m_vertex_tag) { - case ON_SubD::VertexTag::Unset: + case ON_SubDVertexTag::Unset: return ON_SubDIsNotValid(bSilentError); break; - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); @@ -4548,7 +5118,7 @@ static bool IsValidSubDVertexTag( } break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); @@ -4558,7 +5128,7 @@ static bool IsValidSubDVertexTag( } break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); @@ -4576,7 +5146,7 @@ static bool IsValidSubDVertexTag( } break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); @@ -4608,7 +5178,7 @@ static bool IsValidSubDEdgeTag( if (nullptr == edge) return true; // this error detected elsewhere. - //ON_SubD::VertexTag vtag[2] = { ON_SubD::VertexTag::Unset,ON_SubD::VertexTag::Unset }; + //ON_SubDVertexTag vtag[2] = { ON_SubDVertexTag::Unset,ON_SubDVertexTag::Unset }; unsigned int smooth_vertex_count = 0; unsigned int crease_vertex_count = 0; unsigned int corner_vertex_count = 0; @@ -4620,16 +5190,16 @@ static bool IsValidSubDEdgeTag( return true; // topology errors detected elsewhere switch (edge->m_vertex[evi]->m_vertex_tag) { - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: ++smooth_vertex_count; break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: ++crease_vertex_count; break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: ++corner_vertex_count; break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: ++dart_vertex_count; break; } @@ -4644,23 +5214,23 @@ static bool IsValidSubDEdgeTag( switch(edge->m_edge_tag) { - case ON_SubD::EdgeTag::Unset: + case ON_SubDEdgeTag::Unset: return ON_SubDIsNotValid(bSilentError); break; - case ON_SubD::EdgeTag::Smooth: + case ON_SubDEdgeTag::Smooth: if ( 2 != edge->m_face_count) return ON_SubDIsNotValid(bSilentError); if ( smooth_vertex_count < 1) return ON_SubDIsNotValid(bSilentError); break; - case ON_SubD::EdgeTag::Crease: + case ON_SubDEdgeTag::Crease: if ( 0 != smooth_vertex_count ) return ON_SubDIsNotValid(bSilentError); break; - case ON_SubD::EdgeTag::SmoothX: + case ON_SubDEdgeTag::SmoothX: if ( 2 != edge->m_face_count) return ON_SubDIsNotValid(bSilentError); if ( 0 != smooth_vertex_count ) @@ -4703,7 +5273,7 @@ static bool IsValidSubDVertex( if (vertex->m_edge_count < vertex->m_face_count) { - if ( ON_SubD::VertexTag::Corner != vertex->m_vertex_tag || vertex->m_edge_count < 3 ) + if ( ON_SubDVertexTag::Corner != vertex->m_vertex_tag || vertex->m_edge_count < 3 ) return ON_SubDIsNotValid(bSilentError); } @@ -4715,22 +5285,22 @@ static bool IsValidSubDVertex( switch (vertex->m_vertex_tag) { - case ON_SubD::VertexTag::Smooth: // interior vertex + case ON_SubDVertexTag::Smooth: // interior vertex if (vertex->m_edge_count != vertex->m_face_count) return ON_SubDIsNotValid(bSilentError); break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: if ( vertex->m_edge_count < 2 ) return ON_SubDIsNotValid(bSilentError); break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: if ( vertex->m_edge_count < 1 ) return ON_SubDIsNotValid(bSilentError); break; - case ON_SubD::VertexTag::Dart: // interior vertex + case ON_SubDVertexTag::Dart: // interior vertex if (level > 0 && ordinary_valence_count != vertex->m_edge_count) return ON_SubDIsNotValid(bSilentError); if (vertex->m_edge_count != vertex->m_face_count) @@ -4800,11 +5370,11 @@ static bool IsValidSubDEdge( if (edge->IsSmooth()) { - // m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX + // m_edge_tag is ON_SubDEdgeTag::Smooth or ON_SubDEdgeTag::SmoothX if ( 2 != edge->m_face_count) return ON_SubDIsNotValid(bSilentError); } - else if (ON_SubD::EdgeTag::Crease != edge->m_edge_tag) + else if (ON_SubDEdgeTag::Crease != edge->m_edge_tag) { return ON_SubDIsNotValid(bSilentError); } @@ -5242,19 +5812,141 @@ unsigned int ON_SubD::DumpTopology( const unsigned int level_count = LevelCount(); const unsigned int active_level_index = ActiveLevel().m_level_index; - const ON__UINT64 runtime_sn = (text_log.IsTextHash()) ? 0 : RuntimeSerialNumber(); // TextHash ignores settings that don't depend on 3dm file content. + const bool bIsTextHash = text_log.IsTextHash(); + + // TextHash ignores settings that don't depend on 3dm file content. + const ON__UINT64 runtime_sn = (bIsTextHash) ? 0 : RuntimeSerialNumber(); + const ON__UINT64 geometry_content_sn = (bIsTextHash) ? 0 : this->GeometryContentSerialNumber(); + const ON__UINT64 render_content_sn = (bIsTextHash) ? 0 : this->RenderContentSerialNumber(); - const ON_wString subd_texture_domain = ON_SubD::TextureDomainTypeToString(this->TextureDomainType()); if (level_count > 1) - text_log.Print(L"SubD[%" PRIu64 "]: %u levels. Level %u is active. texture domain type = %ls.\n", + text_log.Print(L"SubD[%" PRIu64 "]: %u levels. Level %u is active.\n", runtime_sn, level_count, - active_level_index, - static_cast(subd_texture_domain) + active_level_index ); else - text_log.Print(L"SubD[%" PRIu64 "]: texture domain type = %ls.\n", runtime_sn, static_cast(subd_texture_domain)); + text_log.Print(L"SubD[%" PRIu64 "]:\n", runtime_sn); + text_log.Print(L"Texture coordinate settings:\n"); + { + const ON_SubDTextureCoordinateType subd_texture_coordinate_type = this->TextureCoordinateType(); + ON_TextLogIndent indent1(text_log); + const ON_wString subd_texture_coordinate_type_as_string = ON_SubD::TextureCoordinateTypeToString(this->TextureCoordinateType()); + text_log.Print(L"TextureCoordinateType() = %ls\n", static_cast(subd_texture_coordinate_type_as_string)); + + const bool bShowMappingTag + = false == bIsTextHash + || ON_SubDTextureCoordinateType::FromMapping == subd_texture_coordinate_type + ; + + if (bShowMappingTag) + { + const ON_MappingTag mapping_tag = this->TextureMappingTag(true); + + const bool bSurfaceParameterMappingTag + = (0 == ON_MappingTag::CompareAll(ON_MappingTag::SurfaceParameterMapping, mapping_tag)) + || (bIsTextHash && (ON_TextureMapping::TYPE::srfp_mapping == mapping_tag.m_mapping_type || ON_MappingTag::SurfaceParameterMapping.m_mapping_id == mapping_tag.m_mapping_id)) + ; + + const bool bUnsetMappingTag + = false == bSurfaceParameterMappingTag + && (0 == ON_MappingTag::CompareAll(ON_MappingTag::Unset, mapping_tag)) + || (bIsTextHash && (ON_TextureMapping::TYPE::no_mapping == mapping_tag.m_mapping_type || ON_nil_uuid == mapping_tag.m_mapping_id)) + ; + + // NOTE: the mapping tag is only applied when subd_texture_coordinate_type = FromMapping + if (ON_SubDTextureCoordinateType::FromMapping == subd_texture_coordinate_type && false == bUnsetMappingTag) + text_log.Print(L"TextureMappingTag()"); + else + text_log.Print(L"Inactive TextureMappingTag()"); + + if (bUnsetMappingTag) + text_log.Print(L" = ON_MappingTag::Unset\n"); + else if (bSurfaceParameterMappingTag) + text_log.Print(L" = ON_MappingTag::SurfaceParameterMapping\n"); + else + { + text_log.Print(":\n"); + const ON_TextLogIndent indent2(text_log); + text_log.Print("m_mapping_type = "); + switch (mapping_tag.m_mapping_type) + { + case ON_TextureMapping::TYPE::no_mapping: + text_log.Print("none"); + break; + case ON_TextureMapping::TYPE::srfp_mapping: + text_log.Print("srfp"); + break; + case ON_TextureMapping::TYPE::plane_mapping: + text_log.Print("plane"); + break; + case ON_TextureMapping::TYPE::cylinder_mapping: + text_log.Print("cylinder"); + break; + case ON_TextureMapping::TYPE::sphere_mapping: + text_log.Print("sphere"); + break; + case ON_TextureMapping::TYPE::box_mapping: + text_log.Print("box"); + break; + case ON_TextureMapping::TYPE::mesh_mapping_primitive: + text_log.Print("mesh primative"); + break; + case ON_TextureMapping::TYPE::srf_mapping_primitive: + text_log.Print("srf primative"); + break; + case ON_TextureMapping::TYPE::brep_mapping_primitive: + text_log.Print("brep primative"); + break; + case ON_TextureMapping::TYPE::ocs_mapping: + text_log.Print("ocs"); + break; + } + text_log.PrintNewLine(); + text_log.Print("m_mapping_id = "); + text_log.Print(mapping_tag.m_mapping_id); + if (mapping_tag.m_mapping_id == ON_MappingTag::SurfaceParameterMapping.m_mapping_id) + text_log.Print(" = ON_MappingTag::SurfaceParameterMapping.m_mapping_id"); + text_log.PrintNewLine(); + text_log.Print("m_mapping_crc = %08x\n", mapping_tag.m_mapping_crc); + text_log.Print("m_mesh_xform:\n"); + text_log.PushIndent(); + text_log.Print(mapping_tag.m_mesh_xform); + text_log.PopIndent(); + } + + } + + const ON_SHA1_Hash subd_texture_settings_hash = this->TextureSettingsHash(); + text_log.Print(L"TextureSettingsHash() = "); + subd_texture_settings_hash.Dump(text_log); + text_log.PrintNewLine(); + + if (false == text_log.IsTextHash()) + { + // runtime settings most recentltly used to set fragmant texture coordinates. + const ON_SHA1_Hash frament_texture_settings_hash = this->FragmentTextureCoordinatesTextureSettingsHash(); + text_log.Print(L"FragmentTextureCoordinatesTextureSettingsHash() = "); + if (subd_texture_settings_hash == frament_texture_settings_hash) + text_log.Print(L"TextureSettingsHash()"); + else + frament_texture_settings_hash.Dump(text_log); + text_log.PrintNewLine(); + } + + if (false == text_log.IsTextHash()) + { + const ON_SHA1_Hash subd_fragment_color_settings_hash = this->FragmentColorsSettingsHash(); + text_log.Print(L"FragmentColorsSettingsHash() = "); + subd_fragment_color_settings_hash.Dump(text_log); + text_log.PrintNewLine(); + } + } + + + text_log.Print(L"Geometry content serial number = %" PRIu64 "\n", geometry_content_sn); + text_log.Print(L"Render content serial number = %" PRIu64 "\n", render_content_sn); text_log.Print(L"Levels:\n"); @@ -5294,6 +5986,185 @@ unsigned int ON_SubD::DumpTopology( return error_count; } +static const ON_SHA1_Hash Internal_VertexHash(const ON_SubDVertex* first_vertex) +{ + ON_SHA1 sha1; + for (const ON_SubDVertex* v = first_vertex; nullptr != v; v = v->m_next_vertex) + { + sha1.AccumulateInteger32(v->m_id); + sha1.AccumulateBytes(&v->m_vertex_tag,sizeof(v->m_vertex_tag)); + sha1.AccumulateDoubleArray(3, v->m_P); + } + return sha1.Hash(); +} + +static const ON_SHA1_Hash Internal_EdgeHash(const ON_SubDEdge* first_edge) +{ + ON_SHA1 sha1; + for (const ON_SubDEdge* e = first_edge; nullptr != e; e = e->m_next_edge) + { + sha1.AccumulateInteger32(e->m_id); + sha1.AccumulateBytes(&e->m_edge_tag, sizeof(e->m_edge_tag)); + sha1.AccumulateInteger32(e->VertexId(0)); + sha1.AccumulateInteger32(e->VertexId(1)); + } + return sha1.Hash(); +} + +static const ON_SHA1_Hash Internal_FaceHash(const ON_SubDFace* first_face) +{ + ON_SHA1 sha1; + for (const ON_SubDFace* f = first_face; nullptr != f; f = f->m_next_face) + { + sha1.AccumulateInteger32(f->m_id); + sha1.AccumulateInteger16(f->m_edge_count); + const ON_SubDEdgePtr* eptr = f->m_edge4; + for (unsigned short fei = 0; fei < f->m_edge_count; ++fei, ++eptr) + { + if (4 == fei) + { + eptr = f->m_edgex; + if (nullptr == eptr) + break; + } + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); + if (nullptr == e) + continue; + sha1.AccumulateInteger32(e->m_id); + if (0 != ON_SUBD_EDGE_DIRECTION(eptr->m_ptr)) + sha1.AccumulateBool(true); + } + } + return sha1.Hash(); +} + + +static void Internal_AccumulateFragmentArrayHash(ON_SHA1& sha1, size_t dim, const double* a, unsigned count, size_t stride) +{ + if (nullptr != a && count > 0 && dim > 0 && (0 == stride || stride >= dim)) + { + sha1.AccumulateInteger32((unsigned int)dim); + sha1.AccumulateInteger32(count); + for (unsigned i = 0; i < count; ++i) + { + sha1.AccumulateDoubleArray(dim, a); + a += stride; + } + } +} + +static const ON_SHA1_Hash Internal_PackRectHash(const ON_SubDFace* first_face) +{ + ON_SHA1 sha1; + for (const ON_SubDFace* f = first_face; nullptr != f; f = f->m_next_face) + { + if (false == f->PackRectIsSet()) + continue; + sha1.AccumulateInteger32(f->m_id); + sha1.AccumulateInteger16(f->m_edge_count); + + // The 4 f->TextureDomain...() values specify + // the portion and orientation of texture space + // this face uses. + sha1.AccumulateInteger32(f->PackRectRotationDegrees()); + const ON_2dPoint p = f->PackRectOrigin(); + sha1.Accumulate2dPoint(p); + const ON_2dVector v = f->PackRectSize(); + sha1.Accumulate2dVector(v); + } + return sha1.Hash(); +} + +static const ON_SHA1_Hash Internal_TextureCoordinatesHash(const ON_SubDFace* first_face) +{ + ON_SHA1 sha1; + bool bNotEmpty = false; + for (const ON_SubDFace* f = first_face; nullptr != f; f = f->m_next_face) + { + if (false == f->PackRectIsSet()) + continue; + const ON_SubDMeshFragment* first_frag = f->MeshFragments(); + if (nullptr == first_frag) + continue; + sha1.AccumulateInteger32(f->m_id); + for (const ON_SubDMeshFragment* frag = first_frag; nullptr != frag; frag = frag->NextFaceFragment(false)) + { + const double* a = frag->TextureCoordinateArray(ON_SubDComponentLocation::Surface); + const unsigned count = frag->TextureCoordinateCount(); + if (nullptr != a && count > 0) + { + bNotEmpty = true; + Internal_AccumulateFragmentArrayHash(sha1, 3, a, count, frag->TextureCoordinateArrayStride(ON_SubDComponentLocation::Surface)); + } + } + } + return bNotEmpty ? sha1.Hash() : ON_SHA1_Hash::EmptyContentHash; +} + +static const ON_SHA1_Hash Internal_FragmentColorsHash(const ON_SubDFace* first_face) +{ + ON_SHA1 sha1; + bool bNotEmpty = false; + for (const ON_SubDFace* f = first_face; nullptr != f; f = f->m_next_face) + { + const ON_SubDMeshFragment* first_frag = f->MeshFragments(); + if (nullptr == first_frag) + continue; + bool bAccumulateId = true; + for (const ON_SubDMeshFragment* frag = first_frag; nullptr != frag; frag = frag->NextFaceFragment(false)) + { + if (frag->ColorsExist()) + { + const ON_Color* a = frag->ColorArray(ON_SubDComponentLocation::Surface); + const unsigned count = frag->ColorArrayCount(ON_SubDComponentLocation::Surface); + if (nullptr != a && count > 0) + { + bNotEmpty = true; + if (bAccumulateId) + { + sha1.AccumulateInteger32(f->m_id); + bAccumulateId = false; + } + sha1.AccumulateInteger32Array(count, (const ON__INT32*)a); + } + } + } + } + return bNotEmpty ? sha1.Hash() : ON_SHA1_Hash::EmptyContentHash; +} + +static const ON_SHA1_Hash Internal_FragmentCurvaturesHash(const ON_SubDFace* first_face) +{ + ON_SHA1 sha1; + bool bNotEmpty = false; + for (const ON_SubDFace* f = first_face; nullptr != f; f = f->m_next_face) + { + const ON_SubDMeshFragment* first_frag = f->MeshFragments(); + if (nullptr == first_frag) + continue; + bool bAccumulateId = true; + for (const ON_SubDMeshFragment* frag = first_frag; nullptr != frag; frag = frag->NextFaceFragment(false)) + { + if (frag->CurvaturesExist()) + { + const ON_SurfaceCurvature* a = frag->CurvatureArray(ON_SubDComponentLocation::Surface); + const unsigned count = frag->CurvatureArrayCount(ON_SubDComponentLocation::Surface); + if (nullptr != a && count > 0) + { + bNotEmpty = true; + if (bAccumulateId) + { + sha1.AccumulateInteger32(f->m_id); + bAccumulateId = false; + } + Internal_AccumulateFragmentArrayHash(sha1, 2, (const double*)a, count, 2); + } + } + } + } + return bNotEmpty ? sha1.Hash() : ON_SHA1_Hash::EmptyContentHash; +} + unsigned int ON_SubDLevel::DumpTopology( const unsigned int validate_max_vertex_id, const unsigned int validate_max_edge_id, @@ -5451,6 +6322,40 @@ unsigned int ON_SubDLevel::DumpTopology( if (IsEmpty()) return 0; + // The hash uniquely identifies the subd level topology and geometry. + const ON_SHA1_Hash vhash = Internal_VertexHash(m_vertex[0]); + const ON_SHA1_Hash ehash = Internal_EdgeHash(m_edge[0]); + const ON_SHA1_Hash fhash = Internal_FaceHash(m_face[0]); + + ON_SHA1 level_sha1; + level_sha1.AccumulateSubHash(vhash); + level_sha1.AccumulateSubHash(ehash); + level_sha1.AccumulateSubHash(fhash); + const ON_wString hashstr = level_sha1.Hash().ToString(true); + text_log.Print(L"Level SubD geometry SHA1 = %ls\n", static_cast(hashstr)); + + const ON_wString vhashstr = vhash.ToStringEx(true); + const ON_wString ehashstr = ehash.ToStringEx(true); + const ON_wString fhashstr = fhash.ToStringEx(true); + text_log.Print(L"Vertices SHA1 = %ls\n", static_cast(vhashstr)); + text_log.Print(L"Edges SHA1 = %ls\n", static_cast(ehashstr)); + text_log.Print(L"Faces SHA1 = %ls\n", static_cast(fhashstr)); + + if (false == text_log.IsTextHash()) + { + const ON_SHA1_Hash prhash = Internal_PackRectHash(m_face[0]); + const ON_SHA1_Hash tchash = Internal_TextureCoordinatesHash(m_face[0]); + const ON_SHA1_Hash chash = Internal_FragmentColorsHash(m_face[0]); + const ON_SHA1_Hash khash = Internal_FragmentCurvaturesHash(m_face[0]); + const ON_wString prhashstr = prhash.ToStringEx(true); + const ON_wString tchashstr = tchash.ToStringEx(true); + const ON_wString chashstr = chash.ToStringEx(true); + const ON_wString khashstr = khash.ToStringEx(true); + text_log.Print(L"Faces pack rectangles SHA1 = %ls\n", static_cast(prhashstr)); + text_log.Print(L"Faces fragment texture coordinates SHA1 = %ls\n", static_cast(tchashstr)); + text_log.Print(L"Faces fragment vertex colors SHA1 = %ls\n", static_cast(chashstr)); + text_log.Print(L"Faces fragment vertex curvatures SHA1 = %ls\n", static_cast(khashstr)); + } unsigned int damaged_vertex_count = 0; unsigned int damaged_edge_count = 0; unsigned int damaged_face_count = 0; @@ -5593,39 +6498,26 @@ unsigned int ON_SubDLevel::DumpTopology( const ON_3dPoint P0(v->ControlNetPoint()); - ON_wString vtag; - switch (v->m_vertex_tag) - { - case ON_SubD::VertexTag::Unset: - vtag = L"Unset"; - break; - case ON_SubD::VertexTag::Smooth: - vtag = L"Smooth"; - break; - case ON_SubD::VertexTag::Crease: - vtag = L"Crease"; - break; - case ON_SubD::VertexTag::Corner: - vtag = L"Corner"; - break; - case ON_SubD::VertexTag::Dart: - vtag = L"Dart"; - break; - default: - vtag = L"INVALID"; - break; - } + const ON_wString vtag = ON_SubD::VertexTagToString(v->m_vertex_tag,false); if (bSkippedPreviousComponent) { text_log.Print(L"...\n"); bSkippedPreviousComponent = false; } + if (v->m_group_id > 0) + { + text_log.Print("v%u: group_id=%u ", v->m_id, v->m_group_id); + } + else + { + text_log.Print("v%u: ", v->m_id); + } + if (bIsDamaged) { text_log.Print( - "v%u: (DAMAGED) %ls (%g, %g, %g)\n", - v->m_id, + "(DAMAGED) %ls (%g, %g, %g)\n", static_cast(vtag), P0.x, P0.y, P0.z ); @@ -5633,8 +6525,7 @@ unsigned int ON_SubDLevel::DumpTopology( else { text_log.Print( - "v%u: %ls (%g, %g, %g)\n", - v->m_id, + "%ls (%g, %g, %g)\n", static_cast(vtag), P0.x, P0.y, P0.z ); @@ -5807,54 +6698,41 @@ unsigned int ON_SubDLevel::DumpTopology( edge_dump_count++; ON_TextLogIndent eindent(text_log); - ON_wString etag; - switch (e->m_edge_tag) - { - case ON_SubD::EdgeTag::Unset: - etag = L"Unset"; - break; - case ON_SubD::EdgeTag::Smooth: - etag = L"Smooth"; - break; - case ON_SubD::EdgeTag::Crease: - etag = L"Crease"; - break; - case ON_SubD::EdgeTag::SmoothX: - etag = L"SmmothX"; - break; - default: - etag = L"INVALID"; - break; - } + const ON_wString etag = ON_SubD::EdgeTagToString(e->m_edge_tag,false); if (bSkippedPreviousComponent) { text_log.Print(L"...\n"); bSkippedPreviousComponent = false; } + if (e->m_group_id > 0) + { + text_log.Print("e%u: group_id=%u ", e->m_id, e->m_group_id); + } + else + { + text_log.Print("e%u: ", e->m_id); + } if (bIsDamaged) { if (bIsWireEdge) { text_log.Print( - "e%u: (DAMAGED) %ls wire (", - e->m_id, + "(DAMAGED) %ls wire (", static_cast(etag) ); } else if (bIsNonmanifoldEdge) { text_log.Print( - "e%u: (DAMAGED) %ls nonmanifold (", - e->m_id, + "(DAMAGED) %ls nonmanifold (", static_cast(etag) ); } else { text_log.Print( - "e%u: (DAMAGED) %ls (", - e->m_id, + "(DAMAGED) %ls (", static_cast(etag) ); } @@ -5864,24 +6742,21 @@ unsigned int ON_SubDLevel::DumpTopology( if (bIsWireEdge) { text_log.Print( - "e%u: wire %ls (", - e->m_id, + "wire %ls (", static_cast(etag) ); } else if (bIsNonmanifoldEdge) { text_log.Print( - "e%u: nonmanifold %ls (", - e->m_id, + "nonmanifold %ls (", static_cast(etag) ); } else { text_log.Print( - "e%u: %ls (", - e->m_id, + "%ls (", static_cast(etag) ); } @@ -6049,6 +6924,14 @@ unsigned int ON_SubDLevel::DumpTopology( f->m_id ); } + else if (f->m_group_id > 0) + { + text_log.Print( + "f%u: group_id=%u\n", + f->m_id, + f->m_group_id + ); + } else { text_log.Print( @@ -6144,6 +7027,25 @@ unsigned int ON_SubDLevel::DumpTopology( } text_log.Print(" }\n"); + if (f->TexturePointsAreSet()) + { + text_log.Print("f.TexturePoints[%u] = {", face_edge_count); + prefix[0] = ON_String::Space; + prefix[1] = 0; + for (unsigned int fei = 0; fei < face_edge_count; fei++) + { + if (1 == fei) + { + prefix[0] = ','; + prefix[1] = ON_String::Space; + prefix[2] = 0; + } + const ON_3dPoint tp = f->TexturePoint(fei); + text_log.Print("%s(%g,%g,%g)", prefix, tp.x, tp.y, tp.z); + } + text_log.Print(" }\n"); + } + bool bNeedComma = false; @@ -6174,26 +7076,40 @@ unsigned int ON_SubDLevel::DumpTopology( if (bNeedComma) text_log.PrintNewLine(); - if (f->TextureDomainIsSet()) + if (f->PackRectIsSet()) { bNeedComma = true; - const ON_wString s = ON_SubD::TextureDomainTypeToString(f->TextureDomainType()); const bool bGridOrder = true; - const bool bNormalized = false; ON_2dPoint corners[4] = { - f->TextureDomainCorner(bGridOrder,bNormalized,0), - f->TextureDomainCorner(bGridOrder,bNormalized,1), - f->TextureDomainCorner(bGridOrder,bNormalized,2), - f->TextureDomainCorner(bGridOrder,bNormalized,3) + f->PackRectCorner(bGridOrder,0), + f->PackRectCorner(bGridOrder,1), + f->PackRectCorner(bGridOrder,2), + f->PackRectCorner(bGridOrder,3) }; - text_log.Print("%ls texture domain. Grid corners: (%g,%g), (%g,%g), (%g,%g), (%g,%g)", - static_cast(s), + text_log.Print(L"PackId=%u Pack rectangle corners: (%g,%g), (%g,%g), (%g,%g), (%g,%g)", + f->PackId(), corners[0].x, corners[0].y, corners[1].x, corners[1].y, corners[2].x, corners[2].y, corners[3].x, corners[3].y ); - text_log.PrintNewLine(); + } + else + { + text_log.Print("Pack rectangle is not set."); + } + text_log.PrintNewLine(); + + if (false == text_log.IsTextHash()) + { + const ON_SubDMeshFragment* first_frag = f->MeshFragments(); + if (nullptr != first_frag) + { + text_log.Print(L"Fragments:\n"); + const ON_TextLogIndent indent(text_log); + for (const ON_SubDMeshFragment* frags = first_frag; nullptr != frags; frags = frags->NextFaceFragment(false)) + frags->Dump(text_log); + } } text_log.PopIndent(); @@ -6242,6 +7158,117 @@ unsigned int ON_SubDLevel::DumpTopology( return topology_error_count; } +static void Internal_DumpFragmentArray(ON_TextLog& text_log, const wchar_t* description, size_t dim, const double* a, unsigned count, size_t stride) +{ + if (nullptr != a && count > 0 && dim > 0 && stride >= dim) + { + text_log.Print(L"%ls = ", description); + if (4 == count) + { + text_log.Print("{"); + for (unsigned i = 0; i < count; ++i) + { + if (0 != i) + text_log.Print(L","); + text_log.Print(L"(%g", a[0]); + for (unsigned j = 1; j < dim; ++j) + text_log.Print(L",%g", a[j]); + text_log.Print(L")"); + a += stride; + } + text_log.Print("}"); + } + else + { + for (size_t i = 0; i < dim; ++i) + text_log.Print(L"%g,", a[i]); + ON_SHA1 sha1; + Internal_AccumulateFragmentArrayHash(sha1, dim, a, count, stride); + const ON_wString s = sha1.Hash().ToString(true); + text_log.Print(L" ... SHA1 hash=%ls", static_cast(s)); + } + } + else + { + text_log.Print(L"%ls: Not set.",description); + } + text_log.PrintNewLine(); +} + +void ON_SubDMeshFragment::Dump(ON_TextLog& text_log) const +{ + const unsigned count = VertexCount(); + text_log.Print("ON_SubDMeshFragment: vertex count = %u", count); + const unsigned n = m_grid.SideSegmentCount(); + const unsigned grid_point_count = m_grid.GridPointCount(); + if (count > 0) + { + if (n > 0 && count == grid_point_count) + { + text_log.Print( + L", %u x %u grid\n", + n, n + ); + + ON_TextLogIndent indent1(text_log); + text_log.Print( + L"bounding box (%g to %g X %g to %g X %g to %g)\n", + m_surface_bbox.m_min.x, m_surface_bbox.m_max.x, + m_surface_bbox.m_min.y, m_surface_bbox.m_max.y, + m_surface_bbox.m_min.z, m_surface_bbox.m_max.z + ); + + text_log.Print( + L"pack rect (%g,%g),(%g,%g),(%g,%g),(%g,%g) \n", + m_pack_rect[0][0], m_pack_rect[0][1], + m_pack_rect[1][0], m_pack_rect[1][1], + m_pack_rect[2][0], m_pack_rect[2][1], + m_pack_rect[3][0], m_pack_rect[3][1] + ); + + for (int pass = 0; pass < 2; ++pass) + { + const ON_SubDComponentLocation cl = (0 == pass) ? ON_SubDComponentLocation::ControlNet : ON_SubDComponentLocation::Surface; + if (ON_SubDComponentLocation::ControlNet == cl) + text_log.Print("Corners:\n"); + else if (ON_SubDComponentLocation::Surface == cl) + text_log.Print("Surface:\n"); + ON_TextLogIndent ident2(text_log); + Internal_DumpFragmentArray( + text_log, L"points", 3, + PointArray(cl), + PointArrayCount(cl), + PointArrayStride(cl)); + Internal_DumpFragmentArray( + text_log, L"normals", 3, + NormalArray(cl), + NormalArrayCount(cl), + NormalArrayStride(cl)); + Internal_DumpFragmentArray( + text_log, L"texture coordinates", 3, // tcs are uvw + TextureCoordinateArray(cl), + TextureCoordinateArrayCount(cl), + TextureCoordinateArrayStride(cl)); + Internal_DumpFragmentArray( + text_log, L"curvatures", 2, // 2 principal + (const double*)(CurvatureArray(cl)), + CurvatureArrayCount(cl), + sizeof(ON_SurfaceCurvature)/sizeof(double) + ); + } + } + else + { + text_log.Print(L". Invalid fragment.\n"); + } + } + else + { + text_log.PrintNewLine(); + } +} + + //virtual unsigned int ON_SubD::SizeOf() const @@ -6282,7 +7309,7 @@ void ON_SubD::DestroyRuntimeCache( bool bDelete ) level->MarkAggregateComponentStatusAsNotCurrent(); } } - dimple->ChangeContentSerialNumber(false); + dimple->ChangeGeometryContentSerialNumber(false); } return; } @@ -6546,11 +7573,11 @@ class ON_SubDVertex* ON_SubD::AddVertex( const double* P ) { - return AddVertex(ON_SubD::VertexTag::Unset, P); + return AddVertex(ON_SubDVertexTag::Unset, P); } class ON_SubDVertex* ON_SubD::AddVertex( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, const double* P ) { @@ -6565,7 +7592,7 @@ class ON_SubDVertex* ON_SubD::AddVertex( ON_SubDVertex* ON_SubD::AddVertexForExperts( unsigned int candidate_vertex_id, - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, const double* P, unsigned int initial_edge_capacity, unsigned int initial_face_capacity @@ -6582,7 +7609,7 @@ ON_SubDVertex* ON_SubD::AddVertexForExperts( class ON_SubDEdge* ON_SubDimple::AddEdge( - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, @@ -6602,7 +7629,7 @@ class ON_SubDEdge* ON_SubDimple::AddEdge( class ON_SubDEdge* ON_SubDimple::AddEdge( unsigned int candidate_edge_id, - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, @@ -6625,7 +7652,7 @@ class ON_SubDEdge* ON_SubDimple::AddEdge( && ON_SubDSectorType::IgnoredSectorCoefficient != v0_sector_weight && ON_SubDSectorType::UnsetSectorCoefficient != v0_sector_weight && nullptr != v0 - && ON_SubD::VertexTag::Smooth == v0->m_vertex_tag + && ON_SubDVertexTag::Smooth == v0->m_vertex_tag ) { // minimizes checking when building subds because constant crease weights can be passed in @@ -6636,7 +7663,7 @@ class ON_SubDEdge* ON_SubDimple::AddEdge( && ON_SubDSectorType::IgnoredSectorCoefficient != v1_sector_weight && ON_SubDSectorType::UnsetSectorCoefficient != v1_sector_weight && nullptr != v1 - && ON_SubD::VertexTag::Smooth == v1->m_vertex_tag + && ON_SubDVertexTag::Smooth == v1->m_vertex_tag ) { // minimizes checking when building subds because constant crease weights can be passed in @@ -6683,7 +7710,7 @@ class ON_SubDEdge* ON_SubDimple::AddEdge( return e; } -ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext( +ON_SubDEdgeTag ON_SubD::EdgeTagFromContext( unsigned int edge_face_count, const ON_SubDVertex* v0, const ON_SubDVertex* v1 @@ -6692,16 +7719,16 @@ ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext( return (nullptr != v0 && nullptr != v1) ? ON_SubD::EdgeTagFromContext(edge_face_count, v0->m_vertex_tag, v1->m_vertex_tag) - : ON_SubD::EdgeTag::Unset; + : ON_SubDEdgeTag::Unset; } -ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext( +ON_SubDEdgeTag ON_SubD::EdgeTagFromContext( unsigned int edge_face_count, - const ON_SubD::VertexTag v0_tag, - const ON_SubD::VertexTag v1_tag + const ON_SubDVertexTag v0_tag, + const ON_SubDVertexTag v1_tag ) { - ON_SubD::EdgeTag edge_tag = ON_SubD::EdgeTag::Unset; + ON_SubDEdgeTag edge_tag = ON_SubDEdgeTag::Unset; for(;;) { @@ -6710,24 +7737,24 @@ ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext( if (1 == edge_face_count || edge_face_count >= 3 ) { - edge_tag = ON_SubD::EdgeTag::Crease; + edge_tag = ON_SubDEdgeTag::Crease; break; } - const bool bSmooth0 = ON_SubD::VertexTag::Smooth == v0_tag; - const bool bSmooth1 = ON_SubD::VertexTag::Smooth == v1_tag; + const bool bSmooth0 = ON_SubDVertexTag::Smooth == v0_tag; + const bool bSmooth1 = ON_SubDVertexTag::Smooth == v1_tag; if ( bSmooth0 || bSmooth1 ) { if ( 2 == edge_face_count && bSmooth0 && bSmooth1) - edge_tag = ON_SubD::EdgeTag::Smooth; + edge_tag = ON_SubDEdgeTag::Smooth; break; } if ( ON_SubD::VertexTagIsSet(v0_tag) && ON_SubD::VertexTagIsSet(v1_tag) ) { if (2 == edge_face_count) - edge_tag = ON_SubD::EdgeTag::SmoothX; + edge_tag = ON_SubDEdgeTag::SmoothX; break; } @@ -6806,11 +7833,11 @@ class ON_SubDEdge* ON_SubD::AddEdge( ON_SubDVertex* v1 ) { - return ON_SubD::AddEdge(ON_SubD::EdgeTag::Unset, v0, v1); + return ON_SubD::AddEdge(ON_SubDEdgeTag::Unset, v0, v1); } class ON_SubDEdge* ON_SubD::AddEdge( - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, ON_SubDVertex* v0, ON_SubDVertex* v1 ) @@ -6829,7 +7856,7 @@ class ON_SubDEdge* ON_SubD::AddEdge( } ON_SubDEdge* ON_SubD::AddEdgeWithSectorCoefficients( - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, class ON_SubDVertex* v0, double v0_sector_coefficient, class ON_SubDVertex* v1, @@ -6844,7 +7871,7 @@ ON_SubDEdge* ON_SubD::AddEdgeWithSectorCoefficients( class ON_SubDEdge* ON_SubD::AddEdgeForExperts( unsigned int candidate_edge_id, - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, class ON_SubDVertex* v0, double v0_sector_coefficient, class ON_SubDVertex* v1, @@ -6984,7 +8011,43 @@ class ON_SubDFace* ON_SubDimple::AddFace( return f; } -bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCoefficientsOnly) +unsigned int ON_SubDimple::AllocateFaceTexturePoints(const ON_SubDFace* face) const +{ + return const_cast(this)->m_heap.AllocateFaceTexturePoints(face); +} + +void ON_SubDimple::ReturnFaceTexturePoints(const ON_SubDFace* face) const +{ + const_cast(this)->m_heap.ReturnFaceTexturePoints(face); +} + + +unsigned int ON_SubDHeap::AllocateFaceTexturePoints(const ON_SubDFace* face) +{ + if (nullptr == face) + return false; + unsigned texture_point_capacity = face->TexturePointsCapacity(); + if (texture_point_capacity < 3) + { + face->ClearTexturePoints(); + face->m_texture_points = this->Allocate3dPointArray(4 + face->m_edgex_capacity); + texture_point_capacity = face->TexturePointsCapacity(); + } + return texture_point_capacity; +} + +void ON_SubDHeap::ReturnFaceTexturePoints(const ON_SubDFace* face) +{ + if (nullptr != face) + { + face->ClearTexturePoints(); + ON_3dPoint* a = face->m_texture_points; + face->m_texture_points = nullptr; + this->Return3dPointArray(a); + } +} + +bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCoefficientsOnly) const { const double input_sector_coefficient[2] = { m_sector_coefficient[0], m_sector_coefficient[1] }; if (bUnsetEdgeSectorCoefficientsOnly) @@ -6996,7 +8059,7 @@ bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCo } m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; - if (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::SmoothX == m_edge_tag) + if (ON_SubDEdgeTag::Smooth == m_edge_tag || ON_SubDEdgeTag::SmoothX == m_edge_tag) { const unsigned int tagged_end_index = TaggedEndIndex(); if (tagged_end_index < 2) @@ -7005,12 +8068,12 @@ bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCo } else if (2 == tagged_end_index) { - if (ON_SubD::EdgeTag::Smooth == m_edge_tag && 2 == m_face_count ) - m_edge_tag = ON_SubD::EdgeTag::SmoothX; + if (ON_SubDEdgeTag::Smooth == m_edge_tag && 2 == m_face_count ) + const_cast(this)->m_edge_tag = ON_SubDEdgeTag::SmoothX; - if (ON_SubD::EdgeTag::Smooth == m_edge_tag) - m_edge_tag = ON_SubD::EdgeTag::Crease; - else if (ON_SubD::EdgeTag::SmoothX == m_edge_tag) + if (ON_SubDEdgeTag::Smooth == m_edge_tag) + const_cast(this)->m_edge_tag = ON_SubDEdgeTag::Crease; + else if (ON_SubDEdgeTag::SmoothX == m_edge_tag) { m_sector_coefficient[0] = ON_SubDSectorType::Create( this, 0).SectorCoefficient(); m_sector_coefficient[1] = ON_SubDSectorType::Create( this, 1).SectorCoefficient(); @@ -7027,7 +8090,7 @@ bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCo unsigned int ON_SubDLevel::UpdateEdgeSectorCoefficients( bool bUnsetEdgeSectorCoefficientsOnly - ) + ) const { unsigned int changed_edge_count = 0; for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) @@ -7159,6 +8222,110 @@ class ON_SubDFace* ON_SubD::AddFaceForExperts( return (nullptr != subdimple) ? subdimple->AddFace( candiate_face_id, edge_count, edge) : nullptr; } + +bool ON_SubD::AddFaceTexturePoints( + const class ON_SubDFace* face, + const class ON_3dPoint* texture_points, + size_t texture_points_count +) const +{ + if (nullptr == face) + return false; + face->ClearTexturePoints(); + const unsigned int face_edge_count = face->EdgeCount(); + if (nullptr != texture_points && texture_points_count >= face_edge_count && face_edge_count >= 3) + { + const ON_SubDimple* subdimple = SubDimple(); + if (nullptr != subdimple) + { + const unsigned capacity = subdimple->AllocateFaceTexturePoints(face); + if (capacity >= face_edge_count) + { + for (unsigned i = 0; i < face_edge_count; ++i) + face->SetTexturePoint(i, texture_points[i]); + } + } + } + return face->TexturePointsAreSet(); +} + +unsigned int ON_SubD::AllocateFaceTexturePoints( + const class ON_SubDFace* face +) const +{ + if (nullptr == face) + return false; + const unsigned int face_edge_count = face->EdgeCount(); + if (face_edge_count >= 3) + { + const ON_SubDimple* subdimple = SubDimple(); + if (nullptr != subdimple) + return subdimple->AllocateFaceTexturePoints(face); + } + face->ClearTexturePoints(); + return 0; +} + +unsigned int ON_SubDFace::TexturePointsCapacity() const +{ + return ON_SubDHeap::Managed3dPointArrayCapacity(this->m_texture_points); +} + +bool ON_SubDFace::TexturePointsAreSet() const +{ + return + 0 != (this->m_texture_status_bits & ON_SubDFace::TextureStatusBits::TexturePointsSet) + && TexturePointsCapacity() >= EdgeCount() + && EdgeCount() >= 3 + ; +} + +bool ON_SubDFace::SetTexturePoint( + unsigned i, + ON_3dPoint texture_point +) const +{ + const unsigned texture_point_capacity = this->TexturePointsCapacity(); + if (i < texture_point_capacity) + { + this->m_texture_points[i] = texture_point; + this->m_texture_status_bits |= ON_SubDFace::TextureStatusBits::TexturePointsSet; + return true; + } + return false; +} + +void ON_SubDFace::ClearTexturePoints() const +{ + this->m_texture_status_bits &= ON_SubDFace::TextureStatusBits::NotTexturePointsBitsMask; +} + +const ON_3dPoint ON_SubDFace::TexturePoint( + unsigned int i +) const +{ + const unsigned edge_count = this->EdgeCount(); + return (i < edge_count&& TexturePointsAreSet()) ? this->m_texture_points[i] : ON_3dPoint::NanPoint; +} + +const ON_3dPoint ON_SubDFace::TextureCenterPoint() const +{ + const unsigned edge_count = this->EdgeCount(); + if (edge_count >= 3 && TexturePointsAreSet()) + { + ON_3dPoint c = ON_3dPoint::Origin; + for (unsigned i = 0; i < edge_count; ++i) + c += m_texture_points[i]; + const double d = edge_count; + c.x /= d; + c.y /= d; + c.z /= d; + return c; + } + return ON_3dPoint::NanPoint; +} + + bool ON_SubD::AddFaceEdgeConnection( ON_SubDFace* face, unsigned int i, @@ -7778,6 +8945,24 @@ const ON_ComponentStatus ON_SubDFace::NeighborhoodStatusLogicalOr(bool bIncludeV return s; } +static void Internal_ClearVertexNeighborhoodCache(const ON_SubDVertex* vertex) +{ + // Clear cached values for this vertex every component touching this vertex. + vertex->ClearSavedSubdivisionPoints(); + for (unsigned short vei = 0; vei < vertex->m_edge_count; ++vei) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); + if (nullptr != e) + e->ClearSavedSubdivisionPoints(); + } + for (unsigned short vfi = 0; vfi < vertex->m_face_count; ++vfi) + { + const ON_SubDFace* f = vertex->m_faces[vfi]; + if (nullptr != f) + f->ClearSavedSubdivisionPoints(); + } +} + static void Internal_ClearFaceNeighborhoodCache(const ON_SubDFace* face) { // Clear cached values for every component associated with this face. @@ -7802,8 +8987,9 @@ static void Internal_ClearFaceNeighborhoodCache(const ON_SubDFace* face) for (unsigned int evi = 0; evi < 2; evi++) { const ON_SubDVertex* vertex = edge->m_vertex[evi]; - if (nullptr != vertex) - vertex->ClearSavedSubdivisionPoints(); + if (nullptr == vertex) + continue; + Internal_ClearVertexNeighborhoodCache(vertex); } } } @@ -7811,7 +8997,11 @@ static void Internal_ClearFaceNeighborhoodCache(const ON_SubDFace* face) void ON_SubDVertex::VertexModifiedNofification() const { + // NOTE WELL: + // This function is called by ON_SubDEdge::EdgeModifiedNotification() and ON_SubDFace::FaceModifiedNotification(). + ClearSavedSubdivisionPoints(); + if (nullptr != m_edges) { for (unsigned short vei = 0; vei < m_edge_count; vei++) @@ -7826,20 +9016,26 @@ void ON_SubDVertex::VertexModifiedNofification() const v1->ClearSavedSubdivisionPoints(); } - // This is needed to clear cached information in the Catmull-Clark - // ring that is not immediately adjacent to this vertex but whose values - // this vertex affects. - for (unsigned short vfi = 0; vfi < m_face_count; vfi++) + if (nullptr != m_faces) { - const ON_SubDFace* face = this->m_faces[vfi]; - if (nullptr != face) - Internal_ClearFaceNeighborhoodCache(face); + // This is needed to clear cached information in the Catmull-Clark + // ring that is not immediately adjacent to this vertex but whose values + // this vertex affects. + for (unsigned short vfi = 0; vfi < m_face_count; vfi++) + { + const ON_SubDFace* face = m_faces[vfi]; + if (nullptr != face) + Internal_ClearFaceNeighborhoodCache(face); + } } } } void ON_SubDEdge::EdgeModifiedNofification() const { + // NOTE WELL: + // This function is called by ON_SubDFace::FaceModifiedNotification(). + ClearSavedSubdivisionPoints(); UnsetSectorCoefficientsForExperts(); for (unsigned int evi = 0; evi < 2; evi++) @@ -7847,24 +9043,6 @@ void ON_SubDEdge::EdgeModifiedNofification() const if (nullptr != m_vertex[evi]) m_vertex[evi]->VertexModifiedNofification(); } - - // If the topology pointers are complete and accurate, then the following - // is not required. It's here because this SubD may be under construction - // and we cannot assume the topology pointers are complete and accurate. - const ON_SubDFacePtr* fptr = m_face2; - for (unsigned int efi = 0; efi < 2; efi++) - { - if (2 == efi) - { - fptr = m_facex; - if ( nullptr == fptr) - break; - } - const ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr->m_ptr); - if ( nullptr != face ) - Internal_ClearFaceNeighborhoodCache(face); - fptr++; - } } void ON_SubDEdge::UnsetSectorCoefficientsForExperts() const @@ -7897,11 +9075,8 @@ void ON_SubDVertex::UnsetSectorCoefficientsForExperts(unsigned int relative_edge void ON_SubDFace::FaceModifiedNofification() const { - Internal_ClearFaceNeighborhoodCache(this); + ClearSavedSubdivisionPoints(); - // This is needed to clear cached information in the Catmull-Clark - // ring that is not immediately adjacent to this face but whose values - // this face affects. const ON_SubDEdgePtr* eptr = m_edge4; for (unsigned int efi = 0; efi < m_edge_count; efi++) { @@ -7913,25 +9088,221 @@ void ON_SubDFace::FaceModifiedNofification() const } const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr != edge) - { - const ON_SubDFacePtr* fptr = edge->m_face2; - for (unsigned short fei = 0; fei < edge->m_face_count; fei++) - { - if (2 == fei) - { - fptr = edge->m_facex; - if (nullptr == fptr) - break; - } - const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr->m_ptr); - if (nullptr != f && f != this) - Internal_ClearFaceNeighborhoodCache(f); - } - } + edge->EdgeModifiedNofification(); eptr++; } } +unsigned int ON_SubDFace::PackId() const +{ + return m_pack_id; +} + +void ON_SubDFace::SetPackIdForExperts( + unsigned int pack_id +) +{ + m_pack_id = pack_id; +} + +bool ON_SubDFace::IsValidPackRect( + ON_2dPoint pack_rect_origin, + ON_2dVector pack_rect_size, + int packing_rotation_degrees +) +{ + const double fuzzy_1 = 1.0 + ON_SQRT_EPSILON; + bool rc + = 0.0 <= pack_rect_origin.x && pack_rect_origin.x < 1.0 + && 0.0 <= pack_rect_origin.y && pack_rect_origin.y < 1.0 + && 0.0 < pack_rect_size.x && (pack_rect_origin.x + pack_rect_size.x) <= fuzzy_1 + && 0.0 < pack_rect_size.y && (pack_rect_origin.y + pack_rect_size.y) <= fuzzy_1 + && 0 == packing_rotation_degrees % 90 + ; + return rc; +} + +bool ON_SubDFace::SetPackRectForExperts(ON_2dPoint pack_rect_origin, ON_2dVector pack_rect_size, int packing_rotation_degrees) +{ + const bool bValidPackRectangle = ON_SubDFace::IsValidPackRect(pack_rect_origin, pack_rect_size, packing_rotation_degrees); + if (bValidPackRectangle) + { + m_pack_rect_origin[0] = pack_rect_origin.x; + m_pack_rect_origin[1] = pack_rect_origin.y; + m_pack_rect_size[0] = pack_rect_size.x; + m_pack_rect_size[1] = pack_rect_size.y; + + ON_SubDFace::PackStatusBits packing_rotation = ON_SubDFace::PackStatusBits::PackingRotate0; + switch (((packing_rotation_degrees % 360) + 360) % 360) + { + case 90: + packing_rotation = ON_SubDFace::PackStatusBits::PackingRotate90; + break; + case 180: + packing_rotation = ON_SubDFace::PackStatusBits::PackingRotate180; + break; + case 270: + packing_rotation = ON_SubDFace::PackStatusBits::PackingRotate270; + break; + } + m_pack_status_bits = ON_SubDFace::PackStatusBits::PackRectSet; + m_pack_status_bits |= packing_rotation; + } + else + { + ON_SUBD_ERROR("Invalid pack rect input"); + ClearPackRect(); + } + return bValidPackRectangle; +} + +void ON_SubDFace::ClearPackRect() +{ + m_pack_rect_origin[0] = ON_DBL_QNAN; + m_pack_rect_origin[1] = ON_DBL_QNAN; + m_pack_rect_size[0] = ON_DBL_QNAN; + m_pack_rect_size[1] = ON_DBL_QNAN; + m_pack_status_bits = 0; +} + +void ON_SubDFace::ClearPackId() +{ + m_pack_id = 0; + m_pack_rect_origin[0] = ON_DBL_QNAN; + m_pack_rect_origin[1] = ON_DBL_QNAN; + m_pack_rect_size[0] = ON_DBL_QNAN; + m_pack_rect_size[1] = ON_DBL_QNAN; + m_pack_status_bits = 0; +} + +bool ON_SubDFace::PackRectIsSet() const +{ + return 0 != (m_pack_status_bits & ON_SubDFace::PackStatusBits::PackRectSet); +} + +const ON_2dPoint ON_SubDFace::PackRectOrigin() const +{ + return ON_2dPoint(m_pack_rect_origin); +} + +const ON_2dVector ON_SubDFace::PackRectSize() const +{ + return ON_2dVector(m_pack_rect_size); +} + +unsigned int ON_SubDFace::PackRectRotationDegrees() const +{ + if (0 == (m_pack_status_bits & ON_SubDFace::PackStatusBits::PackRectSet)) + return 0; + unsigned int packing_rotation_degrees = 0; + switch (m_pack_status_bits & ON_SubDFace::PackStatusBits::PackingRotateMask) + { + case ON_SubDFace::PackStatusBits::PackingRotate90: + packing_rotation_degrees = 90; + break; + case ON_SubDFace::PackStatusBits::PackingRotate180: + packing_rotation_degrees = 180; + break; + case ON_SubDFace::PackStatusBits::PackingRotate270: + packing_rotation_degrees = 270; + break; + } + return packing_rotation_degrees; +} + +double ON_SubDFace::PackRectRotationRadians() const +{ + if (0 == (m_pack_status_bits & ON_SubDFace::PackStatusBits::PackRectSet)) + return ON_DBL_QNAN; + double x = 0.0; + switch (m_pack_status_bits & ON_SubDFace::PackStatusBits::PackingRotateMask) + { + case ON_SubDFace::PackStatusBits::PackingRotate90: + x = 1.0; + break; + case ON_SubDFace::PackStatusBits::PackingRotate180: + x = 2.0; + break; + case ON_SubDFace::PackStatusBits::PackingRotate270: + x = 3.0; + break; + } + return x * 0.5 * ON_PI; +} + +const ON_2dPoint ON_SubDFace::PackRectCorner(bool bGridOrder, int corner_index) const +{ + if (0 == (m_pack_status_bits & ON_SubDFace::PackStatusBits::PackRectSet)) + return ON_2dPoint::NanPoint; + + corner_index = ((corner_index % 4) + 4) % 4; + // now corner_index = 0,1,2 or 3. + + if (bGridOrder) + { + if (2 == corner_index) + corner_index = 3; + else if (3 == corner_index) + corner_index = 2; + } + // now corner index is a counter-clockwise corner index + + int packrot_dex = 0; + switch (m_pack_status_bits & ON_SubDFace::PackStatusBits::PackingRotateMask) + { + case ON_SubDFace::PackStatusBits::PackingRotate90: + packrot_dex = 3; + break; + case ON_SubDFace::PackStatusBits::PackingRotate180: + packrot_dex = 2; + break; + case ON_SubDFace::PackStatusBits::PackingRotate270: + packrot_dex = 1; + break; + } + + corner_index = (corner_index + packrot_dex) % 4; + // now the packing rotation is taken into account. + + ON_2dPoint corner = PackRectOrigin(); + const ON_2dVector delta = PackRectSize(); + switch (corner_index) + { + case 1: + corner.x += delta.x; + break; + case 2: + corner.x += delta.x; + corner.y += delta.y; + break; + case 3: + corner.y += delta.y; + break; + } + + return corner; +} + +bool ON_SubDFace::GetFacePackRectCorners(bool bGridOrder, ON_2dPoint face_pack_rect_corners[4]) const +{ + if (nullptr != face_pack_rect_corners) + { + if (this->PackRectIsSet()) + { + face_pack_rect_corners[0] = PackRectCorner(bGridOrder, 0); + face_pack_rect_corners[1] = PackRectCorner(bGridOrder, 1); + face_pack_rect_corners[2] = PackRectCorner(bGridOrder, 2); + face_pack_rect_corners[3] = PackRectCorner(bGridOrder, 3); + return true; + } + face_pack_rect_corners[0] = ON_2dPoint::NanPoint; + face_pack_rect_corners[1] = ON_2dPoint::NanPoint; + face_pack_rect_corners[2] = ON_2dPoint::NanPoint; + face_pack_rect_corners[3] = ON_2dPoint::NanPoint; + } + return false; +} + void ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags() const { ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags); @@ -7966,6 +9337,11 @@ bool ON_SubDComponentBase::SavedSubdivisionPointIsSet() const : false; } +bool ON_SubDEdge::EdgeSurfaceCurveIsSet() const +{ + return false; +} + bool ON_SubDComponentBase::SubdivisionDisplacementIsNonzero() const { return (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) @@ -8352,14 +9728,14 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); const unsigned int tagged_end - = (ON_SubD::VertexTag::Smooth != edge_vertex[0]->m_vertex_tag) + = (ON_SubDVertexTag::Smooth != edge_vertex[0]->m_vertex_tag) ? 0 - : ((ON_SubD::VertexTag::Smooth != edge_vertex[1]->m_vertex_tag) ? 1 : ON_UNSET_UINT_INDEX); + : ((ON_SubDVertexTag::Smooth != edge_vertex[1]->m_vertex_tag) ? 1 : ON_UNSET_UINT_INDEX); double EP[3]; if ( ON_UNSET_UINT_INDEX == tagged_end || 0.5 == m_sector_coefficient[tagged_end] - || (ON_SubD::EdgeTag::SmoothX == m_edge_tag) + || (ON_SubDEdgeTag::SmoothX == m_edge_tag) ) { // ignore edge weights @@ -8367,7 +9743,7 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ EP[1] = 0.375*edgePsum[1]; EP[2] = 0.375*edgePsum[2]; } - else if (ON_SubD::VertexTag::Smooth == edge_vertex[1 - tagged_end]->m_vertex_tag + else if (ON_SubDVertexTag::Smooth == edge_vertex[1 - tagged_end]->m_vertex_tag && m_sector_coefficient[tagged_end] > 0.0 && m_sector_coefficient[tagged_end] < 1.0 ) @@ -8502,7 +9878,7 @@ unsigned int ON_SubDEdge::GetSectorBoundaryEdges( if (edge_face_count <= 0 || edge_face_count > 2) return GetSectorBoundaryEdgesError(); - if (2 == edge_face_count && ON_SubD::EdgeTag::Crease == m_edge_tag) + if (2 == edge_face_count && ON_SubDEdgeTag::Crease == m_edge_tag) return GetSectorBoundaryEdgesError(); if (0 != edge_vertex_index && 1 != edge_vertex_index) @@ -8833,7 +10209,7 @@ bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( || nullptr == vertex->m_faces || nullptr == vertex->m_edges || vertex->m_face_count != vertex->m_edge_count - || n < ON_SubDSectorType::MinimumSectorFaceCount(ON_SubD::VertexTag::Smooth) + || n < ON_SubDSectorType::MinimumSectorFaceCount(ON_SubDVertexTag::Smooth) ) { ON_SUBD_ERROR("input vertex is not valid."); @@ -8914,7 +10290,7 @@ bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( const double* vertexP = vertex->m_P; const unsigned int n = (nullptr != vertex->m_edges ? vertex->m_edge_count : 0); - if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag || ON_SubD::VertexTag::Dart == vertex->m_vertex_tag) + if (ON_SubDVertexTag::Smooth == vertex->m_vertex_tag || ON_SubDVertexTag::Dart == vertex->m_vertex_tag) { const unsigned int minimum_n = ON_SubDSectorType::MinimumSectorEdgeCount(vertex->m_vertex_tag); if (n < minimum_n || n != vertex->m_face_count || nullptr == vertex->m_faces) @@ -9078,12 +10454,12 @@ bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_poin if (n < 2) return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); - if (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag) + if (ON_SubDVertexTag::Smooth == m_vertex_tag || ON_SubDVertexTag::Dart == m_vertex_tag) { return ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint(this, subdivision_point); } - if (ON_SubD::VertexTag::Crease == m_vertex_tag) + if (ON_SubDVertexTag::Crease == m_vertex_tag) { class ON_SubDEdgePtr* edges = m_edges; const ON_SubDVertex* edge0_vertex = nullptr; @@ -9097,7 +10473,7 @@ bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_poin continue; } - if (ON_SubD::EdgeTag::Crease != edge->m_edge_tag) + if (ON_SubDEdgeTag::Crease != edge->m_edge_tag) continue; const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(this); @@ -9141,7 +10517,7 @@ bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_poin return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); } - if (ON_SubD::VertexTag::Corner == m_vertex_tag) + if (ON_SubDVertexTag::Corner == m_vertex_tag) { vertexP = m_P; subdivision_point[0] = vertexP[0]; @@ -9353,7 +10729,7 @@ bool ON_SubDimple::LocalSubdivide( ON_SimpleArray face_points(face_count); // this subd is being modifed. - ChangeContentSerialNumber(false); + ChangeGeometryContentSerialNumber(false); for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) { @@ -9462,14 +10838,14 @@ bool ON_SubDimple::LocalSubdivide( } ReturnFace(f); - ON_SubDVertex* center = AllocateVertex(ON_SubD::VertexTag::Smooth, level0_index, &P.x); + ON_SubDVertex* center = AllocateVertex(ON_SubDVertexTag::Smooth, level0_index, &P.x); AddVertexToLevel(center); radial_edges.SetCount(0); radial_edges.Reserve(e_count /2); for (unsigned fei = 0; fei < e_count; fei += 2) { - ON_SubDEdge* r = AddEdge(ON_SubD::EdgeTag::Smooth, center, ON_SubDSectorType::UnsetSectorCoefficient, const_cast(eptrs[fei].RelativeVertex(1)), ON_SubDSectorType::UnsetSectorCoefficient); + ON_SubDEdge* r = AddEdge(ON_SubDEdgeTag::Smooth, center, ON_SubDSectorType::UnsetSectorCoefficient, const_cast(eptrs[fei].RelativeVertex(1)), ON_SubDSectorType::UnsetSectorCoefficient); radial_edges.Append(r); } @@ -9530,7 +10906,7 @@ unsigned int ON_SubDimple::GlobalSubdivide() // If the object is currently symmetric, a global subdivision will not break symmetry const bool bChangePreservesSymmetry = true; - this->ChangeContentSerialNumber(bChangePreservesSymmetry); + this->ChangeGeometryContentSerialNumber(bChangePreservesSymmetry); // Add face points for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) @@ -9539,7 +10915,7 @@ unsigned int ON_SubDimple::GlobalSubdivide() continue; if (nullptr == f0->m_subd_point1) { - const_cast(f0)->m_subd_point1 = v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, P); + const_cast(f0)->m_subd_point1 = v = AllocateVertex(ON_SubDVertexTag::Smooth, level1_index, P); AddVertexToLevel(v); } else @@ -9556,11 +10932,11 @@ unsigned int ON_SubDimple::GlobalSubdivide() { if (false == e0->GetSubdivisionPoint(P)) continue; - // (the subdivision point of an edge tagged as ON_SubD::EdgeTag::SmoothX is a smooth vertex.) - const ON_SubD::VertexTag vertex_tag - = ON_SubD::EdgeTag::Crease == e0->m_edge_tag - ? ON_SubD::VertexTag::Crease - : ON_SubD::VertexTag::Smooth; + // (the subdivision point of an edge tagged as ON_SubDEdgeTag::SmoothX is a smooth vertex.) + const ON_SubDVertexTag vertex_tag + = ON_SubDEdgeTag::Crease == e0->m_edge_tag + ? ON_SubDVertexTag::Crease + : ON_SubDVertexTag::Smooth; if (nullptr == e0->m_subd_point1) { const_cast(e0)->m_subd_point1 = v = AllocateVertex(vertex_tag, level1_index, P ); @@ -9604,11 +10980,11 @@ unsigned int ON_SubDimple::GlobalSubdivide() ON_SubDVertex* end_vertex[2] = { const_cast(e0->m_vertex[0]->m_subd_point1), const_cast(e0->m_vertex[1]->m_subd_point1) }; ON_SubDVertex* mid_vertex = const_cast(e0->m_subd_point1); double w[2] = { e0->m_sector_coefficient[0], e0->m_sector_coefficient[1] }; - ON_SubD::EdgeTag edge_tag = e0->m_edge_tag; - if (ON_SubD::EdgeTag::SmoothX == edge_tag && 2 == e0->m_face_count) + ON_SubDEdgeTag edge_tag = e0->m_edge_tag; + if (ON_SubDEdgeTag::SmoothX == edge_tag && 2 == e0->m_face_count) { - if ( nullptr != mid_vertex && ON_SubD::VertexTag::Smooth == mid_vertex->m_vertex_tag ) - edge_tag = ON_SubD::EdgeTag::Smooth; + if ( nullptr != mid_vertex && ON_SubDVertexTag::Smooth == mid_vertex->m_vertex_tag ) + edge_tag = ON_SubDEdgeTag::Smooth; } AddEdge(edge_tag, end_vertex[0], w[0], mid_vertex, 0.0); AddEdge(edge_tag, mid_vertex, 0.0, end_vertex[1], w[1]); @@ -9645,7 +11021,7 @@ unsigned int ON_SubDimple::Internal_GlobalQuadSubdivideFace( return 0; f0->SetSavedSubdivisionPoint(faceC); unsigned int level1_index = f0->SubdivisionLevel() + 1; - ON_SubDVertex* v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, faceC ); + ON_SubDVertex* v = AllocateVertex(ON_SubDVertexTag::Smooth, level1_index, faceC ); AddVertexToLevel(v); const_cast(f0)->m_subd_point1 = v; } @@ -9694,13 +11070,13 @@ unsigned int ON_SubDimple::Internal_GlobalQuadSubdivideFace( if (nullptr == E1[3]) { // The value of E0[0]->m_subd_point1->m_vertex_tag should be either - // ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the + // ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Crease. In the // case when it's value is "crease", the resulting edge end weight // will be 0.5 because the edge has two adjacent faces and "theta" // will be pi/2. // The resulting quad edge weight is 0.5 = 1/2 + 1/3*cos(pi/2). - w = (ON_SubD::VertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_2facesector : 0.0; - E1[3] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast(f0->m_subd_point1), 0.0, const_cast(E0[0]->m_subd_point1), w); + w = (ON_SubDVertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_2facesector : 0.0; + E1[3] = AddEdge(ON_SubDEdgeTag::Smooth, const_cast(f0->m_subd_point1), 0.0, const_cast(E0[0]->m_subd_point1), w); if (nullptr == FirstE1) FirstE1 = E1[3]; } @@ -9709,12 +11085,12 @@ unsigned int ON_SubDimple::Internal_GlobalQuadSubdivideFace( if (i + 1 < f0_edge_count || nullptr == FirstE1) { // The value of E0[0]->m_subd_point1->m_vertex_tag should be either - // ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the + // ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Crease. In the // case when it's value is "crease", the resulting edge end weight // will be zero because the edge has two adjacent faces and "theta" // will be pi/2. The resulting edge weight is 0.5. - w = (ON_SubD::VertexTag::Crease == E0[1]->m_subd_point1->m_vertex_tag) ? w_2facesector : 0.0; - E1[2] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast(f0->m_subd_point1), 0.0, const_cast(E0[1]->m_subd_point1), w); + w = (ON_SubDVertexTag::Crease == E0[1]->m_subd_point1->m_vertex_tag) ? w_2facesector : 0.0; + E1[2] = AddEdge(ON_SubDEdgeTag::Smooth, const_cast(f0->m_subd_point1), 0.0, const_cast(E0[1]->m_subd_point1), w); } else { @@ -9844,10 +11220,10 @@ ON_SubDEdgePtr ON_SubDimple::MergeConsecutiveEdges( if (nullptr == end_v[0] || nullptr == end_v[1] || end_v[0] == end_v[1] ) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); - ON_SubD::EdgeTag merged_edge_tag + ON_SubDEdgeTag merged_edge_tag = (e[0]->IsSmooth() || e[1]->IsSmooth()) - ? ON_SubD::EdgeTag::Smooth - : ON_SubD::EdgeTag::Crease; + ? ON_SubDEdgeTag::Smooth + : ON_SubDEdgeTag::Crease; for (unsigned int j = 0; j < e[1]->m_face_count; j++) { @@ -9902,12 +11278,12 @@ ON_SubDEdgePtr ON_SubDimple::MergeConsecutiveEdges( e[0]->m_sector_coefficient[1 - edir[0]] = e[1]->m_sector_coefficient[1 - edir[1]]; const bool bTagged[2] = { end_v[0]->IsCreaseOrCorner(), end_v[1]->IsCreaseOrCorner() }; - if (ON_SubD::EdgeTag::Smooth == merged_edge_tag || false == bTagged[0] || false == bTagged[1]) + if (ON_SubDEdgeTag::Smooth == merged_edge_tag || false == bTagged[0] || false == bTagged[1]) { e[0]->m_edge_tag = (bTagged[0] && bTagged[1]) - ? ON_SubD::EdgeTag::SmoothX - : ON_SubD::EdgeTag::Smooth; + ? ON_SubDEdgeTag::SmoothX + : ON_SubDEdgeTag::Smooth; if ( false == bTagged[0]) e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; else if (!(e[0]->m_sector_coefficient[0] > 0.0 && e[0]->m_sector_coefficient[0] < 1.0)) @@ -9919,7 +11295,7 @@ ON_SubDEdgePtr ON_SubDimple::MergeConsecutiveEdges( } else { - e[0]->m_edge_tag = ON_SubD::EdgeTag::Crease; + e[0]->m_edge_tag = ON_SubDEdgeTag::Crease; e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; } @@ -10638,7 +12014,7 @@ bool ON_SubD::Orient() const faces_array.Append(face); if (face->m_id > face_id1) face_id1 = face->m_id; - else if (face->m_id < face_id1) + else if (face->m_id < face_id0) face_id0 = face->m_id; nonzero_face_count++; } @@ -10648,11 +12024,13 @@ bool ON_SubD::Orient() const return true; const ON_SubDFace** faces = faces_array.Array(); - if (face_id1 - face_id0 > faces_array.UnsignedCount()) + if (face_id1 - face_id0 >= faces_array.UnsignedCount()) { - faces_array.Reserve(face_id1 - face_id0); - faces_array.SetCount(face_id1 - face_id0); + faces_array.Reserve(face_id1 - face_id0 + 1); + faces_array.SetCount(face_id1 - face_id0 + 1); faces_array.Zero(); + // Update faces pointer after reallocating faces_array + faces = faces_array.Array(); for (const ON_SubDFace* face = FirstFace(); nullptr != face; face = face->m_next_face) { faces[face->m_id-face_id0] = face; @@ -10738,13 +12116,13 @@ const ON_SubDVertex * ON_SubD::ReplaceFaceWithTriangleFan(ON_SubDFace * face, ON else P /= ((double)edge_count); - ON_SubDVertex* v0 = AddVertex(ON_SubD::VertexTag::Smooth, P); + ON_SubDVertex* v0 = AddVertex(ON_SubDVertexTag::Smooth, P); if (nullptr == v0) return ON_SUBD_RETURN_ERROR(nullptr); for (unsigned i = 0; i < edge_count; ++i, ++eptr) { - if (nullptr == AddEdge(ON_SubD::EdgeTag::Smooth, v0, const_cast(edges[i].RelativeVertex(0)))) + if (nullptr == AddEdge(ON_SubDEdgeTag::Smooth, v0, const_cast(edges[i].RelativeVertex(0)))) { ON_SubDComponentPtr cptr = ON_SubDComponentPtr::Create(v0); DeleteComponents(&cptr, 1, false); @@ -10814,19 +12192,19 @@ const ON_SubDEdge* ON_SubDimple::SplitEdge( if ( vertex_location == ON_3dPoint(edge->m_vertex[0]->m_P) || vertex_location == ON_3dPoint(edge->m_vertex[1]->m_P) ) return ON_SUBD_RETURN_ERROR(nullptr); - ON_SubD::EdgeTag edge_tag = edge->m_edge_tag; - ON_SubD::VertexTag vertex_tag; + ON_SubDEdgeTag edge_tag = edge->m_edge_tag; + ON_SubDVertexTag vertex_tag; switch (edge->m_edge_tag) { - case ON_SubD::EdgeTag::Smooth: - vertex_tag = ON_SubD::VertexTag::Smooth; + case ON_SubDEdgeTag::Smooth: + vertex_tag = ON_SubDVertexTag::Smooth; break; - case ON_SubD::EdgeTag::Crease: - vertex_tag = ON_SubD::VertexTag::Crease; + case ON_SubDEdgeTag::Crease: + vertex_tag = ON_SubDVertexTag::Crease; break; - case ON_SubD::EdgeTag::SmoothX: - vertex_tag = ON_SubD::VertexTag::Smooth; - edge_tag = ON_SubD::EdgeTag::Smooth; + case ON_SubDEdgeTag::SmoothX: + vertex_tag = ON_SubDVertexTag::Smooth; + edge_tag = ON_SubDEdgeTag::Smooth; break; default: return ON_SUBD_RETURN_ERROR(nullptr); @@ -11065,7 +12443,7 @@ const ON_SubDEdge* ON_SubDimple::SplitFace( } new_e = AddEdge( - ((v[0]->IsSmooth() || v[1]->IsSmooth()) ? ON_SubD::EdgeTag::Smooth : ON_SubD::EdgeTag::SmoothX), + ((v[0]->IsSmooth() || v[1]->IsSmooth()) ? ON_SubDEdgeTag::Smooth : ON_SubDEdgeTag::SmoothX), v[0], ON_SubDSectorType::UnsetSectorCoefficient, v[1], ON_SubDSectorType::UnsetSectorCoefficient); if (nullptr == new_e) @@ -11536,6 +12914,7 @@ void ON_SubDLevel::ClearEvaluationCache() const ClearBoundingBox(); m_surface_mesh = ON_SubDMesh::Empty; m_control_net_mesh = ON_SubDMesh::Empty; + m_aggregates.MarkAllAsNotCurrent(); for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) { @@ -11557,7 +12936,7 @@ void ON_SubDLevel::ClearEvaluationCache() const const ON_SubDVertex* v = edge->m_vertex[evi]; if (nullptr == v) continue; - if (ON_SubD::VertexTag::Corner != v->m_vertex_tag) + if (ON_SubDVertexTag::Corner != v->m_vertex_tag) continue; // corner sector coefficients depend on the subtended angle of the sector's crease boundary. // All other sector coefficients are independent of vertex location. @@ -11572,83 +12951,252 @@ void ON_SubDLevel::ClearEvaluationCache() const } } -void ON_SubDLevel::ClearNeighborhoodEvaluationCache(const ON_SubDVertex * vertex0, bool bTagChanged ) const + +bool ON_SubD::CopyEvaluationCacheForExperts(const ON_SubD& src) { - ClearEdgeFlags(); - ClearBoundingBox(); - m_surface_mesh = ON_SubDMesh::Empty; - m_control_net_mesh = ON_SubDMesh::Empty; + const ON_SubDimple* src_subdimple = src.m_subdimple_sp.get(); + ON_SubDimple* this_subdimple = m_subdimple_sp.get(); + return (nullptr != src_subdimple && nullptr != this_subdimple) ? this_subdimple->CopyEvaluationCacheForExperts(*src_subdimple) : false; +} - if (nullptr == vertex0) - return; +bool ON_SubDimple::CopyEvaluationCacheForExperts(const ON_SubDimple& src) +{ + const ON_SubDLevel* src_level = src.ActiveLevelConstPointer(); + ON_SubDLevel* this_level = this->ActiveLevelPointer(); + return (nullptr != src_level && nullptr != this_level) ? this_level->CopyEvaluationCacheForExperts(*src_level, this->m_heap) : false; +} - vertex0->ClearSavedSubdivisionPoints(); +bool ON_SubDLevel::CopyEvaluationCacheForExperts(const ON_SubDLevel& src, ON_SubDHeap& this_heap) +{ + // Validate conditions for coping the cached evaluation information + if ( + this == &src + || m_vertex_count != src.m_vertex_count + || m_edge_count != src.m_edge_count + || m_face_count != src.m_face_count + ) + return ON_SUBD_RETURN_ERROR(false); - for (unsigned short v0ei = 0; v0ei < vertex0->m_edge_count; ++v0ei) + ON_SubDVertex* this_vertex; + const ON_SubDVertex* src_vertex; + ON_SubDEdgePtr this_veptr, src_veptr; + ON_SubDFace* this_face; + const ON_SubDFace* src_face; + bool bCopyVertexCache = false; + for ( + this_vertex = m_vertex[0], src_vertex = src.m_vertex[0]; + nullptr != this_vertex && nullptr != src_vertex; + this_vertex = const_cast(this_vertex->m_next_vertex), src_vertex = src_vertex->m_next_vertex + ) { - const ON_SubDEdge* edge0 = ON_SUBD_EDGE_POINTER(vertex0->m_edges[v0ei].m_ptr); - if (nullptr == edge0) + if (this_vertex->m_id != src_vertex->m_id) + return ON_SUBD_RETURN_ERROR(false); + if (this_vertex->m_edge_count != src_vertex->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + if (this_vertex->m_face_count != src_vertex->m_face_count) + return ON_SUBD_RETURN_ERROR(false); + if (this_vertex->m_vertex_tag != src_vertex->m_vertex_tag) + return ON_SUBD_RETURN_ERROR(false); + if (false == (this_vertex->ControlNetPoint() == src_vertex->ControlNetPoint())) + return ON_SUBD_RETURN_ERROR(false); + if (this_vertex->SavedSubdivisionPointIsSet() && false == (this_vertex->SubdivisionPoint() == src_vertex->SubdivisionPoint())) + return ON_SUBD_RETURN_ERROR(false); + if (this_vertex->SavedSubdivisionPointIsSet() && this_vertex->SurfacePointIsSet()) continue; - edge0->ClearSavedSubdivisionPoints(); - if (bTagChanged) - edge0->UnsetSectorCoefficientsForExperts(); - else if (edge0->IsSmooth()) + if (false == src_vertex->SavedSubdivisionPointIsSet()) + continue; + bCopyVertexCache = true; + for (unsigned short vei = 0; vei < this_vertex->m_edge_count; ++vei) { - for (unsigned evi = 0; evi < 2; evi++) + this_veptr = this_vertex->m_edges[vei]; + src_veptr = src_vertex->m_edges[vei]; + if ( this_veptr.EdgeId() != src_veptr.EdgeId()) + return ON_SUBD_RETURN_ERROR(false); + if (ON_SUBD_EDGE_DIRECTION(this_veptr.m_ptr) != ON_SUBD_EDGE_DIRECTION(src_veptr.m_ptr)) + return ON_SUBD_RETURN_ERROR(false); + } + for (unsigned short vfi = 0; vfi < this_vertex->m_face_count; ++vfi) + { + this_face = const_cast(this_vertex->m_faces[vfi]); + src_face = src_vertex->m_faces[vfi]; + if (nullptr == this_face || nullptr == src_face || this_face->m_id != src_face->m_id) + return ON_SUBD_RETURN_ERROR(false); + } + } + if (nullptr != this_vertex || nullptr != src_vertex) + return ON_SUBD_RETURN_ERROR(false); + + ON_SubDEdge* this_edge; + const ON_SubDEdge* src_edge; + const ON_SubDFacePtr* this_fptr; + const ON_SubDFacePtr* src_fptr; + bool bCopyEdgeCache = false; + for ( + this_edge = m_edge[0], src_edge = src.m_edge[0]; + nullptr != this_edge && nullptr != src_edge; + this_edge = const_cast(this_edge->m_next_edge), src_edge = src_edge->m_next_edge + ) + { + if (this_edge->m_id != src_edge->m_id) + return ON_SUBD_RETURN_ERROR(false); + if (this_edge->m_face_count != src_edge->m_face_count) + return ON_SUBD_RETURN_ERROR(false); + if (this_edge->m_edge_tag != src_edge->m_edge_tag) + return ON_SUBD_RETURN_ERROR(false); + for (int evi = 0; evi < 2; ++evi) + { + if (nullptr == this_edge->m_vertex[evi] || nullptr == src_edge->m_vertex[evi]) + return ON_SUBD_RETURN_ERROR(false); + if (this_edge->m_vertex[evi]->m_id != src_edge->m_vertex[evi]->m_id) + return ON_SUBD_RETURN_ERROR(false); + } + if (this_edge->SavedSubdivisionPointIsSet() && false == (this_edge->SubdivisionPoint() == src_edge->SubdivisionPoint())) + return ON_SUBD_RETURN_ERROR(false); + if (this_edge->SavedSubdivisionPointIsSet() && this_edge->EdgeSurfaceCurveIsSet()) + continue; + if (false == src_edge->SavedSubdivisionPointIsSet()) + continue; + bCopyEdgeCache = true; + this_fptr = this_edge->m_face2; + src_fptr = src_edge->m_face2; + for (unsigned short efi = 0; efi < this_edge->m_face_count; ++efi, ++this_fptr, ++src_fptr) + { + if (2 == efi) { - if (false == (edge0->m_sector_coefficient[evi] > 0.0 && edge0->m_sector_coefficient[evi] < 1.0)) - continue; - const ON_SubDVertex* v = edge0->m_vertex[evi]; - if (nullptr == v) - continue; - if (ON_SubD::VertexTag::Corner != v->m_vertex_tag) - continue; - // corner sector coefficients depend on the subtended angle of the sector's crease boundary. - // All other sector coefficients are independent of vertex location. - edge0->UnsetSectorCoefficientsForExperts(); + this_fptr = this_edge->m_facex; + src_fptr = src_edge->m_facex; + if (nullptr == this_fptr || nullptr == src_fptr) + return ON_SUBD_RETURN_ERROR(false); + } + this_face = ON_SUBD_FACE_POINTER(this_fptr->m_ptr); + src_face = ON_SUBD_FACE_POINTER(src_fptr->m_ptr); + if (nullptr == this_face || nullptr == src_face || this_face->m_id != src_face->m_id ) + return ON_SUBD_RETURN_ERROR(false); + if ( this_face->m_edge_count != src_face->m_edge_count ) + return ON_SUBD_RETURN_ERROR(false); + if (ON_SUBD_FACE_DIRECTION(this_fptr->m_ptr) != ON_SUBD_FACE_DIRECTION(src_fptr->m_ptr)) + return ON_SUBD_RETURN_ERROR(false); + } + } + if (nullptr != this_edge || nullptr != src_edge) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDEdgePtr* this_eptr; + const ON_SubDEdgePtr* src_eptr; + bool bCopyFaceCache = false; + for ( + this_face = m_face[0], src_face = src.m_face[0]; + nullptr != this_face && nullptr != src_face; + this_face = const_cast(this_face->m_next_face), src_face = src_face->m_next_face + ) + { + if (this_face->m_id != src_face->m_id) + return ON_SUBD_RETURN_ERROR(false); + if (this_face->m_edge_count != src_face->m_edge_count) + return ON_SUBD_RETURN_ERROR(false); + if (this_face->SavedSubdivisionPointIsSet() && false == (this_face->SubdivisionPoint() == src_face->SubdivisionPoint()) ) + return ON_SUBD_RETURN_ERROR(false); + if (this_face->SavedSubdivisionPointIsSet() && nullptr != this_face->MeshFragments()) + continue; + if (false == src_face->SavedSubdivisionPointIsSet()) + continue; + bCopyFaceCache = true; + this_eptr = this_face->m_edge4; + src_eptr = src_face->m_edge4; + for (unsigned short fei = 0; fei < this_face->m_edge_count; ++fei, ++this_eptr, ++src_eptr) + { + if (4 == fei) + { + this_eptr = this_face->m_edgex; + src_eptr = src_face->m_edgex; + if(nullptr == this_eptr || nullptr == src_eptr) + return ON_SUBD_RETURN_ERROR(false); + } + if ( this_eptr->EdgeId() != src_eptr->EdgeId()) + return ON_SUBD_RETURN_ERROR(false); + if ( ON_SUBD_EDGE_DIRECTION(this_eptr->m_ptr) != ON_SUBD_EDGE_DIRECTION(src_eptr->m_ptr)) + return ON_SUBD_RETURN_ERROR(false); + } + } + if (nullptr != this_face || nullptr != src_face) + return ON_SUBD_RETURN_ERROR(false); + + if (false == bCopyVertexCache && false == bCopyEdgeCache && false == bCopyFaceCache) + return false; + + // this and src subd have identical geometry - copy evluation cache + + double subdivision_point[3]; + if (bCopyVertexCache) + { + ON_SubDSectorSurfacePoint this_limit_point; + for ( + this_vertex = m_vertex[0], src_vertex = src.m_vertex[0]; + nullptr != this_vertex && nullptr != src_vertex; + this_vertex = const_cast(this_vertex->m_next_vertex), src_vertex = src_vertex->m_next_vertex + ) + { + if (false == src_vertex->GetSavedSubdivisionPoint(subdivision_point)) + continue; + if (false == this_vertex->SavedSubdivisionPointIsSet()) + this_vertex->SetSavedSubdivisionPoint(subdivision_point); + + if (false == this_vertex->SurfacePointIsSet()) + { + for (const ON_SubDSectorSurfacePoint* src_limit_point = &src_vertex->SectorSurfacePointForExperts(); nullptr != src_limit_point; src_limit_point = src_limit_point->m_next_sector_limit_point) + { + this_limit_point = *src_limit_point; + this_limit_point.m_next_sector_limit_point = (ON_SubDSectorSurfacePoint*)1; + this_limit_point.m_sector_face = nullptr; + if (nullptr != src_limit_point->m_sector_face) + { + const unsigned vfi = src_vertex->FaceArrayIndex(src_limit_point->m_sector_face); + if (vfi >= src_vertex->m_face_count) + break; + this_limit_point.m_sector_face = this_vertex->m_faces[vfi]; + } + this_vertex->SetSavedSurfacePoint(true, this_limit_point); + } } } } - for (unsigned short v0fi = 0; v0fi < vertex0->m_face_count; ++v0fi) - { - const ON_SubDFace* face0 = vertex0->m_faces[v0fi]; - if (nullptr == face0) - continue; - face0->ClearSavedSubdivisionPoints(); - const ON_SubDEdgePtr* face0_eptr = face0->m_edge4; - for (unsigned short f0ei = 0; f0ei < face0->m_edge_count; ++f0ei, ++face0_eptr) + if (bCopyEdgeCache) + { + ON_SimpleArray edge_curve_cvs(ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity); + for ( + this_edge = m_edge[0], src_edge = src.m_edge[0]; + nullptr != this_edge && nullptr != src_edge; + this_edge = const_cast(this_edge->m_next_edge), src_edge = src_edge->m_next_edge + ) { - if (4 == f0ei) - { - face0_eptr = face0->m_edgex; - if (nullptr == face0_eptr) - break; - } - const ON_SubDEdge* edge0 = ON_SUBD_EDGE_POINTER(face0_eptr->m_ptr); - if (nullptr == edge0) + if (false == src_edge->GetSavedSubdivisionPoint(subdivision_point)) continue; - edge0->ClearSavedSubdivisionPoints(); - const ON_SubDVertex* vertex1 = edge0->m_vertex[ON_SUBD_EDGE_DIRECTION(face0_eptr->m_ptr)]; - if (vertex0 == vertex1 || nullptr == vertex1) - continue; - vertex1->ClearSavedSubdivisionPoints(); - for (unsigned short v1fi = 0; v1fi < vertex1->m_face_count; ++v1fi) - { - const ON_SubDFace* face1 = vertex1->m_faces[v1fi]; - if (nullptr == face1 || face0 == face1) - continue; - face1->ClearSavedSubdivisionPoints(); - } - for (unsigned short v1ei = 0; v1ei < vertex1->m_edge_count; ++v1ei) - { - const ON_SubDEdge* edge1 = ON_SUBD_EDGE_POINTER(vertex1->m_edges[v1ei].m_ptr); - if (nullptr == edge1) - continue; - edge1->ClearSavedSubdivisionPoints(); - } + if (false == this_edge->SavedSubdivisionPointIsSet()) + this_edge->SetSavedSubdivisionPoint(subdivision_point); + if ( false == this_edge->EdgeSurfaceCurveIsSet() && src_edge->EdgeSurfaceCurveIsSet( )) + this_heap.CopyEdgeSurfaceCurve(src_edge, this_edge); } } + + if (bCopyFaceCache) + { + for ( + this_face = m_face[0], src_face = src.m_face[0]; + nullptr != this_face && nullptr != src_face; + this_face = const_cast(this_face->m_next_face), src_face = src_face->m_next_face + ) + { + if (false == src_face->GetSavedSubdivisionPoint(subdivision_point)) + continue; + if ( false == this_face->SavedSubdivisionPointIsSet()) + this_face->SetSavedSubdivisionPoint(subdivision_point); + if (nullptr == this_face->MeshFragments() && nullptr != src_face->MeshFragments()) + this_heap.CopyMeshFragments(src_face, this_face); + } + } + + return true; } unsigned int ON_SubD::ComponentPtrFromComponentIndex( @@ -11968,8 +13516,8 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( { next_edge = const_cast(edge->m_next_edge); - const ON_SubD::EdgeTag edge_tag0 = edge->m_edge_tag; - if (bUnsetEdgeTagsOnly && ON_SubD::EdgeTag::Unset != edge_tag0 ) + const ON_SubDEdgeTag edge_tag0 = edge->m_edge_tag; + if (bUnsetEdgeTagsOnly && ON_SubDEdgeTag::Unset != edge_tag0 ) { continue; } @@ -11984,7 +13532,7 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( if (2 != edge->m_face_count) { - edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + edge->m_edge_tag = ON_SubDEdgeTag::Crease; edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; } @@ -11993,8 +13541,8 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; const bool bBothVertexTagsAreSet - = ON_SubD::VertexTag::Unset != edge->m_vertex[0]->m_vertex_tag - && ON_SubD::VertexTag::Unset != edge->m_vertex[1]->m_vertex_tag + = ON_SubDVertexTag::Unset != edge->m_vertex[0]->m_vertex_tag + && ON_SubDVertexTag::Unset != edge->m_vertex[1]->m_vertex_tag ; const unsigned int tagged_end_index = edge->TaggedEndIndex(); if (0 == tagged_end_index || 1 == tagged_end_index) @@ -12002,14 +13550,14 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( switch (edge_tag0) { - case ON_SubD::EdgeTag::Unset: + case ON_SubDEdgeTag::Unset: if (2 == tagged_end_index) { - edge->m_edge_tag = ON_SubD::EdgeTag::SmoothX; + edge->m_edge_tag = ON_SubDEdgeTag::SmoothX; } else if ( bBothVertexTagsAreSet ) { - edge->m_edge_tag = ON_SubD::EdgeTag::Smooth; + edge->m_edge_tag = ON_SubDEdgeTag::Smooth; if (3 == tagged_end_index) { edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; @@ -12018,10 +13566,10 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( } break; - case ON_SubD::EdgeTag::Smooth: + case ON_SubDEdgeTag::Smooth: if (2 == tagged_end_index) { - edge->m_edge_tag = ON_SubD::EdgeTag::SmoothX; + edge->m_edge_tag = ON_SubDEdgeTag::SmoothX; } else if (3 == tagged_end_index && bBothVertexTagsAreSet) { @@ -12030,18 +13578,18 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( } break; - case ON_SubD::EdgeTag::Crease: + case ON_SubDEdgeTag::Crease: edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; break; - //case ON_SubD::EdgeTag::Sharp: - // ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp is not valid in this version of opennurbs."); + //case ON_SubDEdgeTag::Sharp: + // ON_SUBD_ERROR("ON_SubDEdgeTag::Sharp is not valid in this version of opennurbs."); // break; - case ON_SubD::EdgeTag::SmoothX: + case ON_SubDEdgeTag::SmoothX: if ( 2 != tagged_end_index && bBothVertexTagsAreSet) - edge->m_edge_tag = ON_SubD::EdgeTag::Smooth; + edge->m_edge_tag = ON_SubDEdgeTag::Smooth; break; default: @@ -12072,14 +13620,14 @@ unsigned int ON_SubDLevel::UpdateVertexTags( { next_vertex = const_cast(vertex->m_next_vertex); - const ON_SubD::VertexTag vertex_tag0 = vertex->m_vertex_tag; - if (bUnsetVertexTagsOnly && ON_SubD::VertexTag::Unset != vertex_tag0 ) + const ON_SubDVertexTag vertex_tag0 = vertex->m_vertex_tag; + if (bUnsetVertexTagsOnly && ON_SubDVertexTag::Unset != vertex_tag0 ) { continue; } - const ON_SubD::VertexTag vertex_tag1 = vertex->SuggestedVertexTag(true, false); - if (ON_SubD::VertexTag::Unset == vertex_tag1) + const ON_SubDVertexTag vertex_tag1 = vertex->SuggestedVertexTag(true, false); + if (ON_SubDVertexTag::Unset == vertex_tag1) continue; if ( vertex_tag0 != vertex_tag1) @@ -12136,7 +13684,7 @@ unsigned int ON_SubDLevel::UpdateAllTagsAndSectorCoefficients( next_edge = const_cast(edge->m_next_edge); if (edge->IsSmooth()) { - const ON_SubD::EdgeTag etag = (2 == edge->TaggedEndIndex()) ? ON_SubD::EdgeTag::SmoothX : ON_SubD::EdgeTag::Smooth; + const ON_SubDEdgeTag etag = (2 == edge->TaggedEndIndex()) ? ON_SubDEdgeTag::SmoothX : ON_SubDEdgeTag::Smooth; if (etag != edge->m_edge_tag) { edge->m_edge_tag = etag; @@ -12219,9 +13767,9 @@ unsigned int ON_SubD::UpdateEdgeTags( unsigned int ON_SubD::UpdateEdgeSectorCoefficients( bool bUnsetSectorCoefficientsOnly - ) + ) const { - ON_SubDLevel* level = ActiveLevelPointer(); + const ON_SubDLevel* level = ActiveLevelConstPointer(); if ( nullptr == level ) return ON_SUBD_RETURN_ERROR(0); @@ -12409,7 +13957,7 @@ unsigned int ON_SubDimple::DeleteComponents( if (bUpdateTagsAndCoefficients) { if (edge->m_face_count != 2) - edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + edge->m_edge_tag = ON_SubDEdgeTag::Crease; edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; @@ -12462,13 +14010,13 @@ unsigned int ON_SubDimple::DeleteComponents( else { if (1 == crease_count && 1 == vertex->m_edge_count && 0 == vertex->m_face_count) - vertex->m_vertex_tag = ON_SubD::VertexTag::Corner; + vertex->m_vertex_tag = ON_SubDVertexTag::Corner; else if (crease_count > 2) - vertex->m_vertex_tag = ON_SubD::VertexTag::Corner; + vertex->m_vertex_tag = ON_SubDVertexTag::Corner; else if (false == bInteriorVertex || crease_count > 1) { if (false == vertex->IsCreaseOrCorner()) - vertex->m_vertex_tag = ON_SubD::VertexTag::Crease; + vertex->m_vertex_tag = ON_SubDVertexTag::Crease; } } } @@ -12492,7 +14040,7 @@ unsigned int ON_SubDimple::DeleteComponents( } } - ChangeContentSerialNumber(false); + ChangeGeometryContentSerialNumber(false); return deleted_component_count; } @@ -13284,8 +14832,24 @@ static unsigned int Internal_MarkStuffAndMaybeMoveVertices( } } - if ( bTransform ) - const_cast(subd).ChangeContentSerialNumberForExperts(false); + if (bTransform) + { + if (3 * marked_vertex_count >= subd.VertexCount()) + { + subd.ClearEvaluationCache(); + } + else + { + ON_SubDVertexIterator vit(subd); + for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + { + if (v->Mark()) + v->VertexModifiedNofification(); + } + subd.UpdateEdgeSectorCoefficients(true); + } + const_cast(subd).ChangeGeometryContentSerialNumberForExperts(false); + } return marked_vertex_count; } @@ -13372,11 +14936,6 @@ unsigned int ON_SubD::TransformComponents( const unsigned int v_count = Internal_TransformComponents(*this, cptr_list, cptr_count, xform, component_location); - if (v_count > 0) - { - this->ClearEvaluationCache(); - } - if (bRestoreMarks) SetComponentMarks(true, marked_components); @@ -13452,10 +15011,10 @@ public: // // tag the original vertex had before the extrusion was performed. - ON_SubD::VertexTag m_initial_vertex_tag = ON_SubD::VertexTag::Unset; + ON_SubDVertexTag m_initial_vertex_tag = ON_SubDVertexTag::Unset; // in complicated cases when the initial vertex is a crease/corner, m_connecting_edge_tag may be set to crease. - ON_SubD::EdgeTag m_connecting_edge_tag = ON_SubD::EdgeTag::Unset; + ON_SubDEdgeTag m_connecting_edge_tag = ON_SubDEdgeTag::Unset; // id of the original vertex - used to sort and search an array of ON_Internal_ExtrudedVertex elements. unsigned int m_initial_vertex_id = 0; @@ -13537,7 +15096,7 @@ public: bool SetConnectingEdgeTag(); - bool ExtrudeVertex(ON_SubD& subd, const ON_Xform& xform) + bool ExtrudeVertex(ON_SubD& subd, bool bIsInset, const ON_Xform& xform) { if (nullptr != m_copied_vertex) return ON_SUBD_RETURN_ERROR(false); @@ -13545,44 +15104,19 @@ public: if (nullptr == m_original_vertex) return ON_SUBD_RETURN_ERROR(false); - //bool bMarkedInteriorCrease = false; - //bool bUnmarkedInteriorCrease = false; - //for (unsigned short vei = 0; vei < m_original_vertex->m_edge_count; ++vei) - //{ - // const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_original_vertex->m_edges[vei].m_ptr); - // if (nullptr == e || 2 != e->m_face_count) - // continue; - // const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(e->m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr) }; - // if (nullptr == f[0] || nullptr == f[1]) - // continue; - // const bool bFaceMark = f[0]->Mark(); - // if (bFaceMark == f[1]->Mark()) - // { - // if (bFaceMark) - // { - // bMarkedInteriorCrease = true; - // if (bUnmarkedInteriorCrease) - // break; - // } - // else - // { - // bUnmarkedInteriorCrease = true; - // if (bMarkedInteriorCrease) - // break; - // } - // } - //} - const ON_3dPoint P = m_original_vertex->ControlNetPoint(); for (;;) { // transform the original vertex - m_original_vertex->m_vertex_tag = ON_SubD::VertexTag::Unset; + m_original_vertex->m_vertex_tag = ON_SubDVertexTag::Unset; - if (false == m_original_vertex->Transform(false, xform)) - break; + if (false == bIsInset) + { + if (false == m_original_vertex->Transform(false, xform)) + break; + } - m_copied_vertex = subd.AddVertex(ON_SubD::VertexTag::Unset, P); + m_copied_vertex = subd.AddVertex(ON_SubDVertexTag::Unset, P); if (nullptr == m_copied_vertex) break; @@ -13599,7 +15133,7 @@ public: break; //if (bMarkedInteriorCrease && bUnmarkedInteriorCrease) - // m_connecting_edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + // m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Crease; for (unsigned short vei = 0; vei < m_original_vertex->m_edge_count; ++vei) { @@ -13699,7 +15233,7 @@ public: const ON_Internal_ExtrudedVertex* rhs ); - void SetBothVertexTags(ON_SubD::VertexTag vertex_tag) + void SetBothVertexTags(ON_SubDVertexTag vertex_tag) { if (nullptr != m_original_vertex) m_original_vertex->m_vertex_tag = vertex_tag; @@ -13709,23 +15243,23 @@ public: { switch (vertex_tag) { - case ON_SubD::VertexTag::Unset: - m_connecting_edge->m_edge_tag = ON_SubD::EdgeTag::Unset; + case ON_SubDVertexTag::Unset: + m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Unset; break; - case ON_SubD::VertexTag::Smooth: - m_connecting_edge->m_edge_tag = ON_SubD::EdgeTag::Smooth; + case ON_SubDVertexTag::Smooth: + m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Smooth; break; - case ON_SubD::VertexTag::Crease: - m_connecting_edge->m_edge_tag = ON_SubD::EdgeTag::SmoothX; + case ON_SubDVertexTag::Crease: + m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::SmoothX; break; - case ON_SubD::VertexTag::Corner: - m_connecting_edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + case ON_SubDVertexTag::Corner: + m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Crease; break; - case ON_SubD::VertexTag::Dart: - m_connecting_edge->m_edge_tag = ON_SubD::EdgeTag::SmoothX; + case ON_SubDVertexTag::Dart: + m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::SmoothX; break; default: - m_connecting_edge->m_edge_tag = ON_SubD::EdgeTag::Unset; + m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Unset; break; } } @@ -13751,7 +15285,7 @@ public: static const ON_Internal_ExtrudedEdge Unset; // tag m_original_edge had before the extrusion was performed. - ON_SubD::EdgeTag m_initial_edge_tag = ON_SubD::EdgeTag::Unset; + ON_SubDEdgeTag m_initial_edge_tag = ON_SubDEdgeTag::Unset; unsigned int m_initial_vertex_id[2] = {}; @@ -13868,10 +15402,10 @@ public: if (nullptr == copied_vertex[evi]) return ON_SUBD_RETURN_ERROR(nullptr); } - m_copied_edge = subd.AddEdge(ON_SubD::EdgeTag::Unset, copied_vertex[0], copied_vertex[1]); + m_copied_edge = subd.AddEdge(ON_SubDEdgeTag::Unset, copied_vertex[0], copied_vertex[1]); if (nullptr == m_copied_edge) return ON_SUBD_RETURN_ERROR(nullptr); - m_original_edge->m_edge_tag = ON_SubD::EdgeTag::Unset; + m_original_edge->m_edge_tag = ON_SubDEdgeTag::Unset; return m_copied_edge; } @@ -13908,38 +15442,38 @@ public: bool InitialCrease() const { - return (ON_SubD::EdgeTag::Crease == m_initial_edge_tag); + return (ON_SubDEdgeTag::Crease == m_initial_edge_tag); } bool InitialCreaseWithCorner() const { - if (ON_SubD::EdgeTag::Crease == m_initial_edge_tag) + if (ON_SubDEdgeTag::Crease == m_initial_edge_tag) { for (unsigned evi = 0; evi < 2; ++evi) { - if (nullptr != m_extruded_vertex[evi] && ON_SubD::VertexTag::Corner == m_extruded_vertex[evi]->m_initial_vertex_tag) + if (nullptr != m_extruded_vertex[evi] && ON_SubDVertexTag::Corner == m_extruded_vertex[evi]->m_initial_vertex_tag) return true; } } return false; } - void SetBothEdgeTags(ON_SubD::EdgeTag edge_tag) + void SetBothEdgeTags(ON_SubDEdgeTag edge_tag) { - if (ON_SubD::EdgeTag::Crease == edge_tag) + if (ON_SubDEdgeTag::Crease == edge_tag) { if (nullptr != m_original_edge) - m_original_edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + m_original_edge->m_edge_tag = ON_SubDEdgeTag::Crease; if (nullptr != m_copied_edge) - m_copied_edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + m_copied_edge->m_edge_tag = ON_SubDEdgeTag::Crease; for (unsigned evi = 0; evi < 2; ++evi) { - if (nullptr != m_extruded_vertex[evi] && ON_SubD::EdgeTag::Unset == m_extruded_vertex[evi]->m_connecting_edge->m_edge_tag) + if (nullptr != m_extruded_vertex[evi] && ON_SubDEdgeTag::Unset == m_extruded_vertex[evi]->m_connecting_edge->m_edge_tag) { - if (ON_SubD::VertexTag::Corner == m_extruded_vertex[evi]->m_initial_vertex_tag) - m_extruded_vertex[evi]->SetBothVertexTags(ON_SubD::VertexTag::Corner); + if (ON_SubDVertexTag::Corner == m_extruded_vertex[evi]->m_initial_vertex_tag) + m_extruded_vertex[evi]->SetBothVertexTags(ON_SubDVertexTag::Corner); else if (nullptr != m_extruded_vertex[evi]->m_connecting_edge) - m_extruded_vertex[evi]->m_connecting_edge->m_edge_tag = ON_SubD::EdgeTag::Smooth; + m_extruded_vertex[evi]->m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Smooth; } } } @@ -14104,24 +15638,24 @@ bool ON_Internal_ExtrudedVertex::SetConnectingEdgeTag() if (nullptr != m_copied_vertex || nullptr != m_connecting_edge) return ON_SUBD_RETURN_ERROR(false); // to late - if (m_initial_vertex_id != m_original_vertex->m_id || m_initial_vertex_tag != m_original_vertex->m_vertex_tag || ON_SubD::EdgeTag::Unset != m_connecting_edge_tag) + if (m_initial_vertex_id != m_original_vertex->m_id || m_initial_vertex_tag != m_original_vertex->m_vertex_tag || ON_SubDEdgeTag::Unset != m_connecting_edge_tag) return ON_SUBD_RETURN_ERROR(false); // corrupt information if (m_extruded_edges_count != 2) { // simple case - m_connecting_edge_tag = ON_SubD::EdgeTag::Crease; + m_connecting_edge_tag = ON_SubDEdgeTag::Crease; return true; } - //if (ON_SubD::VertexTag::Corner == m_initial_vertex_tag) + //if (ON_SubDVertexTag::Corner == m_initial_vertex_tag) //{ // // simple case - // m_connecting_edge_tag = ON_SubD::EdgeTag::Crease; + // m_connecting_edge_tag = ON_SubDEdgeTag::Crease; // return true; //} - if (ON_SubD::VertexTag::Crease != m_initial_vertex_tag && ON_SubD::VertexTag::Corner != m_initial_vertex_tag) + if (ON_SubDVertexTag::Crease != m_initial_vertex_tag && ON_SubDVertexTag::Corner != m_initial_vertex_tag) return true; // leaving m_connecting_edge_tag unset for anything else works best @@ -14141,9 +15675,9 @@ bool ON_Internal_ExtrudedVertex::SetConnectingEdgeTag() else { // the boundary (crease) will move but the attached face does not move - if (ON_SubD::VertexTag::Corner == m_initial_vertex_tag) + if (ON_SubDVertexTag::Corner == m_initial_vertex_tag) { - m_connecting_edge_tag = ON_SubD::EdgeTag::Crease; + m_connecting_edge_tag = ON_SubDEdgeTag::Crease; return true; } bMovedCrease = true; @@ -14165,7 +15699,7 @@ bool ON_Internal_ExtrudedVertex::SetConnectingEdgeTag() // used the collected vertex topology and edge demographics to determine if connecting_edge_tag should be Crease if (bMovedCrease && bStationaryCrease) - m_connecting_edge_tag = ON_SubD::EdgeTag::Crease; + m_connecting_edge_tag = ON_SubDEdgeTag::Crease; return true; } @@ -14422,7 +15956,7 @@ unsigned int ON_SubD::Extrude(const ON_Xform & xform) static bool Internal_SetSideGroupIds(ON_SimpleArray& new_sides) { - const unsigned count = new_sides.UnsignedCount(); + unsigned count = new_sides.UnsignedCount(); if (count <= 0) return false; unsigned side_group_id = 1; @@ -14430,17 +15964,24 @@ static bool Internal_SetSideGroupIds(ON_SimpleArray& n { if (false == new_sides[i].SetSideGroupId(side_group_id)) continue; - unsigned j1 = i + 1; - while (j1 > 0) - { - unsigned j0 = j1; - j1 = 0; + + // propogate side_group_id through all touching components + unsigned j0 = i + 1; + for (bool bContinue = true; bContinue; /*empty iterator*/) + { + bContinue = false; + while (j0 < count && new_sides[j0].m_side_group_id > 0) + ++j0; + while (count > j0 && new_sides[count - 1].m_side_group_id > 0) + --count; for (unsigned j = j0; j < count; ++j) { - if (new_sides[j].SetSideGroupIdFromVertexPairs() && 0 == j1) - j1 = j + 1; + if (new_sides[j].SetSideGroupIdFromVertexPairs()) + bContinue = true; } } + + ++side_group_id; } @@ -14483,7 +16024,7 @@ static bool Internal_DebugValdateExtrudedTopology( return true; } -unsigned int ON_SubD::Internal_ExtrudeComponents( +static bool Internal_IsInsetHack( const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, @@ -14491,6 +16032,36 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( bool bPermitNonManifoldEdgeCreation ) { + if (nullptr == cptr_list || cptr_count <= 0 || false != bPermitNonManifoldEdgeCreation) + return false; + + // inset xform is 0 except xform[0][2] = ON_ZERO_TOLERANCE. + for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) + { + if (xform.m_xform[i][j] == ((0 == i && 2 == j) ? ON_ZERO_TOLERANCE : 0.0) ) + continue; + return false; + } + + for (size_t i = 0; i < cptr_count; ++i) + { + if (nullptr == cptr_list[i].Face()) + return false; + } + + return true; +} + +unsigned int ON_SubD::Internal_ExtrudeComponents( + const ON_Xform& xform_arg, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + bool bExtrudeBoundaries, + bool bPermitNonManifoldEdgeCreation +) +{ + const bool bIsInset = Internal_IsInsetHack(xform_arg, cptr_list, cptr_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation); + const bool bHaveCptrList = (cptr_count > 0 && nullptr != cptr_list); bool bExtrudeAll = false; if (false == bHaveCptrList && 0 == cptr_count) @@ -14503,11 +16074,13 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( bExtrudeAll = bHasBoundary; } if ( - false == xform.IsValidAndNotZeroAndNotIdentity() + false == xform_arg.IsValidAndNotZeroAndNotIdentity() || (false == bHaveCptrList && false == bExtrudeAll) ) return 0; + const ON_Xform& xform = bIsInset ? ON_Xform::IdentityTransformation : xform_arg; + ON_SubDComponentMarksClearAndRestore mark_guard(*this); // Marks every face in cptr_list[]. @@ -14526,6 +16099,8 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( if (0 == marked_vertex_count) return 0; + const bool bDestroyEvaluationCache = (4 * list_vertex_count >= VertexCount() || 4 * list_edge_count >= EdgeCount() || 4 * list_face_count >= FaceCount()); + // Set moved_faces[] = list of faces that will move. ON_SimpleArray moved_faces(list_face_count + 128); ON_SubDFaceIterator fit(*this); @@ -14594,7 +16169,7 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( if (0 == extruded_edge_count) { // no new faces will be made - if (moved_face_count > 0) + if (moved_face_count > 0 && false == bIsInset) { // Every face is moving Transform(xform); @@ -14713,7 +16288,7 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( // Extrude vertices into side edges - creates new vertices and connecting edges. for (unsigned int i = 0; i < extruded_vertices.UnsignedCount(); ++i) { - if (false == extruded_vertices[i].ExtrudeVertex(*this,xform)) + if (false == extruded_vertices[i].ExtrudeVertex(*this, bIsInset, xform)) return 0; } @@ -14744,6 +16319,7 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( // From this point on, we don't bail out, we just press on doing the best that can be done. + // Mark everything a moved face touches // including interior edges and vertices. // Transform any vertices that are not already marked. @@ -14751,20 +16327,27 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( for (unsigned int i = 0; i < moved_face_count; i++) { const ON_SubDFace* f = moved_faces[i]; + if (false == bDestroyEvaluationCache) + f->ClearSavedSubdivisionPoints(); const unsigned int face_edge_count = f->m_edge_count; for (unsigned int fei = 0; fei < face_edge_count; ++fei) { const ON_SubDEdge* e = f->Edge(fei); if (nullptr == e) continue; + if (false == bDestroyEvaluationCache) + e->ClearSavedSubdivisionPoints(); e->SetMark(); for (unsigned int evi = 0; evi < 2; evi++) { ON_SubDVertex* v = const_cast(e->m_vertex[evi]); if (nullptr != v && false == v->Mark()) { - v->Transform(false, xform); + if (false == bIsInset) + v->Transform(false, xform); v->SetMark(); + if (false == bDestroyEvaluationCache) + v->VertexModifiedNofification(); } } } @@ -14779,7 +16362,8 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( extruded_edges[i].ExtrudeFace(*this); // remove cached subdivision calculations - ClearEvaluationCache(); + if (bDestroyEvaluationCache) + ClearEvaluationCache(); // Set tags that are clearly determined by the extrusion. if ( Internal_SetSideGroupIds(extruded_edges)) @@ -14797,16 +16381,16 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( ON_Internal_ExtrudedEdge& extruded_edge = extruded_edges[i1]; if (nullptr != extruded_edge.m_original_edge && 2 != extruded_edge.m_original_edge->m_face_count) { - extruded_edge.m_original_edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + extruded_edge.m_original_edge->m_edge_tag = ON_SubDEdgeTag::Crease; } if (nullptr != extruded_edge.m_copied_edge) { if ( 2 != extruded_edge.m_copied_edge->m_face_count || - (2 == extruded_edge.m_initial_edge_face_count && ON_SubD::EdgeTag::Crease == extruded_edge.m_initial_edge_tag) + (2 == extruded_edge.m_initial_edge_face_count && ON_SubDEdgeTag::Crease == extruded_edge.m_initial_edge_tag) ) - extruded_edge.m_copied_edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + extruded_edge.m_copied_edge->m_edge_tag = ON_SubDEdgeTag::Crease; } if (extruded_edge.InitialCrease()) { @@ -14821,14 +16405,14 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( ON_SubDEdge* e = (0 == k) ? extruded_edge.m_original_edge : extruded_edge.m_copied_edge; if (2 != e->m_face_count) { - e->m_edge_tag = ON_SubD::EdgeTag::Crease; + e->m_edge_tag = ON_SubDEdgeTag::Crease; for (unsigned evi = 0; evi < 2; ++evi) { ON_Internal_ExtrudedVertex* extruded_vertex = extruded_edge.m_extruded_vertex[evi]; - if (ON_SubD::VertexTag::Corner == extruded_vertex->m_initial_vertex_tag) + if (ON_SubDVertexTag::Corner == extruded_vertex->m_initial_vertex_tag) { ON_SubDVertex* v = (0 == k) ? extruded_vertex->m_original_vertex : extruded_vertex->m_copied_vertex; - v->m_vertex_tag = ON_SubD::VertexTag::Corner; + v->m_vertex_tag = ON_SubDVertexTag::Corner; } } } @@ -14843,7 +16427,7 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( ) { for (unsigned i = i0; i < i1; ++i) - extruded_edges[i].SetBothEdgeTags(ON_SubD::EdgeTag::Crease); + extruded_edges[i].SetBothEdgeTags(ON_SubDEdgeTag::Crease); } } } @@ -14851,7 +16435,7 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( for (unsigned i = 0; i < extruded_vertices.UnsignedCount(); ++i) { ON_Internal_ExtrudedVertex& pair = extruded_vertices[i]; - if (ON_SubD::VertexTag::Corner == pair.m_initial_vertex_tag) + if (ON_SubDVertexTag::Corner == pair.m_initial_vertex_tag) { for (unsigned vdex = 0; vdex < 2; ++vdex) { @@ -14859,16 +16443,30 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( if (nullptr == v) continue; if (v->EdgeProperties().m_crease_edge_count >= 3) - v->m_vertex_tag = ON_SubD::VertexTag::Corner; + v->m_vertex_tag = ON_SubDVertexTag::Corner; } } } + if ( false == bDestroyEvaluationCache) + { + for (unsigned int i = 0; i < extruded_vertices.UnsignedCount(); ++i) + { + ON_Internal_ExtrudedVertex& pair = extruded_vertices[i]; + if (nullptr != pair.m_original_vertex) + pair.m_original_vertex->VertexModifiedNofification(); + if ( nullptr != pair.m_copied_vertex) + pair.m_copied_vertex->VertexModifiedNofification(); + } + } + // Calculate unset vertex tags, unset edge tags, edge sector weights. - this->UpdateAllTagsAndSectorCoefficients(true); + if (false == bIsInset) + this->UpdateAllTagsAndSectorCoefficients(true); #if defined(ON_DEBUG) - IsValid(); + if ( false == bIsInset) + IsValid(); #endif // number of moved faces and new faces created by extruding edges @@ -14878,13 +16476,13 @@ unsigned int ON_SubD::Internal_ExtrudeComponents( unsigned int ON_SubD::SetVertexTags( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, - ON_SubD::VertexTag vertex_tag + ON_SubDVertexTag vertex_tag ) { if ( - ON_SubD::VertexTag::Smooth != vertex_tag - && ON_SubD::VertexTag::Crease != vertex_tag - && ON_SubD::VertexTag::Corner != vertex_tag + ON_SubDVertexTag::Smooth != vertex_tag + && ON_SubDVertexTag::Crease != vertex_tag + && ON_SubDVertexTag::Corner != vertex_tag ) return 0; @@ -14909,13 +16507,13 @@ unsigned int ON_SubD::SetVertexTags( unsigned int ON_SubD::SetVertexTags( const ON_SubDComponentPtr* cptr_list, size_t cptr_count, - ON_SubD::VertexTag vertex_tag + ON_SubDVertexTag vertex_tag ) { if ( - ON_SubD::VertexTag::Smooth != vertex_tag - && ON_SubD::VertexTag::Crease != vertex_tag - && ON_SubD::VertexTag::Corner != vertex_tag + ON_SubDVertexTag::Smooth != vertex_tag + && ON_SubDVertexTag::Crease != vertex_tag + && ON_SubDVertexTag::Corner != vertex_tag ) return 0; @@ -14923,8 +16521,14 @@ unsigned int ON_SubD::SetVertexTags( return 0; ON_SubDComponentMarksClearAndRestore mark_guard(*this); + // be less generous with markbits. + ClearComponentMarkBits(true, true, true); - const bool bNewVertexTagIsSmooth = (ON_SubD::VertexTag::Smooth == vertex_tag); + const ON__UINT8 smooth_bias = 1; + const ON__UINT8 crease_bias = 2; + + + const bool bNewVertexTagIsSmooth = (ON_SubDVertexTag::Smooth == vertex_tag); // count and mark vertex candidates // mark edges that may need to have their tag changed @@ -14937,7 +16541,9 @@ unsigned int ON_SubD::SetVertexTags( if (vertex->m_vertex_tag == vertex_tag) continue; - if (ON_SubD::VertexTag::Corner != vertex_tag) + const bool bRemoveCorner = ON_SubDVertexTag::Corner == vertex->m_vertex_tag; + + if (ON_SubDVertexTag::Corner != vertex_tag) { // new vertex_tag is Smooth or Crease if (nullptr == vertex->m_edges || vertex->m_edge_count < 2) @@ -14951,12 +16557,12 @@ unsigned int ON_SubD::SetVertexTags( if (ep.m_boundary_edge_count + ep.m_wire_edge_count > 2) continue; - if (ON_SubD::VertexTag::Smooth == vertex_tag) + if (ON_SubDVertexTag::Smooth == vertex_tag) { if (ep.m_interior_edge_count != vertex->m_edge_count) continue; } - else if (ON_SubD::VertexTag::Crease == vertex_tag) + else if (ON_SubDVertexTag::Crease == vertex_tag) { if (2 == ep.m_crease_edge_count) { @@ -14982,10 +16588,10 @@ unsigned int ON_SubD::SetVertexTags( vertex->m_status.SetRuntimeMark(); if (nullptr != vertex->m_edges) { - if (ON_SubD::VertexTag::Corner == vertex_tag) + if (ON_SubDVertexTag::Corner == vertex_tag) { - const unsigned int crease_count = vertex->EdgeCount(ON_SubD::EdgeTag::Crease); - if (2 == crease_count) + const unsigned int crease_count = vertex->EdgeCount(ON_SubDEdgeTag::Crease); + if (crease_count >= 2) continue; // do not crease additional edges } @@ -15016,9 +16622,9 @@ unsigned int ON_SubD::SetVertexTags( if (0 == candidate_count) return 0; - bool bUpdateTags = (ON_SubD::VertexTag::Crease != vertex_tag); + bool bUpdateTags = (ON_SubDVertexTag::Crease != vertex_tag); - // This for loop is used when new vertex_tag is ON_SubD::VertexTag::Crease. + // This for loop is used when new vertex_tag is ON_SubDVertexTag::Crease. for (int pass = 0; pass < 2 && false == bUpdateTags; pass++) { // More careful analysis is neeeded to accurately mark smooth edges that will become creases @@ -15036,50 +16642,72 @@ unsigned int ON_SubD::SetVertexTags( if (nullptr == v[0] || nullptr == v[1]) continue; - const ON_SubD::VertexTag vtag[2] = { + const ON_SubDVertexTag vtag[2] = { (v[0]->m_status.RuntimeMark() ? vertex_tag : v[0]->m_vertex_tag), (v[1]->m_status.RuntimeMark() ? vertex_tag : v[1]->m_vertex_tag) }; // At least one of v[0] and v[1] had m_vertex_tag changed. + const bool bGettingSmoother = + (v[0]->IsCorner() && ON_SubDVertexTag::Corner != vertex_tag) + || + (v[1]->IsCorner() && ON_SubDVertexTag::Corner != vertex_tag) + || + (v[0]->IsDartOrCreaseOrCorner() && ON_SubDVertexTag::Smooth == vertex_tag) + || + (v[1]->IsDartOrCreaseOrCorner() && ON_SubDVertexTag::Smooth == vertex_tag) + ; - ON_SubD::EdgeTag edge_tag; + ON_SubDEdgeTag edge_tag; for (;;) { if (2 != edge->m_face_count) { - edge_tag = ON_SubD::EdgeTag::Crease; + edge_tag = ON_SubDEdgeTag::Crease; break; } - if (2 == v[0]->m_edge_count && (ON_SubD::VertexTag::Crease == vtag[0] || ON_SubD::VertexTag::Corner == vtag[0])) + if (bGettingSmoother) { - edge_tag = ON_SubD::EdgeTag::Crease; + edge_tag = ON_SubDEdgeTag::Smooth; break; } - if (2 == v[1]->m_edge_count && (ON_SubD::VertexTag::Crease == vtag[1] || ON_SubD::VertexTag::Corner == vtag[1])) + if (2 == v[0]->m_edge_count && (ON_SubDVertexTag::Crease == vtag[0] || ON_SubDVertexTag::Corner == vtag[0])) { - edge_tag = ON_SubD::EdgeTag::Crease; + edge_tag = ON_SubDEdgeTag::Crease; + break; + } + + if (2 == v[1]->m_edge_count && (ON_SubDVertexTag::Crease == vtag[1] || ON_SubDVertexTag::Corner == vtag[1])) + { + edge_tag = ON_SubDEdgeTag::Crease; break; } if ( - (ON_SubD::VertexTag::Crease == vtag[0] || ON_SubD::VertexTag::Corner == vtag[0] || ON_SubD::VertexTag::Dart == vtag[0]) + (ON_SubDVertexTag::Crease == vtag[0] || ON_SubDVertexTag::Corner == vtag[0] || ON_SubDVertexTag::Dart == vtag[0]) && - (ON_SubD::VertexTag::Crease == vtag[1] || ON_SubD::VertexTag::Corner == vtag[1] || ON_SubD::VertexTag::Dart == vtag[1]) + (ON_SubDVertexTag::Crease == vtag[1] || ON_SubDVertexTag::Corner == vtag[1] || ON_SubDVertexTag::Dart == vtag[1]) ) { - edge_tag = ON_SubD::EdgeTag::Crease; + edge_tag = ON_SubDEdgeTag::Crease; break; } - edge_tag = ON_SubD::EdgeTag::Smooth; + edge_tag = ON_SubDEdgeTag::Smooth; break; } - if (ON_SubD::EdgeTag::Crease == edge_tag) + if (ON_SubDEdgeTag::Crease == edge_tag) + { edge->m_status.SetRuntimeMark(); + edge->SetMarkBits(crease_bias); + } + else + { + edge->SetMarkBits(smooth_bias); + } } // make sure new crease vertices will have the right number of creased edges @@ -15143,6 +16771,11 @@ unsigned int ON_SubD::SetVertexTags( changed_vertex_count++; vertex->m_vertex_tag = vertex_tag; vertex->VertexModifiedNofification(); + if (ON_SubDVertexTag::Corner == vertex_tag && vertex->EdgeCount(ON_SubDEdgeTag::Crease) >= 2) + { + // do not crease additional edges + continue; + } for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++) { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); @@ -15153,20 +16786,21 @@ unsigned int ON_SubD::SetVertexTags( && false == edge->IsCrease() ) { - const_cast(edge)->m_edge_tag = ON_SubD::EdgeTag::Crease; + const_cast(edge)->m_edge_tag = ON_SubDEdgeTag::Crease; edge->EdgeModifiedNofification(); } edge->m_status.SetRuntimeMark(); + const ON_SubDVertex* other_vertex = edge->OtherEndVertex(vertex); other_vertex->m_status.SetRuntimeMark(); if ( false == bNewVertexTagIsSmooth - && ON_SubD::EdgeTag::Crease == edge->m_edge_tag + && ON_SubDEdgeTag::Crease == edge->m_edge_tag && other_vertex->IsSmooth() ) { - const_cast(other_vertex)->m_vertex_tag = ON_SubD::VertexTag::Dart; + const_cast(other_vertex)->m_vertex_tag = ON_SubDVertexTag::Dart; other_vertex->VertexModifiedNofification(); } } @@ -15184,11 +16818,11 @@ unsigned int ON_SubD::SetVertexTags( if (nullptr == v[0] || nullptr == v[1]) continue; - ON_SubD::EdgeTag edge_tag; + ON_SubDEdgeTag edge_tag; if (v[0]->IsDartOrCreaseOrCorner() && v[1]->IsDartOrCreaseOrCorner()) - edge_tag = ON_SubD::EdgeTag::Crease; + edge_tag = (smooth_bias == edge->MarkBits()) ? ON_SubDEdgeTag::SmoothX : ON_SubDEdgeTag::Crease; else - edge_tag = ON_SubD::EdgeTag::Smooth; + edge_tag = ON_SubDEdgeTag::Smooth; if (edge->m_edge_tag == edge_tag) continue; @@ -15201,25 +16835,26 @@ unsigned int ON_SubD::SetVertexTags( { if (false == vertex->m_status.RuntimeMark()) continue; - const unsigned int crease_count = vertex->EdgeCount(ON_SubD::EdgeTag::Crease); - ON_SubD::VertexTag vtag = vertex->m_vertex_tag; + const unsigned int crease_count = vertex->EdgeCount(ON_SubDEdgeTag::Crease); + ON_SubDVertexTag vtag = vertex->m_vertex_tag; if (2 == crease_count) { if ( false == vertex->IsCreaseOrCorner() ) - vtag = ON_SubD::VertexTag::Crease; + vtag = ON_SubDVertexTag::Crease; } else if (1 == crease_count) - vtag = ON_SubD::VertexTag::Dart; + vtag = ON_SubDVertexTag::Dart; else if (crease_count > 2) - vtag = ON_SubD::VertexTag::Corner; + vtag = ON_SubDVertexTag::Corner; else - vtag = ON_SubD::VertexTag::Smooth; + vtag = ON_SubDVertexTag::Smooth; if (vertex->m_vertex_tag == vtag) continue; const_cast(vertex)->m_vertex_tag = vtag; vertex->ClearSavedSubdivisionPoints(); } + ClearComponentMarkBits(true, true, true); ClearEvaluationCache(); UpdateAllTagsAndSectorCoefficients(false); @@ -15229,10 +16864,10 @@ unsigned int ON_SubD::SetVertexTags( unsigned int ON_SubD::SetEdgeTags( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, - ON_SubD::EdgeTag edge_tag + ON_SubDEdgeTag edge_tag ) { - if (ON_SubD::EdgeTag::Smooth != edge_tag && ON_SubD::EdgeTag::Crease != edge_tag) + if (ON_SubDEdgeTag::Smooth != edge_tag && ON_SubDEdgeTag::Crease != edge_tag) return 0; if ( @@ -15256,10 +16891,10 @@ unsigned int ON_SubD::SetEdgeTags( unsigned int ON_SubD::SetEdgeTags( const ON_SubDComponentPtr* cptr_list, size_t cptr_count, - ON_SubD::EdgeTag edge_tag + ON_SubDEdgeTag edge_tag ) { - if (ON_SubD::EdgeTag::Smooth != edge_tag && ON_SubD::EdgeTag::Crease != edge_tag) + if (ON_SubDEdgeTag::Smooth != edge_tag && ON_SubDEdgeTag::Crease != edge_tag) return 0; if ( @@ -15271,7 +16906,7 @@ unsigned int ON_SubD::SetEdgeTags( unsigned int changed_edge_count = 0; - const bool bChangeToSmooth = (ON_SubD::EdgeTag::Smooth == edge_tag) ? true : false; + const bool bChangeToSmooth = (ON_SubDEdgeTag::Smooth == edge_tag) ? true : false; for (size_t i = 0; i < cptr_count; i++) { @@ -15293,7 +16928,7 @@ unsigned int ON_SubD::SetEdgeTags( ON_SubDVertex* v = const_cast(edge->m_vertex[evi]); if (nullptr == v) continue; - v->m_vertex_tag = ON_SubD::VertexTag::Unset; + v->m_vertex_tag = ON_SubDVertexTag::Unset; v->ClearSavedSubdivisionPoints(); } } @@ -15306,7 +16941,7 @@ unsigned int ON_SubD::SetEdgeTags( ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { - if (ON_SubD::VertexTag::Unset != v->m_vertex_tag) + if (ON_SubDVertexTag::Unset != v->m_vertex_tag) continue; unsigned crease_count = 0; const unsigned vertex_edge_count = v->EdgeCount(); @@ -15322,20 +16957,20 @@ unsigned int ON_SubD::SetEdgeTags( break; } } - ON_SubD::VertexTag vertex_tag; + ON_SubDVertexTag vertex_tag; switch (crease_count) { case 0: - vertex_tag = ON_SubD::VertexTag::Smooth; + vertex_tag = ON_SubDVertexTag::Smooth; break; case 1: - vertex_tag = ON_SubD::VertexTag::Dart; + vertex_tag = ON_SubDVertexTag::Dart; break; case 2: - vertex_tag = ON_SubD::VertexTag::Crease; + vertex_tag = ON_SubDVertexTag::Crease; break; default: - vertex_tag = ON_SubD::VertexTag::Corner; + vertex_tag = ON_SubDVertexTag::Corner; break; } if (v->m_vertex_tag != vertex_tag) @@ -15348,13 +16983,13 @@ unsigned int ON_SubD::SetEdgeTags( ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { - ON_SubD::EdgeTag e_tag = e->m_edge_tag; + ON_SubDEdgeTag e_tag = e->m_edge_tag; if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) - e_tag = ON_SubD::EdgeTag::Unset; - else if (ON_SubD::EdgeTag::Smooth == e_tag - && ON_SubD::VertexTag::Smooth != e->m_vertex[0]->m_vertex_tag - && ON_SubD::VertexTag::Smooth != e->m_vertex[1]->m_vertex_tag) - e_tag = ON_SubD::EdgeTag::Unset; + e_tag = ON_SubDEdgeTag::Unset; + else if (ON_SubDEdgeTag::Smooth == e_tag + && ON_SubDVertexTag::Smooth != e->m_vertex[0]->m_vertex_tag + && ON_SubDVertexTag::Smooth != e->m_vertex[1]->m_vertex_tag) + e_tag = ON_SubDEdgeTag::Unset; if (e_tag != e->m_edge_tag) { const_cast(e)->m_edge_tag = e_tag; @@ -15375,13 +17010,13 @@ unsigned int ON_SubD::RemoveAllCreases() { if ( false == e->IsCrease() || 2 != e->m_face_count) continue; - const_cast(e)->m_edge_tag = ON_SubD::EdgeTag::Smooth; + const_cast(e)->m_edge_tag = ON_SubDEdgeTag::Smooth; e->UnsetSectorCoefficientsForExperts(); for (int evi = 0; evi < 2; evi++) { if (nullptr == e->m_vertex[evi]) continue; - const_cast(e->m_vertex[evi])->m_vertex_tag = ON_SubD::VertexTag::Unset; + const_cast(e->m_vertex[evi])->m_vertex_tag = ON_SubDVertexTag::Unset; } ++changed_count; } @@ -15451,13 +17086,13 @@ const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor( if (bIsSmooth) { // edge is smooth so vertex must be smooth - if (ON_SubD::VertexTag::Smooth != v->m_vertex_tag) + if (ON_SubDVertexTag::Smooth != v->m_vertex_tag) break; } else { // edge is crease so vertex must be crease - if (ON_SubD::VertexTag::Crease != v->m_vertex_tag) + if (ON_SubDVertexTag::Crease != v->m_vertex_tag) break; } } @@ -16124,13 +17759,13 @@ void ON_SubDEdgeChain::Dump(class ON_TextLog& text_log) const text_log.Print(L"SubD[%" PRIu64 "]", subd_sn); if (bSubDIdIsNotNil) { - text_log.Print(L" persistent subd id = "); + text_log.Print(L" persistent SubD id = "); text_log.Print(m_persistent_subd_id); } } else { - text_log.Print(L"Persistent subd id = "); + text_log.Print(L"Persistent SubD id = "); text_log.Print(m_persistent_subd_id); } text_log.Print("%u edges.\n", edge_count); @@ -16176,11 +17811,11 @@ void ON_SubDEdgeChain::Dump(class ON_TextLog& text_log) const } else if( 0 == edge_count) { - text_log.Print("Empty subd edge chain."); + text_log.Print("Empty SubD edge chain."); } else { - text_log.Print("Corrupt subd edge chain."); + text_log.Print("Corrupt SubD edge chain."); } text_log.PrintNewLine(); @@ -17203,7 +18838,8 @@ unsigned int ON_SubDEdgeChain::OrientEdgesIntoEdgeChains( ON_SubDComponentList::ON_SubDComponentList(const ON_SubDComponentList& src) : m_subd_runtime_serial_number(src.m_subd_runtime_serial_number) - , m_subd_content_serial_number(src.m_subd_content_serial_number) + , m_subd_geometry_content_serial_number(src.m_subd_geometry_content_serial_number) + , m_subd_render_content_serial_number(src.m_subd_render_content_serial_number) , m_subd_vertex_count(src.m_subd_vertex_count) , m_subd_edge_count(src.m_subd_edge_count) , m_subd_face_count(src.m_subd_face_count) @@ -17217,7 +18853,8 @@ ON_SubDComponentList& ON_SubDComponentList::operator=(const ON_SubDComponentList if (this != &src) { m_subd_runtime_serial_number = src.m_subd_runtime_serial_number; - m_subd_content_serial_number = src.m_subd_content_serial_number; + m_subd_geometry_content_serial_number = src.m_subd_geometry_content_serial_number; + m_subd_render_content_serial_number = src.m_subd_render_content_serial_number; m_subd_vertex_count = src.m_subd_vertex_count; m_subd_edge_count = src.m_subd_edge_count; m_subd_face_count = src.m_subd_face_count; @@ -17232,9 +18869,14 @@ ON__UINT64 ON_SubDComponentList::SubDRuntimeSerialNumber() const return m_subd_runtime_serial_number; } -ON__UINT64 ON_SubDComponentList::SubDContentSerialNumber() const +ON__UINT64 ON_SubDComponentList::SubDGeometryContentSerialNumber() const { - return m_subd_content_serial_number; + return m_subd_geometry_content_serial_number; +} + +ON__UINT64 ON_SubDComponentList::SubDRenderContentSerialNumber() const +{ + return m_subd_render_content_serial_number; } unsigned int ON_SubDComponentList::Count() const @@ -17279,10 +18921,10 @@ const ON_SubD& ON_SubDComponentList::SubD() const return m_subd; } -ON__UINT64 ON_SubDComponentList::UpdateContentSerialNumber() +void ON_SubDComponentList::UpdateContentSerialNumbers() { - m_subd_content_serial_number = m_subd.ContentSerialNumber(); - return m_subd_content_serial_number; + m_subd_geometry_content_serial_number = m_subd.GeometryContentSerialNumber(); + m_subd_render_content_serial_number = m_subd.RenderContentSerialNumber(); } unsigned int ON_SubDComponentList::UpdateSubDForExperts(const ON_SubD & subd, bool bUpdateDeletedComponents) @@ -17314,7 +18956,8 @@ unsigned int ON_SubDComponentList::UpdateSubDForExperts(const ON_SubD & subd, bo m_component_list.SetCount(count1); m_subd.ShareDimple(subd); m_subd_runtime_serial_number = m_subd.RuntimeSerialNumber(); - m_subd_content_serial_number = m_subd.ContentSerialNumber(); + m_subd_geometry_content_serial_number = m_subd.GeometryContentSerialNumber(); + m_subd_render_content_serial_number = m_subd.RenderContentSerialNumber(); return Count(); } @@ -17530,7 +19173,8 @@ unsigned ON_SubDComponentList::Internal_Create(const ON_SubD & subd, bool bAddVe { m_subd.ShareDimple(subd); m_subd_runtime_serial_number = subd.RuntimeSerialNumber(); - m_subd_content_serial_number = subd.ContentSerialNumber(); + m_subd_geometry_content_serial_number = subd.GeometryContentSerialNumber(); + m_subd_render_content_serial_number = subd.RenderContentSerialNumber(); } return m_component_list.UnsignedCount(); } @@ -17598,7 +19242,8 @@ unsigned ON_SubDComponentList::Internal_RemoveComponents( void ON_SubDComponentList::Destroy() { m_subd_runtime_serial_number = 0; - m_subd_content_serial_number = 0; + m_subd_geometry_content_serial_number = 0; + m_subd_render_content_serial_number = 0; m_component_list.SetCount(0); m_subd.ShareDimple(ON_SubD::Empty); } @@ -17884,14 +19529,14 @@ void ON_SubDComponentFilter::ClearFaceTopologyFilter() m_face_topology_filter = ON_SubDComponentFilter::Topology::Unset; } -bool ON_SubDComponentFilter::AcceptVertexTag(ON_SubD::VertexTag vertex_tag) const +bool ON_SubDComponentFilter::AcceptVertexTag(ON_SubDVertexTag vertex_tag) const { - if (ON_SubD::VertexTag::Unset == m_vertex_tag_filter[0]) + if (ON_SubDVertexTag::Unset == m_vertex_tag_filter[0]) return true; // no tag filter for (size_t i = 0; i < sizeof(m_vertex_tag_filter) / sizeof(m_vertex_tag_filter[0]); ++i) { - if (ON_SubD::VertexTag::Unset == m_vertex_tag_filter[i]) + if (ON_SubDVertexTag::Unset == m_vertex_tag_filter[i]) break; if (m_vertex_tag_filter[i] != vertex_tag) continue; @@ -17900,13 +19545,13 @@ bool ON_SubDComponentFilter::AcceptVertexTag(ON_SubD::VertexTag vertex_tag) cons return false; } -void ON_SubDComponentFilter::AddAcceptedVertexTag(ON_SubD::VertexTag vertex_tag) +void ON_SubDComponentFilter::AddAcceptedVertexTag(ON_SubDVertexTag vertex_tag) { for (size_t i = 0; i < sizeof(m_vertex_tag_filter) / sizeof(m_vertex_tag_filter[0]); ++i) { if (vertex_tag == m_vertex_tag_filter[i]) break; - if (ON_SubD::VertexTag::Unset == m_vertex_tag_filter[i]) + if (ON_SubDVertexTag::Unset == m_vertex_tag_filter[i]) { m_vertex_tag_filter[i] = vertex_tag; break; @@ -17917,18 +19562,18 @@ void ON_SubDComponentFilter::AddAcceptedVertexTag(ON_SubD::VertexTag vertex_tag) void ON_SubDComponentFilter::ClearVertexTagFilter() { for (size_t i = 0; i < sizeof(m_vertex_tag_filter) / sizeof(m_vertex_tag_filter[0]); ++i) - m_vertex_tag_filter[i] = ON_SubD::VertexTag::Unset; + m_vertex_tag_filter[i] = ON_SubDVertexTag::Unset; } -bool ON_SubDComponentFilter::AcceptEdgeTag(ON_SubD::EdgeTag edge_tag) const +bool ON_SubDComponentFilter::AcceptEdgeTag(ON_SubDEdgeTag edge_tag) const { - if (ON_SubD::EdgeTag::Unset == m_edge_tag_filter[0]) + if (ON_SubDEdgeTag::Unset == m_edge_tag_filter[0]) return true; // no tag filter for (size_t i = 0; i < sizeof(m_edge_tag_filter) / sizeof(m_edge_tag_filter[0]); ++i) { - if (ON_SubD::EdgeTag::Unset == m_edge_tag_filter[i]) + if (ON_SubDEdgeTag::Unset == m_edge_tag_filter[i]) break; if (m_edge_tag_filter[i] != edge_tag) continue; @@ -17937,13 +19582,13 @@ bool ON_SubDComponentFilter::AcceptEdgeTag(ON_SubD::EdgeTag edge_tag) const return false; } -void ON_SubDComponentFilter::AddAcceptedEdgeTag(ON_SubD::EdgeTag edge_tag) +void ON_SubDComponentFilter::AddAcceptedEdgeTag(ON_SubDEdgeTag edge_tag) { for (size_t i = 0; i < sizeof(m_edge_tag_filter) / sizeof(m_edge_tag_filter[0]); ++i) { if (edge_tag == m_edge_tag_filter[i]) break; - if (ON_SubD::EdgeTag::Unset == m_edge_tag_filter[i]) + if (ON_SubDEdgeTag::Unset == m_edge_tag_filter[i]) { m_edge_tag_filter[i] = edge_tag; break; @@ -17954,7 +19599,7 @@ void ON_SubDComponentFilter::AddAcceptedEdgeTag(ON_SubD::EdgeTag edge_tag) void ON_SubDComponentFilter::ClearEdgeTagFilter() { for (size_t i = 0; i < sizeof(m_edge_tag_filter) / sizeof(m_edge_tag_filter[0]); ++i) - m_edge_tag_filter[i] = ON_SubD::EdgeTag::Unset; + m_edge_tag_filter[i] = ON_SubDEdgeTag::Unset; } bool ON_SubDComponentFilter::AcceptFaceEdgeCount( diff --git a/opennurbs_subd.h b/opennurbs_subd.h index 197d13cc..188b161b 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -121,9 +121,35 @@ public: #define OPENNURBS_SUBD_INC_ /// -/// ON_SubDTextureDomainType identifies the way face 2d texture coordinate domains are set. +/// ON_SubDGetControlNetMeshPriority specifies what type of ON_SubD information +/// is most important to transfer to the ON_Mesh. /// -enum class ON_SubDTextureDomainType : unsigned char +enum class ON_SubDGetControlNetMeshPriority : unsigned char +{ + /// + /// Create a mesh that can be used to reliably transfer SubD control net geometry, + /// topology, and interior edge crease information. Use this option when the mesh + /// will be used to create a Catmull-Clark subdivision surface. SubD texture + /// coordinates cannot be recovered from the mesh. + /// + Geometry = 0, + + /// + /// Create a mesh that has the shape of the SubD control net and + /// includes SubD texture coordinate information. + /// Use this option when the mesh will be used to create an image of the + /// SubD control net that relies on texture coordinates. SubD interior edge + /// crease inforation cannot be recovered from the mesh. Most applications will + /// not be able to use the mesh to recreate a Catmull-Clark subdivision surface. + /// + TextureCoordinates = 1 +}; + + +/// +/// ON_SubDTextureCoordinateType identifies the way ON_SubDMeshFragment texture coordinates are set from an ON_SubDFace. +/// +enum class ON_SubDTextureCoordinateType : unsigned char { /// /// Texture domains are not set. @@ -131,33 +157,267 @@ enum class ON_SubDTextureDomainType : unsigned char Unset = 0, /// - /// Each face texture domain is a unique rectangle of normlized texture space. + /// Each SubDFace uses the unit square in texture space. /// - PerFace = 1, + Unpacked = 1, /// - /// Each face texture domain is a unique rectangle of normlized texture space. - /// When possible, faces are partitioned into quad groups. Adjactent members - /// of the group are assigned adjacent rectangles in texture space. + /// The face's pack rect is used to set fragment texture coordintes. + /// When possible, adjacent quads with the same ON_SubDFace::PackId() value are assigned adjacent + /// rectangles in texture space. /// Packed = 2, /// - /// All face texture domain values are zero. + /// All ON_SubDMeshFragment texture coordinate values are zero. /// Zero = 3, /// - /// All face texture domain values are nan. + /// All ON_SubDMeshFragment texture coordinate values are ON_DBL_QNAN. /// Nan = 4, /// - /// Code outside of opennurbs set the values. No other information is available. + /// All ON_SubDMeshFragment texture coordinate values are set by + /// barycentric interpolation of ON_SubDFace.TexturePoint() values. /// - Custom = 7, + FromFaceTexturePoints = 6, + + /// + /// Texture coordinates are set from an ON_TextureMapping and transformation specificed + /// by ON_SubD::TextureMappingTag(). In all other cases, ON_SubD::TextureMappingTag() + /// can be set, but is ignored. + /// + FromMapping = 7, }; +#pragma region RH_C_SHARED_ENUM [ON_SubDVertexTag] [Rhino.Geometry.SubDVertexTag] [byte] +/// +/// ON_SubDVertexTag identifies the type of subdivision vertex. Different tags use +/// different subdivision algorithms to determine where the subdivision point and +/// limit point are located. There are toplological constraints that restrict which +/// tags can be assigned. +/// +enum class ON_SubDVertexTag : unsigned char +{ + /// + /// Not a valid vertex tag and the default value for ON_SubDVertex.m_vertex_tag. + /// This encourages developers to thoughtfully initialize ON_SubDVertex.m_vertex_tag + /// or use ON_SubD.UpdateAllTagsAndSectorCoefficients() to automatically set the + /// m_vertex_tag values at an appropriate time. + /// + Unset = 0, + + /// + /// Must be an interior vertex. + /// All edges attached to a smooth vertex must be tagged as ON_SubDEdgeTag::Smooth + /// and must have 2 faces. + /// + Smooth = 1, + + /// + /// Can be an interior or a boundary vertex. + /// Exactly two edges ending at a crease vertex must be tagged as ON_SubDEdgeTag::Crease and may + /// have 1 or 2 faces. + /// All other edges ending at a crease must be tagged as tagON_SubD::EdgeTag::Smooth and have 2 faces. + /// Below P = ON_SubDVertex.ControlNetPoint() and Ai = ON_SubDVertex.Edge(i)->OtherEndVertex()->ControlNetPoint(). + /// A crease vertex subdivision point is (6*P + A1 + A2)/8. + /// A crease vertex limit surface point is (4*P + A1 + A2)/6. + /// + Crease = 2, + + /// + /// Can be an interior, boundary, nonmanifold, or isolated vertex. + /// The location of a corner vertex is fixed. + /// The all subdivision points and the limit point are at the initial vertex location. + /// The edges ending at a corner vertex can be smooth or crease edges. + /// A corner vertex subdivision point is P where P = ON_SubDVertex.ControlNetPoint(). + /// A corner vertex limit surface point is P where P = ON_SubDVertex.ControlNetPoint(). + /// + Corner = 3, + + /// + /// Must be an interior vertex. + /// Every edge attached to a dart vertex must have 2 faces. + /// Exactly one edge attached to a dart vertex must be tagged as ON_SubDEdgeTag::Crease + /// and every other attached edge must be tagged as ON_SubDEdgeTag::smooth. + /// + Dart = 4 +}; +#pragma endregion + +#pragma region RH_C_SHARED_ENUM [ON_SubDEdgeTag] [Rhino.Geometry.SubDEdgeTag] [byte] +/// +/// ON_SubDEdgeTag identifies the type of subdivision edge. Different tags use +/// different subdivision algorithms to calculate the subdivision point. +/// +enum class ON_SubDEdgeTag : unsigned char +{ + /// + /// Not a valid edge tag and the default value for ON_SubDEdge.m_edge_tag. + /// This encourages developers to thoughtfully initialize ON_SubDEdge.m_edge_tag. + /// or use ON_SubD.UpdateAllTagsAndSectorCoefficients() to automatically set the + /// m_edge_tag values at an appropriate time. + /// + Unset = 0, + + /// + /// At least one the edge's vertices must be tagged as ON_SubDVertexTag::Smooth. + /// The edge must have exactly two faces. + /// The edge's subdivision point is (A1 + A2 + S(f1) + S(f2))/4, where + /// Ai = ON_SubDEdge.Vertex(i)->ControlNetPoint() and + /// S(fi) = ON_SubDEdge.Face(i)->SubdivisionPoint(). + /// + Smooth = 1, + + /// + /// Both of the edge's vertices must be tagged as ON_SubDVertexTag::Dart, + /// ON_SubDVertexTag::Crease, or ON_SubDVertexTag::Corner. + /// (The vertex tags can be different.) The edge can have any number of faces. + /// The edge's subdivision point is (A1+A2)/2 where Ai = ON_SubDEdge.Vertex(i)->ControlNetPoint(). + /// + Crease = 2, + + /// + /// This tag appears only on level 0 edges that have exactly two neighboring faces + /// and both of the edge's vertices are tagged as ON_SubDVertexTag::Dart, + /// ON_SubDVertexTag::Crease, or ON_SubDVertexTag::Corner. + /// The level 1 subdivision point for a level 0 edge tagged as ON_SubDEdgeTag::SmoothX + /// is the standard smooth edge subdivision point. + /// When subdivided, the new subdivision vertex will be tagged + /// as ON_SubDVertexTag::Smooth and the subdivided edges will + /// be tagged as ON_SubDEdgeTag::Smooth. + /// The tag ON_SubDEdgeTag::SmoothX can only appear on a level 0 edge. + /// This tag exists because the ON_SubD subdivision + /// algorithm requires any edge with both end vertices + /// tagged as not smooth must be subdivided at its midpoint. + /// Sector iterators treat "SmoothX" edges as smooth. + /// Both edge m_sector_coefficient[] values must be set so the smooth subdivided edges will be valid. + /// + SmoothX = 4 +}; +#pragma endregion + +class ON_CLASS ON_SubDToBrepParameters +{ +public: + ON_SubDToBrepParameters() = default; + ~ON_SubDToBrepParameters() = default; + ON_SubDToBrepParameters(const ON_SubDToBrepParameters&) = default; + ON_SubDToBrepParameters& operator=(const ON_SubDToBrepParameters&) = default; + + /* + Description: + Default ON_SubDToBrepParameters settings. + Remarks: + These are the settings used by ON_SubD::BrepForm() + */ + static const ON_SubDToBrepParameters Default; + + /* + Description: + Default ON_SubDToBrepParameters settings for creating an unpacked brep. + */ + static const ON_SubDToBrepParameters DefaultUnpacked; + + /* + Description: + Default ON_SubDToBrepParameters settings for creating an packed brep. + */ + static const ON_SubDToBrepParameters DefaultPacked; + + static int Compare( + const ON_SubDToBrepParameters& lhs, + const ON_SubDToBrepParameters& rhs + ); + static int CompareFromPointers( + const ON_SubDToBrepParameters* lhs, + const ON_SubDToBrepParameters* rhs + ); + + /// + /// ON_SubDToBrepParameters::Vertex identifies the options for post processing extraorindary vertices. + /// + enum class VertexProcess : unsigned char + { + /// + /// The NURBS patches are used as is. + /// At extraordinary vertices, the brep vertex may not be G1. + /// Typically the deviation bewtween the brep and SubD surface is smallest with this option. + /// + None = 0, + + /// + /// The brep vertex is G1. + /// Typically the deviation bewtween the brep and SubD surface is larger than None. + /// + LocalG1 = 1, + + /// + /// The brep vertex is G2. + /// Typically the deviation bewtween the brep and SubD surface is larger than LocalG1. + /// + LocalG2 = 2, + + /// + /// The brep vertex is G1. + /// Typically the deviation bewtween the brep and SubD surface is larger than None. + /// In some cases GlobalG1 produces the most please aesthetic result. + /// + ///GlobalG1 = 3, + }; + + static ON_SubDToBrepParameters::VertexProcess VertexProcessFromUnsigned( + unsigned int vertex_process_as_unsigned + ); + + /* + Returns: + Option used for post processing extraorindary vertices. + */ + ON_SubDToBrepParameters::VertexProcess ExtraordinaryVertexProcess() const; + + /* + Description: + Set the ExtraordinaryVertexProcess() property. + Parameters: + ev_process - [in] + */ + void SetExtraordinaryVertexProcess( + ON_SubDToBrepParameters::VertexProcess ev_process + ); + + /* + Returns: + If true, then quad packs of SubD faces are returned as a single brep face. + Otherwise each SubD face generates a brep face. + Remarks: + SubD n-gons with n != 4 always generate n brep faces. + */ + bool PackFaces() const; + + /* + Description: + Set the PackFaces() property. + Parameters: + bPackFaces - [in] + */ + void SetPackFaces( + bool bPackFaces + ); + +private: + bool m_bPackFaces = false; + ON_SubDToBrepParameters::VertexProcess m_extraordinary_vertex_process = ON_SubDToBrepParameters::VertexProcess::LocalG1; + unsigned short m_reserved1 = 0; + unsigned int m_reserved2 = 0; + double m_reserved3 = 0.0; +}; + +bool operator==(const ON_SubDToBrepParameters& lhs, const ON_SubDToBrepParameters& rhs); +bool operator!=(const ON_SubDToBrepParameters& lhs, const ON_SubDToBrepParameters& rhs); + class ON_CLASS ON_SubDVertexPtr { public: @@ -948,6 +1208,32 @@ public: ON__UINT8 ClearMarkBits() const; + /* + Parameters: + null_component_value - [in] + Value to return if the component is null. + Returns: + If this is not null, the group id of the component is returned. + Otherwise null_component_value is returned. + */ + unsigned int GroupId( + unsigned int null_component_value + ) const; + + /* + Description: + Sets ON_SubDComponentBase.m_group_id. + Parameters: + group_id - [in] + Value to return if the component is null. + Returns: + True if the component is not null and the group id was set. + False if the component is null. + */ + bool SetGroupId( + unsigned int group_id + ); + static const ON_SubDComponentPtr CreateNull( ON_SubDComponentPtr::Type component_type, @@ -1107,8 +1393,7 @@ public: bool BothAreNotNull() const; public: - const static ON_SubDComponentPtrPair Null; - + static const ON_SubDComponentPtrPair Null; }; #if defined(ON_DLL_TEMPLATE) @@ -1169,6 +1454,176 @@ private: ON_SubDComponentPtrPairHashTable& operator=(const ON_SubDComponentPtrPairHashTable&) = delete; }; +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDSectorId +// + +class ON_CLASS ON_SubDSectorId +{ +public: + ON_SubDSectorId() = default; + ~ON_SubDSectorId() = default; + ON_SubDSectorId(const ON_SubDSectorId&) = default; + ON_SubDSectorId& operator=(const ON_SubDSectorId&) = default; + +public: + // VertexId(), MinimumFaceId(), and SectorFaceCount() are all zero. + static const ON_SubDSectorId Zero; + + // VertexId() and MinimumFaceId() are zero. SectorFaceCount() = 0xFFFFFFFF; + static const ON_SubDSectorId Invalid; + +public: + /* + Description: + Dictionary compare of VertexId() and MinimumFaceId() in that order. + */ + static int CompareVertexIdAndMinimumFaceId(ON_SubDSectorId lhs, ON_SubDSectorId rhs); + + static int CompareVertexId(ON_SubDSectorId lhs, ON_SubDSectorId rhs); + static int CompareMinimumFaceId(ON_SubDSectorId lhs, ON_SubDSectorId rhs); + + /* + Description: + Dictionary compare of VertexId() and MinimumFaceId() in that order. + */ + static int CompareVertexIdAndMinimumFaceIdFromPointers(const ON_SubDSectorId* lhs, const ON_SubDSectorId* rhs); + + static int CompareVertexIdFromPointers(const ON_SubDSectorId* lhs, const ON_SubDSectorId* rhs); + static int CompareMinimumFaceIdFromPointers(const ON_SubDSectorId* lhs, const ON_SubDSectorId* rhs); + + /* + Description: + Dictionary compare of VertexId(), MinimumFaceId(), and SectorFaceCount() in that order. + */ + static int Compare(ON_SubDSectorId lhs, ON_SubDSectorId rhs); + + /* + Description: + Dictionary compare of VertexId(), MinimumFaceId(), and SectorFaceCount() in that order. + */ + static int CompareFromPointers(const ON_SubDSectorId* lhs, const ON_SubDSectorId* rhs); + + // No initialized construction for performance reasons. + // If initialization is required, then use sector_id = ON_SubDSectorId::Zero or sector_id = ON_SubDSectorId::Create(...). + + /* + Description: + Create a sector id from a vertex and face in the sector. A sector id uniquely identifies + a sector in the context of a single ON_SubD. + Parameters: + vertex - [in] + face - [in] + A face in the sector. + If vertex->IsSingleSectorVertex() is true, then face can be nullptr. + Returns: + If the vertex and face are not nullptr and the face is in a sector of the vertex, + a nonzero zector id is returned. Otherwise ON_SubDSectorId::Invalid is returned. + */ + static const ON_SubDSectorId Create( + const class ON_SubDVertex* vertex, + const class ON_SubDFace* face + ); + + /* + Description: + This function is used to create ON_SubDSectorId values for searching or comparing + to values from ON_SubDSectorId::Create(). Use ON_SubDSectorId::Create() for all + other uses. + Parameters: + vertex - [in] + face - [in] + A face in the sector. + Returns: + A ON_SubDSectorId with the specified values for vertex_id and minimum_face_id. + The sector face count will be zero. + */ + static const ON_SubDSectorId CreateFromIds( + unsigned int vertex_id, + unsigned int minimum_face_id + ); + + /* + Returns: + The sector's center vertex id. + Remarks: + Either SectorId(), VertexId(), and MinimumFaceId() are all zero or are all nonzero. + */ + const unsigned int VertexId() const; + + /* + Returns: + The sector's minimum face id. + */ + const unsigned int MinimumFaceId() const; + + /* + Returns: + Number of faces in the sector. + */ + const unsigned int SectorFaceCount() const; + + /* + Returns: + True if this sector id is zero. + */ + bool IsZero() const; + + /* + Returns: + True if VertexId(), MinimumFaceId(), and SectorFaceCount() are all not zero. + */ + bool IsSet() const; + + /* + Parameters: + bVerbose - [in] + If true, the returned string begins with ON_SubDSectorId. + Returns: + "Zero", "Invalid", or "vX.fYxN" where X is VertexId(), Y is MinimumFaceId(), and N is the SectorFaceCount(). + */ + const ON_wString ToString(bool bVerbose) const; + + /* + Parameters: + s - [in] + beginning of string buffer + s_capacity + wchar_t element capacity of the string buffer + Returns: + nullptr if ther is not enough room in the buffer. + Otherwise a pointer to the null terminator of the returned string. + Remarks + The returned string will be "0" for a zero sector id, "X" for an invalid sector id, + or "vX.fYxN" where X is VertexId(), Y is MinimumFaceId(), and N is the SectorFaceCount(). + */ + wchar_t* ToString( + wchar_t* s, + size_t s_capacity + ) const; + +private: + unsigned int m_vertex_id = 0; + // minimum face id in the sector. Since a face can be in only one sector, the + // combination of m_vertex_id and m_minimum_face_id uniquely identify a sector + // in the context of a single ON_SubD. + unsigned int m_minimum_face_id = 0; + // number of faces in the sector + unsigned int m_sector_face_count = 0; +}; + +bool operator==(ON_SubDSectorId, ON_SubDSectorId); + +bool operator!=(ON_SubDSectorId, ON_SubDSectorId); + +bool operator>(ON_SubDSectorId, ON_SubDSectorId); + +bool operator<(ON_SubDSectorId, ON_SubDSectorId); + +bool operator>=(ON_SubDSectorId, ON_SubDSectorId); + +bool operator<=(ON_SubDSectorId, ON_SubDSectorId); class ON_CLASS ON_SubDVertexSurfacePointCoefficient { @@ -1634,6 +2089,8 @@ public: // Identifies a region of an ON_SubDFace ON_SubDComponentRegion m_face_region; + const ON_SubDFace* Level0Face() const; + // When the face region is a quad, m_edge_region[4] identifies regions of ON_SubDEdge elements. // When the face region is a sub-quad, these edges may be null or have null ON_SubDEdge pointers // and the ids will be zero or ON_SubDComponentRegion::IsTransientId() will be true. @@ -1643,6 +2100,21 @@ public: unsigned int m_level0_edge_count = 0; +private: + unsigned int m_reserved = 0; + +public: + /* + Returns: + If vertex_id > 0 and there is a unique element of m_vertex_id[] with the same value, + the index of that element is returned (0,1,2 or 3). + Otherwise ON_UNSET_UNINT_INDEX is returned. + */ + unsigned int CornerIndexFromVertexId( + unsigned int vertex_id + ) const; + + // If set, these are the vertice ids at the region's limit surface corners. // m_vertex_id[] is mutable because these values appear during recursive calculations. // When the face region is a sub-quad, these ids will be zero or ON_SubDComponentRegion::IsTransientId() @@ -1651,6 +2123,15 @@ public: // a persistent vertex in the ON_SubD. mutable unsigned int m_vertex_id[4] = {}; + // When a vertex is exceptional, a NURBS conversion is typically an approximation + // of the SubD around the exceptional vertex. There are a variety of post processes + // that can be applied in this case and the processes need to be applied + // sector by sector. + // Note well that when a level zero face is an N-gon with N != 4, + // the face subdivision point is an exceptional smooth vertex with valence = N. + // In this case the corresponding m_vertex_id[] value will be zero. + mutable ON_SubDSectorId m_sector_id[4]; + public: void Push(unsigned int quadrant_index); @@ -1787,10 +2268,10 @@ public: /* Returns: - A runtime serial number that is incremented every time a the active level, + A runtime serial number that is changed every time a the active level, vertex location, vertex or edge flag, or subd topology is changed. */ - ON__UINT64 ContentSerialNumber() const; + ON__UINT64 GeometryContentSerialNumber() const; /* Returns: @@ -1801,9 +2282,9 @@ public: /* Description: - Change the content serial number. - This should be done ONLY when the active level, - vertex location, vertex or edge flag, or subd topology is changed. + Change the geoemtry content serial number to indicate something affecting + the geometric shape of the subd has changed. This includes topologial changes, + vertex and edge tag changes, and changes to vertex control net locations. Parameters: bChangePreservesSymmetry - [in] When in doubt, pass false. @@ -1813,16 +2294,38 @@ public: Transformations do not preserve symmetries that are set with respect to world coordinate systems. Returns: - The new value of ConentSerialNumber(). + The new value of GeometryConentSerialNumber(). Remarks: - The value can change by any amount and core editing - functions typically take care of changing the content serial number. - A "top level" user of ON_SubD should never need to call this function. + The value can change by any amount. + Changing the geometry content serial number automatically changes + the render content serial number. */ - ON__UINT64 ChangeContentSerialNumberForExperts( + ON__UINT64 ChangeGeometryContentSerialNumberForExperts( bool bChangePreservesSymmetry ); + /* + Description: + The render content serial number changes whenever a change the might effect + rendered appearance changes. This includes both geometry changes and + changes that affect rendered appeance including changes to per face colors, + per face materials, texture coordinates, and texture mappings. + */ + ON__UINT64 RenderContentSerialNumber() const; + + /* + Description: + Change the render content serial number to indicate something affecting + only rendered appearance has changed. This includes changes to per face colors, + per face materials, texture coordinates, and texture mappings. + Remarks: + Changing the geometry content serial number automatically changes + the render content serial number. If you call ChangeGeometryContentSerialNumber(), + there is no need to also call ChangeRenderContentSerialNumber(). + */ + ON__UINT64 ChangeRenderContentSerialNumber() const; + + /* Description: Get the SubD appearance (surface or control net); @@ -1851,68 +2354,28 @@ public: ON_SubDComponentLocation::Surface or ON_SubDComponentLocation::ControlNet. */ static ON_SubDComponentLocation ToggleSubDAppearanceValue(ON_SubDComponentLocation subd_appearance); - static ON_SubDComponentLocation DefaultSubDAppearance; // = ON_SubDComponentLocation::Surface + static const ON_SubDComponentLocation DefaultSubDAppearance; // = ON_SubDComponentLocation::Surface + static const ON_SubDTextureCoordinateType DefaultTextureCoordinateType; // = ON_SubDTextureCoordinateType::Packed public: -#pragma region RH_C_SHARED_ENUM [ON_SubD::VertexTag] [Rhino.Geometry.SubDVertexTag] [byte] - /// - /// SubD::VertexTag identifies the type of subdivision vertex. Different tags use - /// different subdivision algorithms to determine where the subdivision point and - /// limit point are located. There are toplological constraints that restrict which - /// tags can be assigned. - /// - enum class VertexTag : unsigned char - { - /// - /// Not a valid vertex tag and the default value for ON_SubDVertex.m_vertex_tag. - /// This encourages developers to thoughtfully initialize ON_SubDVertex.m_vertex_tag - /// or use ON_SubD.UpdateAllTagsAndSectorCoefficients() to automatically set the - /// m_vertex_tag values at an appropriate time. - /// - Unset = 0, - - /// - /// Must be an interior vertex. - /// All edges attached to a smooth vertex must be tagged as ON_SubD::EdgeTag::Smooth - /// and must have 2 faces. - /// - Smooth = 1, - - /// - /// Can be an interior or a boundary vertex. - /// Exactly two edges ending at a crease vertex must be tagged as ON_SubD::EdgeTag::Crease and may - /// have 1 or 2 faces. - /// All other edges ending at a crease must be tagged as tagON_SubD::EdgeTag::Smooth and have 2 faces. - /// Below P = ON_SubDVertex.ControlNetPoint() and Ai = ON_SubDVertex.Edge(i)->OtherEndVertex()->ControlNetPoint(). - /// A crease vertex subdivision point is (6*P + A1 + A2)/8. - /// A crease vertex limit surface point is (4*P + A1 + A2)/6. - /// - Crease = 2, - - /// - /// Can be an interior, boundary, nonmanifold, or isolated vertex. - /// The location of a corner vertex is fixed. - /// The all subdivision points and the limit point are at the initial vertex location. - /// The edges ending at a corner vertex can be smooth or crease edges. - /// A corner vertex subdivision point is P where P = ON_SubDVertex.ControlNetPoint(). - /// A corner vertex limit surface point is P where P = ON_SubDVertex.ControlNetPoint(). - /// - Corner = 3, - - /// - /// Must be an interior vertex. - /// Every edge attached to a dart vertex must have 2 faces. - /// Exactly one edge attached to a dart vertex must be tagged as ON_SubD::EdgeTag::Crease - /// and every other attached edge must be tagged as tagON_SubD::EdgeTag::smooth. - /// - Dart = 4 - }; -#pragma endregion - - static ON_SubD::VertexTag VertexTagFromUnsigned( + static ON_SubDVertexTag VertexTagFromUnsigned( unsigned int vertex_tag_as_unsigned ); + + /* + Paramters: + vertex_tag - [in] + bVerbose - [in] + If verbose, the tag name is preceded with "ON_SubDVertexTag::". + Returns: + vertex_tag as a string. + */ + static const ON_wString VertexTagToString( + ON_SubDVertexTag vertex_tag, + bool bVertose + ); + /* Parameters: vertex_tag - [in] @@ -1921,7 +2384,7 @@ public: False otherwise. */ static bool VertexTagIsSet( - ON_SubD::VertexTag vertex_tag + ON_SubDVertexTag vertex_tag ); @@ -1932,73 +2395,35 @@ public: /// Currently this tag is not used and is invalid. /// /// FUTURE: The edge is a "soft crease" or "semi-sharp". - /// At lease one end vertex must be tagged as ON_SubD::VertexTag::Smooth + /// At lease one end vertex must be tagged as ON_SubDVertexTag::Smooth /// The edge must have exactly two faces. /// The value of ON_SubDEdge::m_sharpness controls how /// soft/hard the edge appears. - /// ON_SubDEdge::m_sharpness = 0 is identical to ON_SubD::EdgeTag::Smooth. - /// ON_SubDEdge::m_sharpness = 1 is identical to ON_SubD::EdgeTag::Crease. + /// ON_SubDEdge::m_sharpness = 0 is identical to ON_SubDEdgeTag::Smooth. + /// ON_SubDEdge::m_sharpness = 1 is identical to ON_SubDEdgeTag::Crease. /// Sharp = 3, #endif -#pragma region RH_C_SHARED_ENUM [ON_SubD::EdgeTag] [Rhino.Geometry.SubDEdgeTag] [byte] - /// - /// SubD::EdgeTag identifies the type of subdivision edge. Different tags use - /// different subdivision algorithms to calculate the subdivision point. - /// - enum class EdgeTag : unsigned char - { - /// - /// Not a valid edge tag and the default value for ON_SubDEdge.m_edge_tag. - /// This encourages developers to thoughtfully initialize ON_SubDEdge.m_edge_tag. - /// or use ON_SubD.UpdateAllTagsAndSectorCoefficients() to automatically set the - /// m_edge_tag values at an appropriate time. - /// - Unset = 0, - - /// - /// At least one the edge's vertices must be tagged as ON_SubD::VertexTag::Smooth. - /// The edge must have exactly two faces. - /// The edge's subdivision point is (A1 + A2 + S(f1) + S(f2))/4, where - /// Ai = ON_SubDEdge.Vertex(i)->ControlNetPoint() and - /// S(fi) = ON_SubDEdge.Face(i)->SubdivisionPoint(). - /// - Smooth = 1, - - /// - /// Both of the edge's vertices must be tagged as ON_SubD::VertexTag::Dart, - /// ON_SubD::VertexTag::Crease, or ON_SubD::VertexTag::Corner. - /// (The vertex tags can be different.) The edge can have any number of faces. - /// The edge's subdivision point is (A1+A2)/2 where Ai = ON_SubDEdge.Vertex(i)->ControlNetPoint(). - /// - Crease = 2, - - /// - /// This tag appears only on level 0 edges that have exactly two neighboring faces - /// and both of the edge's vertices are tagged as ON_SubD::VertexTag::Dart, - /// ON_SubD::VertexTag::Crease, or ON_SubD::VertexTag::Corner. - /// The level 1 subdivision point for a level 0 edge tagged as ON_SubD::EdgeTag::SmoothX - /// is the standard smooth edge subdivision point. - /// When subdivided, the new subdivision vertex will be tagged - /// as ON_SubD::VertexTag::Smooth and the subdivided edges will - /// be tagged as ON_SubD::EdgeTag::Smooth. - /// The tag ON_SubD::EdgeTag::SmoothX can only appear on a level 0 edge. - /// This tag exists because the ON_SubD subdivision - /// algorithm requires any edge with both end vertices - /// tagged as not smooth must be subdivided at its midpoint. - /// Sector iterators treat "SmoothX" edges as smooth. - /// Both edge m_sector_coefficient[] values must be set so the smooth subdivided edges will be valid. - /// - SmoothX = 4 - }; -#pragma endregion - - static ON_SubD::EdgeTag EdgeTagFromUnsigned( + static ON_SubDEdgeTag EdgeTagFromUnsigned( unsigned int edge_tag_as_unsigned ); + /* + Paramters: + edge_tag - [in] + bVerbose - [in] + If verbose, the tag name is preceded with "ON_SubDEdgeTag::". + Returns: + edge_tag as a string. + */ + static const ON_wString EdgeTagToString( + ON_SubDEdgeTag edge_tag, + bool bVertose + ); + + /* Parameters: edge_tag - [in] @@ -2007,7 +2432,7 @@ public: False otherwise. */ static bool EdgeTagIsSet( - ON_SubD::EdgeTag edge_tag + ON_SubDEdgeTag edge_tag ); @@ -2311,12 +2736,12 @@ public: true if sector_edge_count is valid for the vertex type */ static bool IsValidSectorEdgeCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_edge_count ); static bool IsValidSectorFaceCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_face_count ); @@ -2392,6 +2817,10 @@ public: + + + + ON_SubD() ON_NOEXCEPT; virtual ~ON_SubD(); @@ -2560,6 +2989,10 @@ public: bool HasBrepForm() const override; //virtual + /* + Returns: + GetSurfaceBrep( ON_SubDToBrepParameters::Default, nullptr ); + */ ON_Brep* BrepForm( ON_Brep* brep = nullptr ) const override; @@ -2674,7 +3107,7 @@ public: The bottom quad is specified by the first 4 points and the top quad specified by the last 4 points. edge_tag - [in] - If edge_tag = ON_SubD::EdgeTag::Crease, then the box will have + If edge_tag = ON_SubDEdgeTag::Crease, then the box will have creases and corners. Otherwise the box will be smooth. facecount_x - [in] Number of faces in x direction facecount_y - [in] Number of faces in y direction @@ -2687,7 +3120,7 @@ public: */ static ON_SubD* CreateSubDBox( const ON_3dPoint corners[8], - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, unsigned int facecount_x, unsigned int facecount_y, unsigned int facecount_z, @@ -3226,6 +3659,45 @@ public: */ bool HasPerFaceColors() const; + /* + Description: + If a face has a nonzero PackId(), then its per face color is set to ON_Color::RandomColor(f->PackId()). + Otherwise, its per face color is cleared. + */ + void SetPerFaceColorsFromPackId() const; + + + ///* + //Description: + // The ON__INT_PTRs in the tree are const ON_SubDMeshFragment* pointers. + // The bounding boxes are from the surface points. + //*/ + //ON_RTreeRef FragmentTree() const; + + ///* + //Description: + // If the tree is not needed and memory resources are tight, then call ClearTree() + // to remove the RTree. + //*/ + //void ClearFragmentTree(); + + ///* + //Description: + // The ON__INT_PTRs in the tree are const ON_SubDComponentPtrs. + // The bounding boxs are based on the subd + //*/ + //ON_RTreeRef ControlNetComponentTree( + // bool bIncludeVertices, + // bool bIncludeEdges, + // bool bIncludeFaces + // ) const; + + ///* + //Description: + // If the tree is not needed and memory resources are tight, then call ClearTree() + // to remove the RTree. + //*/ + //void ClearControlNetComponentTree(); ///////////////////////////////////////////////////////// // @@ -3476,7 +3948,7 @@ public: Parameters: bUnsetValuesOnly - [in] If true, the update is restricted to vertices tagged as - ON_SubD::VertexTag::Unset and edges tagged as ON_SubD::EdgeTag::Unset. + ON_SubDVertexTag::Unset and edges tagged as ON_SubDEdgeTag::Unset. Returns: Number of vertices and edges that were changed during the update. @@ -3554,7 +4026,7 @@ public: */ unsigned int UpdateEdgeSectorCoefficients( bool bUnsetSectorCoefficientsOnly - ); + ) const; /* @@ -3808,7 +4280,7 @@ public: const ON_SubDComponentPtr* cptr_list, size_t cptr_count, ON_SubDComponentLocation component_location - ); + ); /* Description: @@ -3867,7 +4339,7 @@ public: vertex_tag - [in] Desired tag. If a vertex has the desired tag or cannot accept the desired tag, then that vertex is skipped. - If vertex_tag is ON_SubD::VertexTag::Corner, then every edge touching + If vertex_tag is ON_SubDVertexTag::Corner, then every edge touching that vertex is converted to a crease. Returns: number of vertex tags that were changed. @@ -3875,7 +4347,7 @@ public: unsigned int SetVertexTags( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, - ON_SubD::VertexTag vertex_tag + ON_SubDVertexTag vertex_tag ); /* @@ -3888,7 +4360,7 @@ public: vertex_tag - [in] Desired tag. If a vertex has the desired tag or cannot accept the desired tag, then that vertex is skipped. - If vertex_tag is ON_SubD::VertexTag::Corner, then every edge touching + If vertex_tag is ON_SubDVertexTag::Corner, then every edge touching that vertex is converted to a crease. Returns: number of vertex tags that were changed. @@ -3896,7 +4368,7 @@ public: unsigned int SetVertexTags( const ON_SubDComponentPtr* cptr_list, size_t cptr_count, - ON_SubD::VertexTag vertex_tag + ON_SubDVertexTag vertex_tag ); /* @@ -3906,7 +4378,7 @@ public: unsigned int SetEdgeTags( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, - ON_SubD::EdgeTag edge_tag + ON_SubDEdgeTag edge_tag ); /* @@ -3916,7 +4388,7 @@ public: unsigned int SetEdgeTags( const ON_SubDComponentPtr* cptr_list, size_t cptr_count, - ON_SubD::EdgeTag edge_tag + ON_SubDEdgeTag edge_tag ); @@ -3973,7 +4445,7 @@ public: /* Description: - Adds a vertex with tag = ON_SubD::VertexTag::Unset. + Adds a vertex with tag = ON_SubDVertexTag::Unset. */ class ON_SubDVertex* AddVertex( const double* P @@ -3984,7 +4456,7 @@ public: Adds a vertex with specified tag. */ class ON_SubDVertex* AddVertex( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, const double* P ); @@ -3998,7 +4470,7 @@ public: the returned value with have id = candidate_vertex_id. Otherwise a new id will be assigned. vertex_tag - [in] - Pass ON_SubD::VertexTag::Unset if not known. + Pass ON_SubDVertexTag::Unset if not known. P - [in] nullptr or a 3d point. initial_edge_capacity - [in] @@ -4008,7 +4480,7 @@ public: */ class ON_SubDVertex* AddVertexForExperts( unsigned int candidate_vertex_id, - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, const double* P, unsigned int initial_edge_capacity, unsigned int initial_face_capacity @@ -4063,27 +4535,27 @@ public: v1 - [in] ending vertex Returns: - If edge_face_count > 0x7FFFU, then ON_SubD::EdgeTag::Unset is returned. + If edge_face_count > 0x7FFFU, then ON_SubDEdgeTag::Unset is returned. - If edge_face_count is 1 or >= 3, then ON_SubD::EdgeTag::Crease is returned. + If edge_face_count is 1 or >= 3, then ON_SubDEdgeTag::Crease is returned. - If both vertex tags are ON_SubD::VertexTag::Smooth, then ON_SubD::EdgeTag::Smooth is returned. + If both vertex tags are ON_SubDVertexTag::Smooth, then ON_SubDEdgeTag::Smooth is returned. - If edge_face_count is 1 and both vertex tags are ON_SubD::VertexTag::Crease or ON_SubD::VertexTag::Corner, - then ON_SubD::EdgeTag::Crease is returned. + If edge_face_count is 1 and both vertex tags are ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner, + then ON_SubDEdgeTag::Crease is returned. - If edge_face_count is 2 and both vertex tags are set and both are not ON_SubD::VertexTag::Smooth, - then ON_SubD::EdgeTag::SmoothX is returned. + If edge_face_count is 2 and both vertex tags are set and both are not ON_SubDVertexTag::Smooth, + then ON_SubDEdgeTag::SmoothX is returned. - Otherwise, ON_SubD::EdgeTag::Unset is returned. + Otherwise, ON_SubDEdgeTag::Unset is returned. */ - static ON_SubD::EdgeTag EdgeTagFromContext( + static ON_SubDEdgeTag EdgeTagFromContext( unsigned int edge_face_count, - const ON_SubD::VertexTag v0_tag, - const ON_SubD::VertexTag v1_tag + const ON_SubDVertexTag v0_tag, + const ON_SubDVertexTag v1_tag ); - static ON_SubD::EdgeTag EdgeTagFromContext( + static ON_SubDEdgeTag EdgeTagFromContext( unsigned int edge_face_count, const ON_SubDVertex* v0, const ON_SubDVertex* v1 @@ -4091,7 +4563,7 @@ public: /* Description: - Add an edge with tag = ON_SubD::EdgeTag::Unset to the subd. + Add an edge with tag = ON_SubDEdgeTag::Unset to the subd. Parameters: v0 - [in] v1 - [in] @@ -4146,14 +4618,14 @@ public: Add an edge to the subd. Parameters: edge_tag - [in] - ON_SubD::EdgeTag::Unset + ON_SubDEdgeTag::Unset Edge tag is not known at this time. - ON_SubD::EdgeTag::Smooth + ON_SubDEdgeTag::Smooth Smooth edge. If both vertices are tagged as not smooth, the - tag on the returned edge will be ON_SubD::EdgeTag::SmoothX. This - tag is changed to ON_SubD::EdgeTag::Smooth on the first + tag on the returned edge will be ON_SubDEdgeTag::SmoothX. This + tag is changed to ON_SubDEdgeTag::Smooth on the first subdivision step. - ON_SubD::EdgeTag::Crease. + ON_SubDEdgeTag::Crease. Crease edge. Both vertices must be tagged as not smooth. v0 - [in] v1 - [in] @@ -4166,7 +4638,7 @@ public: tag values in simple situations. */ class ON_SubDEdge* AddEdge( - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, class ON_SubDVertex* v0, class ON_SubDVertex* v1 ); @@ -4187,7 +4659,7 @@ public: Pass ON_SubDSectorType::UnsetSectorCoefficient if unknown. */ class ON_SubDEdge* AddEdgeWithSectorCoefficients( - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, class ON_SubDVertex* v0, double v0_sector_coefficient, class ON_SubDVertex* v1, @@ -4204,7 +4676,7 @@ public: the returned edge with have id = candidate_edge_id. Otherwise a new id will be assigned. edge_tag - [in] - Pass ON_SubD::EdgeTag::Unset if not known. + Pass ON_SubDEdgeTag::Unset if not known. v0 - [in] The edge begins at v0 and ends at v1. v0_sector_coefficient - [in] @@ -4218,7 +4690,7 @@ public: */ class ON_SubDEdge* AddEdgeForExperts( unsigned int candidate_edge_id, - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, class ON_SubDVertex* v0, double v0_sector_coefficient, class ON_SubDVertex* v1, @@ -4402,6 +4874,40 @@ public: unsigned int edge_count ); + /* + Description: + Add texture points to a face. + Parameters: + texture_points - [in] + An array of face->EdgeCount() points. + texture_points_count - [in] + number of elements in texture_points[]. + Must be >= face->EdgeCount(). + Returns: + True if texture points were set. + Remarks: + This function automatically handles the management of face texture point storage. + Texture points are a mutable property on ON_SubDFace. + */ + bool AddFaceTexturePoints( + const class ON_SubDFace* face, + const class ON_3dPoint* texture_points, + size_t texture_points_count + ) const; + + /* + Description: + Add texture point storage capacity to a face. + Parameters: + face - [in] + The ability to store at least face->EdgeCount() texture points will be added to this face. + Returns: + Number of texture points that can be set (>= face->EdgeCount()). + */ + unsigned int AllocateFaceTexturePoints( + const class ON_SubDFace* face + ) const; + public: #pragma region RH_C_SHARED_ENUM [ON_SubD::PatchStyle] [Rhino.Geometry.SubDPatchStyle] [byte] @@ -4600,34 +5106,48 @@ public: */ void ClearEvaluationCache() const; - /* Description: - Clear all cached evaluation information (meshes, surface points, boundiang boxes, ...) - that depends on the vertex's control point location or tag. - Parameter: - vertex - [in] + This function copies cached evaluations of component subdivision points and limit + surface information from src to this. Typically this is done for performance critical + sitations like control point editing. */ - void ClearNeighborhoodEvaluationCache( - const ON_SubDVertex* vertex, - bool bTagChanged - ) const; + bool CopyEvaluationCacheForExperts(const ON_SubD& src); /* Description: - Get a mesh of the subdivision control net. + Get a mesh representation of the ON_SubD control net. Parameters: - level_index - [in] (>=0) mesh - [in] If not null, the returned mesh will be stored on the input class. + + priority - [in] + Specifies what type of SubD information is most important to transfer to the mesh. + For more details, see the comments for ON_SubDGetControlNetMeshPriority. + Returns: - The subdivision level as a mesh. + A mesh representation of the ON_SubD control net. */ class ON_Mesh* GetControlNetMesh( - class ON_Mesh* mesh - ) const; + class ON_Mesh* mesh, + ON_SubDGetControlNetMeshPriority priority + ) const; + +private: + bool Internal_GetGeometryControlNetMesh( + const class ON_SubDLevel& level, + class ON_SubDLevelComponentIdIterator& vit_by_id, + class ON_SubDLevelComponentIdIterator& fit_by_id, + class ON_Mesh& mesh + ) const; + bool Internal_GetTextureCoordinatesGeometryControlNetMesh( + const class ON_SubDLevel& level, + class ON_Mesh& mesh + ) const; +public: + @@ -4648,15 +5168,15 @@ public: Unset = 0, /// - /// A single NURBS surface will be created for each SubD quad. Near extraordinary vertices, the surfaces may - /// have lots of knots. + /// Onee NURBS surface will be generated for each SubD quad. + /// N NURBS surfaces will be generated for each SubD N-gon (N = 3, 5 or more). ON_Brepface may cover multiple + /// Near extraordinary vertices, the surfaces may have lots of knots. /// Large = 1, /// /// NURBS surfaces will be as large as possible without the addition of extra knots. - /// Near extraordinary vertices, the surfaces may - /// have lots of knots. + /// Near extraordinary vertices, the surfaces may have lots of knots. /// This option is prefered when a user wants larger NURBS surfaces but not at the cost of addtional NURBS control points. /// Medium = 2, @@ -4675,6 +5195,7 @@ public: }; #pragma endregion + @@ -4702,132 +5223,154 @@ public: double image_height ); - static unsigned int TextureImageSuggestedMinimumSize( - ON_2udex grid_size + enum : unsigned int + { + /// + /// ON_SUbDFace packing rectangle information is calculated so that there is at least + /// one unused pixel between adjacent packing rectangles when a texture image size is + /// TextureImageSuggestedMinimumSize x TextureImageSuggestedMinimumSize + /// pixels or larger. + /// Core subd code assumes TextureImageSuggestedMinimumSize is a power of 2 and >= 512. + /// + TextureImageSuggestedMinimumSize = 1024 + }; + + static ON_SubDTextureCoordinateType TextureCoordinateTypeFromUnsigned( + unsigned int texture_coordinate_type_as_unsigned ); - /* - Returns: - Suggesting minimum number of pixels for a texture image width and height for this SubD. - */ - unsigned int TextureImageSuggestedMinimumSize() const; + static ON_SubDTextureCoordinateType TextureCoordinateTypeFromObsoleteTextureDomainType( + unsigned int obsolete_texture_domain_type_as_unsigned + ); - /* - Parameters: - normalize_texture_domain_delta - [in] - > 0.0 and <= 1.0 - count - [in] - >= 1 number of rectangles - image_width - [in] - image_height = [in] - If a texture image size is known, pass it here. Otherwise pass 0.0 for both parameters. - When available, it is used to avoid have different rectangles share pixels. - quad_texture_domain - [out] - Each quad will have texture domain - minimum point = (x0,y0) - maximum point = (x0+quad_texture_domain,y0+quad_texture_domain) - where (x0,y0) is the normalized coord - quad_texture_delta - [out] - To move from one quad to the next in the same row, increment the x-coordinate by quad_texture_delta. - To move from the last quad in a row to the first quad in the next row, - set x-coordinate = to the next in the same row, increment the x-coordinate by quad_texture_delta. - - Returns: - number of quads per row and column in the region. - */ - static const ON_2udex GetTextureDomainAndDelta( - unsigned minimum_rectangle_count, - double image_width, - double image_height, - ON_2dVector& quad_texture_domain, - ON_2dVector& quad_texture_delta + static unsigned char ObsoleteTextureDomainTypeFromTextureCoordinateType( + ON_SubDTextureCoordinateType texture_coordinate_type ); - static ON_SubDTextureDomainType TextureDomainTypeFromUnsigned( - unsigned int texture_domain_type_as_unsigned - ); - - static const ON_wString TextureDomainTypeToString( - ON_SubDTextureDomainType texture_domain_type + static const ON_wString TextureCoordinateTypeToString( + ON_SubDTextureCoordinateType texture_domain_type ); - /* Description: - Set the default texture coordinate domain on each face. + Set the texture coordinate type. Parameters: - texture_domain_type - [in] + texture_coordinate_type - [in] Type of texture coordinates. - If ON_SubDTextureDomainType::Unset or ON_SubDTextureDomainType::Custom, - is passed, the type setting is changed but no changes are made to texture coordinats. - When in doubt and performance may be an issue, pass false. - If true, then, where possible, texture domains are set so - quad grid regions have neighboring texture domains. - On subds with large regions quad grids, this produces a result that - looks better when default surface parameter texture coordinates are used. - However, this requires a calculation that can be slow on subs with lots of faces. - bLazy - [in] - If true and if texture_domain_type == TextureDomainType(), - then nothing is done and true is returned. - bSetFragmentTextureCoordinates - When in doubt, pass true. - If true and if the faces have cached fragments, then after the domains are set the - fragments texture domains and their texture coordinates are set as well. + If ON_SubDTextureCoordinateType::Unset or ON_SubDTextureCoordinateType::Custom, + is passed, the type setting is changed but no changes are made to texture coordinates. Remarks: - SubD texture domains and coordinates are a mutable property. - They can be changed by rendering applications as needed. Call SetTextureCoordinates() to restore them to the default values. + + If texture_coordinate_type is ON_SubDTextureCoordinateType::FromMapping, then + the mapping this->TextureCoordinateMapping() is used. You may call + this->SetTextureCoordinateMapping() to set the mapping. + + Calling this->SetTextureCoordinateType() does not change existing cached + texture coordinates. At an approprite time, call SetFragmentTextureCoordinates() + to update texture coordinates on any cached fragments. + + SubD texture coordinate type and fragment texture coordinates are a mutable property. + They can be changed by rendering applications as needed. */ - bool SetTextureDomains( - ON_SubDTextureDomainType texture_domain_type, - bool bLazy, - bool bSetFragmentTextureCoordinates + void SetTextureCoordinateType( + ON_SubDTextureCoordinateType texture_coordinate_type ) const; - ON_SubDTextureDomainType TextureDomainType() const; + ON_SubDTextureCoordinateType TextureCoordinateType() const; - static bool SetTextureDomains( - ON_SubDFaceIterator& fit, - ON_SubDTextureDomainType texture_domain_type, - bool bSetFragmentTextureCoordinates - ); + /* + Returns: + Number of faces with ON_SubDFace.TexturePointsAreSet() = true. + */ + unsigned int TexturePointsAreSet() const; /* Description: - Use the current default texture domain values on each face - to set the currently cached ON_MeshFragment texture coordinates. - The subd's TextureMappingTag() property is set to - ON_TextureMapping::SurfaceParameterTextureMapping. + Delete texture points from faces. + Returns: + Number of faces that had texture points. + */ + unsigned int ClearTexturePoints() const; + + /* + Parameters: + bIgnoreTextureCoordinateType - [in] + If true, the current texture mapping tag is returned. + If false, the current texture mapping tag is returned only when ON_SubDTextureCoordinateType::FromMapping = TextureCoordinateType(). + Returns: + The current texture mapping tag. Remarks: + The texture mapping tag should be applied only when this->TextureCoordinateType() + is ON_SubDTextureCoordinateType::FromMapping. SubD texture domains and coordinates are a mutable property. They can be changed by rendering applications as needed. - Call SetTextureCoordinatesFromFaceDomains() to restore them to the default values. */ - bool SetTextureCoordinatesFromFaceDomains() const; + const ON_MappingTag TextureMappingTag( + bool bIgnoreTextureCoordinateType + ) const; /* Description: - Use the current default texture domain values on each face - to set the currently cached ON_MeshFragment texture coordinates. - Parameters: - fit - [in] - faces to set. + Set the texture mapping tag. + Remarks: + The texture mapping tag should be applied only when this->TextureCoordinateType() + is ON_SubDTextureCoordinateType::FromMapping. + + Calling this->SetTextureMappingTag() does not change existing cached + texture coordinates. At an approprite time, call this->SetFragmentTextureCoordinates() + to update texture coordinates on any cached fragments. + + SubD texture domains and coordinates are a mutable property. + They can be changed by rendering applications as needed. */ - static bool SetTextureCoordinatesFromFaceDomains( - ON_SubDFaceIterator& fit + void SetTextureMappingTag(const class ON_MappingTag&) const; + + /* + Returns: + True if setting texture coordinates requires a set ON_TextureMapping mapping. + Remarks: + An explict texture mapping is required when TextureCoordinateType() is + ON_SubDTextureCoordinateType::FromMapping and TextureMappingTag() + is set and not ON_MappingTag::SurfaceParameterMapping or an equivalent + surface parameter mapping. + */ + bool TextureMappingRequired() const; + + /* + Parameters: + texture_coordinate_type - [in] + texture_mapping_tag - [in] + If texture_coordinate_type is ON_SubDTextureCoordinateType::Custom, then + then texture_mapping_tag information is included in the hash. + Otherwise, texture_mapping_tag is ignored. + Returns: + A hash that uniquely identifies the information in TextureCoordinateType() and + TextureMappingTag() that applies to the current subd. + */ + static const ON_SHA1_Hash TextureSettingsHash( + ON_SubDTextureCoordinateType texture_coordinate_type, + const class ON_MappingTag& texture_mapping_tag ); /* + Returns: + ON_SubD::TextureSettingsHash(this->TextureCoordinateType(),this->TextureMappingTag()); + Remarks: + Comparing with this->FragmentTextureCoordinatesTextureSettingsHash() can tell you + if the current fragment texture coordinates were calculated using the same settings. + */ + const ON_SHA1_Hash TextureSettingsHash() const; + + /* Description: - Use a texture mapping function to set currently cached - ON_SubDMeshFragment m_T[] values. + If needed, set the frament texture coordinates. Parameters: mapping - [in] - subd_xform - [in] - If not nullptr, the mapping calculation is performed as - if the subd were transformed by subd_xform; the - location of the subd is not changed. + If ON_SubD::TextureMappingRequired() is true, then you must + pass a mapping with a tag that matches ON_SubDTextureMappingTag(). + Otherwise, mapping is ignored and you may pass ON_TextureMapping::Unset. bLazy - [in] If true and the m_T[] values were set using the same mapping parameters, then no calculation is performed. @@ -4838,24 +5381,129 @@ public: They can be changed by rendering applications as needed. Call SetTextureCoordinatesFromFaceDomains() to restore them to the default values. */ - bool SetTextureCoordinates( + bool SetFragmentTextureCoordinates( const class ON_TextureMapping& mapping, - const class ON_Xform* subd_xform, bool bLazy ) const; - + /* Returns: - The current texture mapping tag. This tag is set by SetTextureCoordinatesFromFaceDomains() - and SetTextureCoordinates(). - Remarks: - SubD texture domains and coordinates are a mutable property. - They can be changed by rendering applications as needed. - Call SetTextureCoordinatesFromFaceDomains() to restore them to the default values. + The value of ON_SubD::TextureSettingsHash(texture_coordinate_type,texture_mapping_tag) + for the texture_coordinate_type and texture_mapping_tag used to set the current + fragment texture coordinates. If no fragments exist or the coordinates are not set, + then ON_SHA1_Hash::EmptyContentHash is returned. */ - const ON_MappingTag TextureMappingTag() const; + const ON_SHA1_Hash FragmentTextureCoordinatesTextureSettingsHash() const; - void SetTextureMappingTag(const class ON_MappingTag&) const; + /* + Description: + If a change requires existing fragment texture coordinates to be recalculated, + then call ClearFragmentTextureCoordinatesTextureSettingsHash(). + */ + void ClearFragmentTextureCoordinatesTextureSettingsHash() const; + +private: + /* + Description: + Unconditionally sets fragment texture coordinates when a mapping is not required or is not available. + */ + bool Internal_SetFragmentTextureCoordinatesWithoutMapping() const; + + /* + Description: + Sets the value returned by FragmentTextureCoordinatesTextureSettingsHash() + */ + void Internal_SetFragmentTextureCoordinatesTextureSettingsHash(ON_SHA1_Hash hash) const; + + ON_SubDTextureCoordinateType Internal_BestChoiceTextureCoordinateType( + const class ON_TextureMapping& available_mapping + ) const; + +public: + /* + Description: + Use a callback to set the vertex colros in m_C[]. + Parameters: + bLazySet - [in] + If bLazySet is true and fragment_colors_settings_hash and the current + FragmentColorsSettingsHash() are equal, then nothing is changed. + fragment_colors_settings_hash - [in] + A that uniquely identifies the method and parameters being + used to set the fragment vertex colors. In general this hash + should depend on the value of this->GeometryContentSerialNumber(), + color_callback, and all values in the callback_context that + determine vertex colors. Under no circumstances should this + hash depend on this->RenderContentSerialNumber(). + fragment_colors_mapping_tag - [in] + If not applicable, pass ON_MappingTag::Unset. + A mapping tag indentifying what is setting the fragment colors. + This is the only property that persists in SubD copies and saves in 3dm archives. + Typically: + m_mapping_id is an id you make up that identifies what is setting the colors (thickness, curvature, ...). + m_mapping_type will be ON_TextureMapping::TYPE::false_colors. + m_mapping_crc is a field from the 1990s that the SHA1 hash handles better now and setting + m_mapping_crc = ON_CRC32(0, sizeof(fragment_colors_settings_hash), &fragment_colors_settings_hash) + works well. + works well. + Typically, m_mapping_type = TYPE::false_colors. + callback_context - [in] + first parameter passed to color_callback() + color_callback - [i] + A callback function used to set the fragment vertex colors. + */ + bool SetFragmentColorsFromCallback( + bool bLazySet, + ON_SHA1_Hash fragment_colors_settings_hash, + ON_MappingTag fragment_colors_mapping_tag, + ON__UINT_PTR callback_context, + const ON_Color(*color_callback)( + ON__UINT_PTR callback_context, + const ON_MappingTag& mapping_tag, + const ON_SubD& subd, + ON_SubDComponentPtr cptr, + const ON_3dPoint& P, + const ON_3dVector& N, + const ON_3dPoint& T, + const ON_SurfaceCurvature& K + ) + ) const; + + /* + Descrption: + Clear all fragment vertex colors + Parameters: + bClearFragmentColorsMappingTag - [in] + When in doubt, pass true. + If true, the mapping tag associated with the fragment vertex colors is unset as well. + */ + void ClearFragmentColors( + bool bClearFragmentColorsMappingTag + ); + + /* + Returns: + hash identifying the way the fragment vertex colors were set. + */ + const ON_SHA1_Hash FragmentColorsSettingsHash() const; + + /* + Returns: + The current fragment vertex colors mapping tag. + */ + const ON_MappingTag FragmentColorsMappingTag() const; + + /* + Description: + Set the fragment colors mapping tag. + Remarks: + Calling this->SetFragmentColorsMappingTag() does not change existing cached + fragment vertex colors. At an approprite time, call this->SetFragmentColorsFromCallback() + to update fragment vertex colors on any cached fragments. + + SubD fragment vertex tag and colors are a mutable property. + They can be changed by rendering applications as needed. + */ + void SetFragmentColorsMappingTag(const class ON_MappingTag&) const; public: /* @@ -4887,6 +5535,13 @@ private: void CopyHelper(const ON_SubD&); + public: + /* + Returns: + True if every face has a nonzero PackId and a set PackRect. + */ + bool FacesArePacked() const; + private: friend class ON_SubDRef; @@ -5114,9 +5769,16 @@ public: /* Returns: SubD content serial number when this list was created or the last - time UpdateContentSerialNumber() was run. + time UpdateContentSerialNumbers() was run. */ - ON__UINT64 SubDContentSerialNumber() const; + ON__UINT64 SubDGeometryContentSerialNumber() const; + + /* + Returns: + SubD content serial number when this list was created or the last + time UpdateContentSerialNumbers() was run. + */ + ON__UINT64 SubDRenderContentSerialNumber() const; unsigned int Count() const; @@ -5137,11 +5799,10 @@ public: /* Description: - Update the saved subd content serial number to the current value of SubD().ContentSerialNumber(). - Returns: - Updated value of subd content serial number. + Update the saved subd geometry and render content serial number to the current values + of SubD().GeometryContentSerialNumber() and SubD().RenderContentSerialNumber(). */ - ON__UINT64 UpdateContentSerialNumber(); + void UpdateContentSerialNumbers(); /* Description: @@ -5177,7 +5838,8 @@ private: private: ON__UINT64 m_subd_runtime_serial_number = 0; - ON__UINT64 m_subd_content_serial_number = 0; + ON__UINT64 m_subd_geometry_content_serial_number = 0; + ON__UINT64 m_subd_render_content_serial_number = 0; unsigned m_subd_vertex_count = 0; unsigned m_subd_edge_count = 0; @@ -5352,7 +6014,7 @@ public: unsigned int FacetEdgeCount() const; - ON_SubD::VertexTag VertexTag() const; + ON_SubDVertexTag VertexTag() const; unsigned int EdgeCount() const; @@ -5375,7 +6037,7 @@ public: /* Returns: - If the sector vertex tag is ON_SubD::VertexTag::Corner, + If the sector vertex tag is ON_SubDVertexTag::Corner, the angle between the corner crease boundary edges is returned. Otherwise, ON_SubDSectorType::ErrorCornerSectorAngle is returned. @@ -5478,9 +6140,10 @@ public: sector_boundary_edge1_ptr - [in] Crease edges that bound the sector containing the smooth edge. The edge direction must identify the corner vertex. + corner vertex = sector_boundary_edge0_ptr.RelativeVertex(0) = sector_boundary_edge1_ptr.RelativeVertex(0) Returns: tagged end angle for a smooth edge that - 1) ends at a vertex tagged on ON_SubD::VertexTag::Corner + 1) ends at a vertex tagged on ON_SubDVertexTag::Corner 2) has two adjacent faces. 3) lies in a sector bounded by 2 distinct crease edges. */ @@ -5509,31 +6172,31 @@ public: (valence + 1) for tri subds */ static unsigned int SectorPointRingCountFromEdgeCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_edge_count ); static unsigned int SectorPointRingCountFromFaceCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_face_count ); static unsigned int SectorFaceCountFromEdgeCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_edge_count ); static unsigned int SectorEdgeCountFromFaceCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_face_count ); static unsigned int MinimumSectorEdgeCount( - ON_SubD::VertexTag vertex_tag + ON_SubDVertexTag vertex_tag ); static unsigned int MinimumSectorFaceCount( - ON_SubD::VertexTag vertex_tag + ON_SubDVertexTag vertex_tag ); public: @@ -5639,17 +6302,17 @@ public: sector_face_count - [in] Number of faces in the sector. corner_sector_angle_radians - [in] - If vertex_tag is ON_SubD::VertexTag::Corner, this + If vertex_tag is ON_SubDVertexTag::Corner, this parameter is the angle between the crease edges that bound the corner. - If vertex_tag is not ON_SubD::VertexTag::Corner, + If vertex_tag is not ON_SubDVertexTag::Corner, this parameter is ignored. Returns: An ON_SubDSectorType for the case the input parameters identify. */ static ON_SubDSectorType Create( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_face_count, double corner_sector_angle_radians ); @@ -6006,7 +6669,7 @@ public: //// 0: Eigenvalues for this case are not known. ////*/ ////static unsigned int AllEigenvaluesAvailableKnown( - //// ON_SubD::VertexTag vertex_tag, + //// ON_SubDVertexTag vertex_tag, //// unsigned int sector_edge_count //// ); @@ -6024,7 +6687,7 @@ public: are 2*radius from the origin. sector_angle_radians - [in] If radius > 0, - this->VertexTag() is ON_SubD::VertexTag::Crease, + this->VertexTag() is ON_SubDVertexTag::Crease, crease_sector_angle_radians > 0.0 and crease_sector_angle_radians < 2.0*ON_PI, then this will be the angle between the crease boundary edges. @@ -6043,7 +6706,7 @@ public: ) const; private: - ON_SubD::VertexTag m_vertex_tag = ON_SubD::VertexTag::Unset; + ON_SubDVertexTag m_vertex_tag = ON_SubDVertexTag::Unset; unsigned char m_reserved1 = 0; unsigned short m_reserved2 = 0; unsigned int m_hash = 0; // SetHash() sets this field, SectorTypeHash() returns its value. @@ -6784,7 +7447,7 @@ public: ) const; bool TextureCoordinatesExist() const; - void SetTextureCoordinatesExist(bool TextureCoordinatesExist) const; + void SetTextureCoordinatesExist(bool bTextureCoordinatesExist) const; /* Parameters: @@ -6804,7 +7467,7 @@ public: /* Parameters: grid_corner_index - [in] - grid side N is between corner index N and corner index (N+1)%4. + 0, 1, 2, or 3 Remarks: For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. @@ -6818,7 +7481,7 @@ public: /* Parameters: grid_corner_index - [in] - grid side N is between corner index N and corner index (N+1)%4. + 0, 1, 2, or 3 Remarks: For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. @@ -6833,7 +7496,7 @@ public: /* Parameters: grid_corner_index - [in] - grid side N is between corner index N and corner index (N+1)%4. + 0, 1, 2, or 3 Returns: Limit surface location at the grid corner or ON_3dPoint::NanPoint if the fragment is empty. Remarks: @@ -6849,7 +7512,7 @@ public: /* Parameters: grid_corner_index - [in] - grid side N is between corner index N and corner index (N+1)%4. + 0, 1, 2, or 3 Returns: Limit surface normal at the grid corner or ON_3dPoint::NanPoint if the fragment is empty. Remarks: @@ -6878,6 +7541,39 @@ public: unsigned int grid_corner_index ) const; + /* + Parameters: + grid_corner_index - [in] + 0, 1, 2, or 3 + Returns: + vertex color at the grid corner or ON_Color::UnsetColor if thre are not vertex colors. + Remarks: + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only + corner that corresponds to a SubD vertex. + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 + correspond to half of original SuD edges. + */ + const ON_Color CornerColor( + unsigned int grid_corner_index + ) const; + + /* + Parameters: + grid_corner_index - [in] + 0, 1, 2, or 3 + Returns: + vertex surface curvature at the grid corner or ON_SurfaceCurvature::Nan if there are not vertex curvatures. + Remarks: + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only + corner that corresponds to a SubD vertex. + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 + correspond to half of original SuD edges. + */ + const ON_SurfaceCurvature CornerCurvature( + unsigned int grid_corner_index + ) const; + + /* Parameters: grid_side_index - [in] @@ -6945,6 +7641,8 @@ public: const ON_Plane CenterFrame() const; + const ON_3dPoint CenterTextureCoordinate() const; + private: bool Internal_GetFrameHelper( unsigned int P_dex, @@ -6978,7 +7676,10 @@ public: public: enum : unsigned { - MaximumVertexCount = 0x3FFF + /// + /// 64x64 grid of points + /// + MaximumVertexCount = 0x1000 }; /* @@ -7048,33 +7749,298 @@ public: private: friend class ON_SubDManagedMeshFragment; friend class ON_SubDMeshImpl; + friend class ON_SubDHeap; + // Number of grid vertices and capacity of arrays in certain conditions. + enum + { + /// + /// 3 double for the vertex point + /// 3 double for the vertex normal + /// 3 double for the vertex texture coordinate + /// 1 double (4 bytes used for vertex color and 4 bytes currently not used) + /// + ManagedDoublesPerVertex = 10, + + /// + /// Number of doubles between successive 3d points in m_P[]/m_N[]/m_T[]. + /// When managed arrays are not interlaced, this value must be 3. + /// When managed arraays are interlaced, this value must be ManagedDoublesPerPoint. + /// + Managed_3d_stride = 3, + + /// + /// Number of ON_Color elements between successive colors in m_C[]. + /// When managed arrays are not interlaced, this value must be 1. + /// When managed arraays are interlaced, this value must be ManagedDoublesPerPoint*sizeof(double)/sizeof(ON_Color). + /// + Managed_color_stride = 1, + }; + + static bool Internal_ManagedArraysAreInterlaced(); + static size_t Internal_Managed3dArrayOffset(size_t vertex_capacity); + + /* + Parameters: + PNTC_array[] - [in] + An array of ManagedDoublesPerVertex*vertex_capacity doubles. + */ + void Internal_LayoutArrays( + bool bManagedArray, + double* PNTC_array, + size_t vertex_capacity + ); + enum : unsigned short { - ValueMask = 0x3FFF, // maximum vertex count and capacity - EtcMask = 0xC000, - EtcControlNetQuadBit = 0x8000, // bit is on m_vertex_count_etc. Set means 4 m_ctrlnetP[] points are set. - EtcTextureCoordinatesExistBit = 0x4000, // bit is on m_vertex_count_etc - EtcManagedArraysBit = 0x8000, // bit is on m_vertex_capacity_etc. Set means this class manages the m_P[], m_N[], and m_T[] arrays. + /// + /// Vertex count = (m_vertex_count_etc & EtcMask) + /// Vertex capacity = (m_vertex_capacity_etc & EtcMask) + /// These are the counts and capacities of the m_P[]/m_N[]/m_T[]/m_C[] arrays the pointer + /// is not nullpr and the corresponding m_P_stride/m_N_stride/m_T_stride/m_C_stride > 0. + /// + ValueMask = 0x1FFF, // maximum vertex count and capacity + + /// + /// The use of the 3 bits in (m_vertex_count_etc & EtcMask) and 3 bits in (m_vertex_capacity_etc & EtcMask) + /// are described in the enum values below. + /// + EtcMask = 0xE000, + + /// + /// This bit is on m_vertex_count_etc. + /// Set means 4 m_ctrlnetP[] points are set and the m_ctrlnetT[]/m_ctrlnetC[] + /// are set if m_T[]/m_C[] is set. + /// + EtcControlNetQuadBit = 0x8000, + + /// + /// This bit is on m_vertex_count_etc. + /// Set means m_T[] contains set values. + /// + EtcTextureCoordinatesExistBit = 0x4000, + + /// + /// This bit is on m_vertex_capacity_etc. + /// Set means m_C[] contains set values. + /// + EtcColorsExistBit = 0x2000, + + /// + /// This bit is on m_vertex_capacity_etc. + /// Set means m_K[] contains set values. + /// + EtcCurvaturesExistBit = 0x4000, + + /// + /// This bit is on m_vertex_capacity_etc. + /// Set means the memory for the m_P[], m_N[], m_T[], and m_C[] arrays is managed by this class as a single + /// allocation. + /// m_P = new(std::nothrow) double[10*(point capacity)] and m_N, m_T, and m_C point into this allocation. + /// + EtcManagedArraysBit = 0x8000, }; - mutable unsigned short m_vertex_count_etc; - mutable unsigned short m_vertex_capacity_etc; + mutable unsigned short m_vertex_count_etc; // count value and 3 et cetera status bits + mutable unsigned short m_vertex_capacity_etc; // capacity value and 3 et cetera status bits + static void Internal_Set3dPointArrayToNan(double* a, size_t a_count, size_t a_stride); private: // corners for control net display in grid order (counter-clockwise quad order must swap [2] and[3]) double m_ctrlnetP[4][3]; - // Normal used for shading the control net display. + // Normal used for shading the control net display in grid order. double m_ctrlnetN[3]; - // The fragment's default corner texture coordinates in grid order. - // m_ctrlnetT[0] (bbox min = lower left) - // m_ctrlnetT[1] (lower right) - // m_ctrlnetT[2] (upper left) - // m_ctrlnetT[3] (bbox max = upper right) + +public: + /* + Parameters: + grid_corner_index - [in] + grid side N is between corner index N and corner index (N+1)%4. + Returns: + Texture coordinate at that corner. + Remarks: + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only + corner that corresponds to a SubD vertex. + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 + correspond to half of original SuD edges. + */ + const ON_3dPoint TextureCoordinateCorner( + unsigned int grid_corner_index + ) const; + +private: + // The fragment's current corner texture coordinates in grid order. + // These can be from ON_SubDFace pack rectangles, ON_SubDFace custom texture points, + // a texture mapping, or any other values rendering code deems desirable. + // When a texture mapping evaluatior is not involved, they are typically + // used to generate the fragment's texture coordinates by interpolation. + // m_ctrlnetT[0] + // m_ctrlnetT[1] + // m_ctrlnetT[2] + // m_ctrlnetT[3] mutable double m_ctrlnetT[4][3]; + // Corner principal curvatures in grid order. + mutable ON_SurfaceCurvature m_ctrlnetK[4]; + + // Corner vertex colors in grid order. + mutable ON_Color m_ctrlnetC[4]; + +private: + // When an ON_SubDFace is a quad, there is a single ON_SubDMeshFragment for the face + // and ON_SubDMeshFragment.m_pack_rect are the corners of the quad face's pack rect in + // ON_SubDMeshFragment gid order. + // When an ON_SubDFace is an N-gon with N !+ 4, there are N ON_SubDMeshFragments for the face + // and ON_SubDMeshFragment.m_pack_rect are corners of a subrect of the face's pack rect + // in ON_SubDMeshFragment gid order. + // The m_pack_rect[] points are in grid order. If the parent ON_SubDFace pack rect is rotated, + // then "lower/upper" and "left/right" are with the rotation applied and hence a lower/left coordinate + // may be greater than an upper/right coorinate value. + // m_pack_rect[0] "lower left" + // m_pack_rect[1] "lower right" + // m_pack_rect[2] "upper left" + // m_pack_rect[3] "upper right" + double m_pack_rect[4][2]; // NOT a mutable property + +public: + /* + Parameters: + grid_corner_index - [in] + grid side N is between corner index N and corner index (N+1)%4. + Returns: + Pack rect corner point. + Remarks: + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only + corner that corresponds to a SubD vertex. + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 + correspond to half of original SuD edges. + */ + const ON_2dPoint PackRectCorner( + unsigned int grid_corner_index + ) const; + + const ON_2dPoint PackRectCenter() const; + + void SetPackRectCornersForExperts( + bool bGridOrder, + const ON_2dPoint fragment_pack_rect_corners[4] + ); + + /* + Description: + This functions sets the pack rect corners on fragments used to render + quad and 3-gon ON_SubDFaces. + When an ON_SubDFace is a quad, it is rendered with one ON_SubDMeshFragment. + When an ON_SubDFace is a 3-gon, it is rendered with three ON_SubDMeshFragments. + Remarks: + It is critcial that m_face_fragment_count and m_face_fragment_index be set + correctly before calling this function. + A ON_SubDMeshFragment used to render a quad ON_SubDFace has + m_face_fragment_count = 1 and m_face_fragment_index = 0. + A ON_SubDMeshFragment used to render a 3-gon ON_SubDFace has + m_face_fragment_count = 3 and m_face_fragment_index = 0, 1 or 2. + */ + void SetQuadOr3gonFaceFragmentPackRectCorners( + bool bGridOrder, + const ON_2dPoint face_pack_rect_corners[4] + ); + + /* + Description: + This functions sets the pack rect corners on fragments used to render + n-gon ON_SubDFaces when n >= 5. + When an ON_SubDFace is an n-gon, it is rendered with n ON_SubDMeshFragments. + Parameters: + bGridOrder = [in] + Order for face_pack_rect_corners[]. + face_texture_coordinate_corners - [in] + The points returned by ON_SubDFace::PackRectCorner(bGridOrder, i) + face_pack_rect_size - [in] + The n-gon face's ON_SubDFace::PackRectSize() value. + ngon_grid_size - [in] + ngon_sub_pack_rect_size - [in] + ngon_sub_pack_rect_delta - [in] + These three parameters are values from ON_SubDFace::GetNgonSubPackRectSizeAndDelta(). + ngon_grid_size, ngon_sub_pack_rect_size, and ngon_sub_pack_rect_delta are identical + for all the ON_SubDMeshFragments used to render the n-gon face. The value of + m_face_fragment_index detemines which sub pack rect is assigned to each of + the n ON_SubDMeshFragments use to render the n-gon face. + Remarks: + It is critcial that m_face_fragment_count and m_face_fragment_index be set + correctly before calling this function. + A ON_SubDMeshFragment used to render an n-gon ON_SubDFace has + m_face_fragment_count = n and m_face_fragment_index = 0, ..., n-1. + */ + void SetNgonFaceFragmentPackRectCorners( + bool bGridOrder, + const ON_2dPoint face_pack_rect_corners[4], + ON_2dVector face_pack_rect_size, + ON_2udex ngon_grid_size, + ON_2dVector ngon_sub_pack_rect_size, + ON_2dVector ngon_sub_pack_rect_delta + ); + + /* + Description: + This functions gets the pack rect corners on fragments used to render 3-gon ON_SubDFaces. + When an ON_SubDFace is an 3-gon, it is rendered with 3 ON_SubDMeshFragments. + Parameters: + bFacePackRectGridOrder - [in] + Order for face_pack_rect_corners[]. + face_pack_rect_corners - [in] + The points returned by ON_SubDFace::PackRectCorner(bGridOrder, i) + fragment_index - [in] + 0 <= ngon_fragment_index < 3 + bFragmentPackRectGridOrder - [in] + Order for face_pack_rect_corners[]. + fragment_pack_rect_corners - [out] + */ + static bool Get3gonFaceFragmentPackRectCorners( + bool bFaceGridOrder, + const ON_2dPoint face_pack_rect_corners[4], + unsigned int fragment_index, + bool bFragmentGridOrder, + ON_2dPoint fragment_pack_rect_corners[4] + ); + + /* + Description: + This functions gets the pack rect corners for fragments used to render + n-gon ON_SubDFaces when n >= 5. + When an ON_SubDFace is an n-gon, it is rendered with n ON_SubDMeshFragments. + Parameters: + ngon_fragment_index - [in] + 0 <= ngon_fragment_index < ngon_edge_count + bGridOrder - [in] + Order for face_pack_rect_corners[]. + face_pack_rect_corners - [in] + The points returned by ON_SubDFace::PackRectCorner(bGridOrder, i) + face_pack_rect_size - [in] + The n-gon face's ON_SubDFace::PackRectSize() value. + ngon_grid_size - [in] + ngon_sub_pack_rect_size - [in] + ngon_sub_pack_rect_delta - [in] + These three parameters are values from ON_SubDFace::GetNgonSubPackRectSizeAndDelta(). + ngon_grid_size, ngon_sub_pack_rect_size, and ngon_sub_pack_rect_delta are identical + for all the ON_SubDMeshFragments used to render the n-gon face. The value of + m_face_fragment_index detemines which sub pack rect is assigned to each of + the n ON_SubDMeshFragments use to render the n-gon face. + fragment_pack_rect_corners - [out] + */ + static bool GetNgonFaceFragmentPackRectCorners( + unsigned int ngon_edge_count, + unsigned int ngon_fragment_index, + bool bGridOrder, + const ON_2dPoint face_pack_rect_corners[4], + ON_2dVector face_pack_rect_size, + ON_2udex ngon_grid_size, + ON_2dVector ngon_sub_pack_rect_size, + ON_2dVector ngon_sub_pack_rect_delta, + ON_2dPoint fragment_pack_rect_corners[4] + ); + public: /////////////////////////////////////////////////////////////////////////////////// // @@ -7086,7 +8052,7 @@ public: // Never modify m_P_stride, m_P, or the values in m_P. // Use m_grid functions to get point indices and quad face indices. double* m_P; // surface points - size_t m_P_stride; + size_t m_P_stride; // stride between points for m_P[] as an array of 8 byte doubles (so 0 or >= 3) const double* PointArray(ON_SubDComponentLocation subd_appearance)const; size_t PointArrayStride(ON_SubDComponentLocation subd_appearance)const; unsigned PointArrayCount(ON_SubDComponentLocation subd_appearance) const; @@ -7174,7 +8140,7 @@ public: // Use m_grid functions to get normal indices and quad face indices. // Note well: m_N_stride can be 0 when the normal is constant (control net face normal for example). double* m_N; // surface normals - size_t m_N_stride; + size_t m_N_stride; // stride between normals for m_N[] as an array of 8 byte doubles (so 0 or >= 3) const double* NormalArray(ON_SubDComponentLocation subd_appearance)const; size_t NormalArrayStride(ON_SubDComponentLocation subd_appearance)const; unsigned NormalArrayCount(ON_SubDComponentLocation subd_appearance) const; @@ -7200,7 +8166,7 @@ public: // Use m_grid functions to get texture indices and quad face indices. // Note well: m_T_stride can be 0 when the texture coordinate is constant (one color per face for example) mutable double* m_T; - mutable size_t m_T_stride; + mutable size_t m_T_stride; // stride between texture points for m_T[] as an array of 8 byte doubles (so 0 or >= 3) const double* TextureCoordinateArray(ON_SubDComponentLocation subd_appearance)const; size_t TextureCoordinateArrayStride(ON_SubDComponentLocation subd_appearance)const; @@ -7223,25 +8189,64 @@ public: If true, the texture coordinate corners are used ti set the fragment's m_T[] values after m_ctrlnetT[] is set. */ - void SetTextureCoordinateCorners( + void SetTextureCoordinateCornersForExperts( bool bGridOrder, - const ON_2dPoint texture_coordinate_corners[4], + const ON_3dPoint fragment_texture_coordinate_corners[4], bool bSetTextureCoordinates ) const; - void SetTextureCoordinateCorners( + /* + Description: + This functions sets the texture coordinates on fragments used to render + quad and 3-gon ON_SubDFaces. + A quad ON_SubDFace is rendered with one ON_SubDMeshFragment. + A 3-gon ON_SubDFace is rendered with three ON_SubDMeshFragments. + Remarks: + It is critcial that m_face_fragment_count and m_face_fragment_index be set + correctly before calling this function. + A ON_SubDMeshFragment used to render a quad ON_SubDFace has + m_face_fragment_count = 1 and m_face_fragment_index = 0. + A ON_SubDMeshFragment used to render a 3-gon ON_SubDFace has + m_face_fragment_count = 3 and m_face_fragment_index = 0, 1 or 2. + */ + void SetQuadOr3gonFaceFragmentTextureCoordinateCorners( bool bGridOrder, - const ON_2dPoint texture_coordinate_corners[4], - double s0, - double s1, - double t0, - double t1, + const ON_3dPoint face_texture_coordinate_corners[4], bool bSetTextureCoordinates ) const; - void SetTextureCoordinateCorners( + /* + Description: + When an ON_SubDFace is an n-gon with n >= 5, it is rendered with n ON_SubDMeshFragments. + This functions sets the texture coordinates on those fragments. + Parameters: + bGridOrder = [in] + Order for face_pack_rect_corners[]. + face_texture_coordinate_corners - [in] + The points returned by ON_SubDFace::TextureCoordinateCorner(bGridOrder, i, false) + face_pack_rect_size - [in] + The n-gon face's ON_SubDFace::PackRectSize() value. + ngon_grid_size - [in] + ngon_sub_pack_rect_size - [in] + ngon_sub_pack_rect_delta - [in] + These three parameters are values from ON_SubDFace::GetNgonSubPackRectSizeAndDelta(). + ngon_grid_size, ngon_sub_pack_rect_size, and ngon_sub_pack_rect_delta are identical + for all the ON_SubDMeshFragments used to render the n-gon face. The value of + m_face_fragment_index detemines which sub pack rect is assigned to each of + the n ON_SubDMeshFragments use to render the n-gon face. + Remarks: + It is critcial that m_face_fragment_count and m_face_fragment_index be set + correctly before calling this function. + A ON_SubDMeshFragment used to render an n-gon ON_SubDFace has + m_face_fragment_count = n and m_face_fragment_index = 0, ..., n-1. + */ + void SetNgonFaceFragmentTextureCoordinateCorners( bool bGridOrder, - const ON_3dPoint texture_coordinate_corners[4], + const ON_3dPoint face_texture_coordinate_corners[4], + ON_2dVector face_pack_rect_size, + ON_2udex ngon_grid_size, + ON_2dVector ngon_sub_pack_rect_size, + ON_2dVector ngon_sub_pack_rect_delta, bool bSetTextureCoordinates ) const; @@ -7254,7 +8259,6 @@ public: Otherwise, points are returned in counter-clockwise quad order. texture_coordinate_corners - [out] */ - bool GetTextureCoordinteCorners( bool bGridOrder, ON_3dPoint texture_coordinate_corners[4] @@ -7266,6 +8270,171 @@ public: */ void SetTextureCoordinatesFromCorners() const; + /* + Description: + Set the texture coordinates in m_T[] from the values in m_pack_rect[]. + */ + void SetPackedTextureCoordinates() const; + + /* + Description: + Set the this fragments texture coordinates in m_T[] to cover (0,1)x(0,1) + */ + void SetUnpackedTextureCoordinates() const; + +private: + void Internal_SetTextureCoordinatesFromCorners( + const double* corner0, + const double* corner1, + const double* corner2, + const double* corner3, + double default_coordinate_value, + int corner_dim + ) const; + +private: + /////////////////////////////////////////////////////////////////////////////////// + // + // Per vertex colors + // + // Depending on the strides, m_P[], m_N[], m_T[] and m_C[] can be separate or interlaced. + // + // If m_C is not nullptr and m_C_stride>0, then m_C[] can accomodate up to m_P_capacity + // elements. + // Otherwise there is no per vertex false color. + // Never modify m_C_stride, m_C. + // Use m_grid functions to get color indices and quad face indices. + // + // m_C[] is rarely used and typically managed with ReserveManagedColorCapacity() / DeleteManagedColorCapacity(). + // NOTE WELL: + // When m_C is interlaced with something containing doubles, m_C_stride must be + // a multiple of 2 to keep the doubles 8 bytes aligned. + // When m_C is not interlaced, m_C_stride is typically 1. If this is confusing, + // please learn more about algnment and interlacing before working on this code. + mutable ON_Color* m_C; + mutable size_t m_C_stride; // stride for m_C[] as an array of 4 bytes ON_Color elements (so 0 or >= 1). + +public: + /* + Returns: + If grid vertex colors are available, then VertexCount() is returned. + Otherwise 0 is returned. + */ + unsigned int ColorCount() const; + unsigned int ColorCapacity() const; + + const ON_Color* ColorArray(ON_SubDComponentLocation subd_appearance)const; + size_t ColorArrayStride(ON_SubDComponentLocation subd_appearance)const; + unsigned ColorArrayCount(ON_SubDComponentLocation subd_appearance) const; + + /* + Returns: + True if vertex color values are set on this fragment. + */ + bool ColorsExist() const; + + /* + Parameters: + bSetColorsExist - [in] + True if vertex colors exist. + False vertex colors do not exist or are no longer valid. + */ + void SetColorsExist(bool bSetColorsExist) const; + + /* + Description: + Set the vertex colors in m_C[] to color. + Parameters: + color - [in] + */ + bool SetColors( + ON_Color color + ) const; + + /* + Description: + Set the vertex colors in m_C[] from a callback function. + Parameters: + */ + bool SetColorsFromCallback( + const ON_MappingTag& fragment_colors_mapping_tag, + const ON_SubD& subd, + ON__UINT_PTR callback_context, + const ON_Color(*color_callback)( + ON__UINT_PTR callback_context, + const ON_MappingTag& mapping_tag, + const ON_SubD& subd, + ON_SubDComponentPtr cptr, + const ON_3dPoint& P, + const ON_3dVector& N, + const ON_3dPoint& T, + const ON_SurfaceCurvature& K + ) + )const; + +private: + /////////////////////////////////////////////////////////////////////////////////// + // + // Principal curvatures + // + // If m_K is not nullptr and m_K_stride>0, then m_K[] can accomodate up to m_P_capacity + // principal curvatures and sectional curvatures (four doubles, k1,k2,sx,sy). + // Otherwise there are no principal curvatures or sectional curvatures. + // (sx = sectional curvature in grid "x" direction, sy = sectional curvature in grid "y" direction.) + // At exceptional points, the curvature values may be nan. + // Never modify m_K_stride, m_K. + // Use m_grid functions to get principal curvature indices and quad face indices. + // + // m_K[] is rarely used and typically managed with ReserveManagedCurvatureCapacity() / DeleteManagedCurvatureCapacity(). + // NOTE WELL: + // If m_K[] is interlacesd, the number of bytes between successive elements of m_K[] must be a multiple of 16 + // because sizeof(m_K) = 16 AND m_K_stride is the stride between elements of m_K[] + // This is different than m_P_stride, m_N_stride, and m_T_stride, which count the number of doubles between successive + // points/normals/texture points in m_P[]/m_N[]/m_T[]. + mutable ON_SurfaceCurvature* m_K; + size_t m_K_stride; // stride for m_K[] as an array of 16 byte ON_SurfaceCurvature elements (so 0 or >= 1). + +public: + /* + Returns: + If grid vertex principal curvatures and sectional curvatures are available, then VertexCount() is returned. + Otherwise 0 is returned. + */ + unsigned int CurvatureCount() const; + unsigned int CurvatureCapacity() const; + + const ON_SurfaceCurvature* CurvatureArray(ON_SubDComponentLocation subd_appearance)const; + unsigned CurvatureArrayCount(ON_SubDComponentLocation subd_appearance) const; + + /* + Description: + Adds managed curvature capacity to store vertex curvatures. + */ + bool ReserveManagedCurvatureCapacity() const; + + /* + Description: + Deletes capacity added by ReserveManagedCurvatureCapacity(). + */ + void DeleteManagedCurvatureCapacity() const; + + + /* + Returns: + True if vertex color values are set on this fragment. + */ + bool CurvaturesExist() const; + + /* + Parameters: + bSetCurvaturesExist - [in] + True if vertex curvatures exist. + False vertex curvatures do not exist or are no longer valid. + */ + void SetCurvaturesExist(bool bSetCurvaturesExist) const; + +public: + // Normalized grid parameters useful for appling a texture to the grid are available // from m_grid functions. @@ -7278,10 +8447,22 @@ public: ON_BoundingBox m_surface_bbox; public: /* + Parameters: + display_density - [in] + Determines grid size + bCurvatureArray - [in] + true to include room for the m_K[] array. Returns: - Amount of memory needed for the fragment, the m_P[], and the m_N[] arrays. + Amount of memory needed for the fragment, the m_P[], m_N[], m_T[], m_C[], + and optionally the m_K[] arrays. */ - static size_t SizeofFragment(unsigned int display_density); + static size_t SizeofFragment( + unsigned int display_density, + bool bCurvatureArray + ); + + public: + void Dump(ON_TextLog& text_log) const; }; class ON_CLASS ON_SubDManagedMeshFragment : public ON_SubDMeshFragment @@ -7867,6 +9048,11 @@ public: double subdivision_point[3] ) const; + /* + Description: + Clears saved subdivision and limit surface information for this component. + Attached components are not modifed. + */ void ClearSavedSubdivisionPoint() const; /* @@ -7921,7 +9107,7 @@ protected: // if ( 0 != (m_saved_points_flags & SubdivisionPointBit), then m_cache_subd_P is set. SubdivisionPointBit = 0x40, - // if ( 0 != (m_saved_points_flags & SurfacePointBit), then ON_subDVertex.m_limit* values are set. + // if ( 0 != (m_saved_points_flags & SurfacePointBit), then ON_SubDVertex.m_limit* values are set. // ON_SubDVertex: Set means one or more sector limit surface points are saved in ON_SubDVertex.m_limit_point. // ON_SubDEdge: Set means the limit surface NURBS curve control points are cached. // ON_SubDFace: Set means limit surface mesh fragments are saved in ON_SubDFace.m_surface_mesh_fragments. @@ -8115,13 +9301,13 @@ public: // Edge tag counts // - // Number of edges tags ON_SubD::EdgeTag::Unset + // Number of edges tags ON_SubDEdgeTag::Unset unsigned short m_unset_edge_count = 0; - // Number of edges tags ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX + // Number of edges tags ON_SubDEdgeTag::Smooth or ON_SubDEdgeTag::SmoothX unsigned short m_smooth_edge_count = 0; - // Number of edges tags ON_SubD::EdgeTag::Crease + // Number of edges tags ON_SubDEdgeTag::Crease unsigned short m_crease_edge_count = 0; ///////////////////////////////////////////////////// @@ -8160,6 +9346,11 @@ public: // class ON_CLASS ON_SubDVertex : public ON_SubDComponentBase { +private: + friend class ON_SubDArchiveIdMap; + friend class ON_SubDEdge; + friend class ON_SubDFace; + public: ON_SubDVertex() = default; ~ON_SubDVertex() = default; @@ -8169,12 +9360,11 @@ public: public: unsigned int VertexId() const; - public: /* Description: - Clears saved subdivision and limit surface information - for this component. + Clears saved subdivision and limit surface information for this vertex. + Attached edges and faces are not modifed. */ void ClearSavedSubdivisionPoints() const; @@ -8191,6 +9381,11 @@ public: class ON_SubDVertex*& vertex ); + const ON_wString ToString( + bool bIncludeControlNetPoint, + bool bIncludeTopology + ) const; + /* Parameters: bIncludeEdges - [in] @@ -8249,7 +9444,7 @@ public: const class ON_SubDVertex* m_next_vertex = nullptr; // linked list of vertices on this level public: - ON_SubD::VertexTag m_vertex_tag = ON_SubD::VertexTag::Unset; + ON_SubDVertexTag m_vertex_tag = ON_SubDVertexTag::Unset; private: @@ -8306,14 +9501,14 @@ public: bReturnBestGuessWhenInvalid If bReturnBestGuessWhenInvalid is true and the topology and current edges tags do not meet the conditions for a valid vertex, then a best guess for - a vertex tag is returned. Otherwise ON_SubD::VertexTag::Unset is returned. + a vertex tag is returned. Otherwise ON_SubDVertexTag::Unset is returned. When in doubt pass false and check for unset before using. Returns: The suggested value for this vertices tag based on its current tag value and its current edges. Assumes the vertex and edge topology are correct and the edge tags are correctly set. */ - ON_SubD::VertexTag SuggestedVertexTag( + ON_SubDVertexTag SuggestedVertexTag( bool bApplyInputTagBias, bool bReturnBestGuessWhenInvalid ) const; @@ -8371,7 +9566,7 @@ public: //ON_SubD::VertexEdgeOrder SortEdges(); unsigned int EdgeCount( - ON_SubD::EdgeTag edge_tag + ON_SubDEdgeTag edge_tag ) const; unsigned int EdgeCount() const; @@ -8423,49 +9618,56 @@ public: /* Returns: - True if m_vertex_tag is ON_SubD::VertexTag::Smooth. + True if m_vertex_tag is ON_SubDVertexTag::Smooth. */ bool IsSmooth() const; /* Returns: - True if m_vertex_tag is ON_SubD::VertexTag::Crease. + True if m_vertex_tag is ON_SubDVertexTag::Crease. */ bool IsCrease() const; /* Returns: - True if m_vertex_tag is ON_SubD::VertexTag::Corner. + True if m_vertex_tag is ON_SubDVertexTag::Corner. */ bool IsCorner() const; /* Returns: - True if m_vertex_tag is ON_SubD::VertexTag::Dart. + True if m_vertex_tag is ON_SubDVertexTag::Dart. */ bool IsDart() const; /* Returns: - True if m_vertex_tag is ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. + True if m_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Crease. */ bool IsSmoothOrCrease() const; /* Returns: - True if m_vertex_tag is ON_SubD::VertexTag::Crease or ON_SubD::VertexTag::Corner. + True if m_vertex_tag is ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner. */ bool IsCreaseOrCorner() const; /* Returns: - True if m_vertex_tag is ON_SubD::VertexTag::Crease or ON_SubD::VertexTag::Corner or ON_SubD::VertexTag::Dart. + True if m_vertex_tag is ON_SubDVertexTag::Dart, ON_SubDVertexTag::Crease or ON_SubDVertexTag::Corner. */ bool IsDartOrCreaseOrCorner() const; + /* Returns: - True if m_vertex_tag is ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Dart. + True if m_vertex_tag is ON_SubDVertexTag::Dart or ON_SubDVertexTag::Crease + */ + bool IsDartOrCrease() const; + + /* + Returns: + True if m_vertex_tag is ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart. */ bool IsSmoothOrDart() const; @@ -8604,6 +9806,17 @@ public: bool bInteriorEdgesOnly ) const; + /* + Returns: + Number of creased edges + */ + const unsigned int CreasedEdgeCount( + bool bCountInteriorCreases, + bool bCountBoundaryCreases, + bool bCountNonmanifoldCreases, + bool bCountWireCreases + ) const; + /* Parameters: @@ -8674,11 +9887,31 @@ public: double surface_point[3] ) const; - /// - /// The SubD vertex Catmull-Clark limit surface point. - /// + /* + Returns: + The SubD surface point. + */ const ON_3dPoint SurfacePoint() const; + /* + Parameters: + sector_face - [in] + When the vertex is a crease or corner vertex, different sectors typically + have different normals and you must specify a face to determine the sector. + bUndefinedNormalPossible - [in] + If the SubD control net is degenerate around the vertex, the normal may + be zero. Pass true if you will accept a zero normal vector. Otherwise + ON_3dVector::NanVector is returned when a non-zero normal cannot be calculated. + Returns: + The SubD surface normal. + */ + const ON_3dVector SurfaceNormal( + const ON_SubDFace* sector_face, + bool bUndefinedNormalPossible + ) const; + + const ON_SubDSectorSurfacePoint& SectorSurfacePointForExperts() const; + /* Parameters: subd_appearance - [in] @@ -8706,6 +9939,18 @@ public: const ON_SubDSectorSurfacePoint surface_point ) const; + /* + Description: + In general, after you modify a vertex you should call VertexModifiedNotification(). + + This is an expert user function that clears any saved limit point evaluations for this vertex. + No saved subdivision points are cleared. + No modifications are made to attached edges or faces. + Remarks: + In general, you should call VertexModifiedNotification() after you modify a vertex. + Compare with ClearSavedSubdivisionPoints() which clears any subdivision point + limit point evaluations saved on this vertex. + */ void ClearSavedSurfacePoints() const; /* @@ -8723,8 +9968,8 @@ public: /* Description: - Call this function if the vertex is modified and it will clear any - cached subdivision information that needs to be recalculated. + Call this function if the vertex is modified. + It will clear saved subdivision information on the vertex and neighboring faces and edges that needs to be recalculated. */ void VertexModifiedNofification() const; @@ -8831,9 +10076,6 @@ private: ); private: - friend class ON_SubDArchiveIdMap; - friend class ON_SubDEdge; - friend class ON_SubDFace; void CopyFrom( const ON_SubDVertex* src, bool bCopyEdgeArray, @@ -8848,6 +10090,13 @@ private: // class ON_CLASS ON_SubDEdge : public ON_SubDComponentBase { +private: + friend class ON_Internal_SubDFaceMeshFragmentAccumulator; + friend class ON_SubDHeap; + friend class ON_SubDArchiveIdMap; + friend class ON_SubDVertex; + friend class ON_SubDFace; + public: ON_SubDEdge() = default; ~ON_SubDEdge() = default; @@ -8860,8 +10109,8 @@ public: public: /* Description: - Clears saved subdivision and limit surface information - for this component. + Clears saved subdivision and limit surface information for this edge. + Attached vertices and faces are not modifed. */ void ClearSavedSubdivisionPoints() const; @@ -8941,13 +10190,13 @@ public: /* Description: - Expert user tool to set sector coefficients. + Expert user tool to set mutable sector coefficients. Returns: True if values were modified. */ bool UpdateEdgeSectorCoefficientsForExperts( bool bUnsetEdgeSectorCoefficientsOnly - ); + ) const; public: const ON_COMPONENT_INDEX ComponentIndex() const; @@ -8960,11 +10209,11 @@ public: public: // When checking the edge tag, it is important to consider what - // should happen in the ON_SubD::EdgeTag::SmoothX case. It is strongly + // should happen in the ON_SubDEdgeTag::SmoothX case. It is strongly // suggested that you use the member functions ON_SubDEdge::IsSmooth() // and ON_SubDEdge::IsCrease() instead of comparing m_edge_tag to - // ON_SubD::EdgeTag values. - ON_SubD::EdgeTag m_edge_tag = ON_SubD::EdgeTag::Unset; + // ON_SubDEdgeTag values. + ON_SubDEdgeTag m_edge_tag = ON_SubDEdgeTag::Unset; private: unsigned char m_reserved1 = 0; @@ -8997,45 +10246,45 @@ public: // m_vertex[1] = vertex at the end of the edge. const class ON_SubDVertex* m_vertex[2] = {}; - // If the value of vertex->m_vertex_tag is not ON_SubD::VertexTag::Smooth, + // If the value of vertex->m_vertex_tag is not ON_SubDVertexTag::Smooth, // then that vertex is "tagged". // - // If the value of m_edge_tag is ON_SubD::EdgeTag::Crease, + // If the value of m_edge_tag is ON_SubDEdgeTag::Crease, // then m_sector_coefficient[] should be {0,0}. // In any case m_sector_coefficient[] values are ignored and the // midpoint of the edge is the location of the edge.s subdivision point. - // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Crease - // and both subdivision edges will be tagged as ON_SubD::EdgeTag::Crease. + // The edge's subdivision vertex will be tagged as ON_SubDVertexTag::Crease + // and both subdivision edges will be tagged as ON_SubDEdgeTag::Crease. // - // If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth + // If the value of m_edge_tag is ON_SubDEdgeTag::Smooth // and neither end vertex is tagged, then m_sector_coefficient[] should be {0,0}. // In any case m_sector_coefficient[] values are ignored on smooth edges // with smooth vertices at both ends. - // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth - // and both subdivision edges will be tagged as ON_SubD::EdgeTag::Smooth. + // The edge's subdivision vertex will be tagged as ON_SubDVertexTag::Smooth + // and both subdivision edges will be tagged as ON_SubDEdgeTag::Smooth. // - // If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth and + // If the value of m_edge_tag is ON_SubDEdgeTag::Smooth and // exactly one end vertex is tagged, then the m_sector_coefficient[] // value for the tagged end is calculated by ON_SubDSectorType::SectorCoefficient(). // tagged_weight*tagged_vertex + (1.0 - tagged_weight)*untagged_vertex // is used when combining the edge ends. - // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth - // and both subdivision edges will be tagged as ON_SubD::EdgeTag::Smooth. + // The edge's subdivision vertex will be tagged as ON_SubDVertexTag::Smooth + // and both subdivision edges will be tagged as ON_SubDEdgeTag::Smooth. // - // If the value of m_edge_tag is ON_SubD::EdgeTag::SmoothX, then the edge + // If the value of m_edge_tag is ON_SubDEdgeTag::SmoothX, then the edge // must have exactly two neighboring faces, // both vertices must be tagged and the m_sector_coefficient[] // values are calculated by ON_SubDSectorType::SectorCoefficient(). // When the edge is subdivided, the midpoint of the edge is the // location of the edge.s subdivision point. - // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth - // and both subdivision edges will be tagged as ON_SubD::EdgeTag::Smooth. + // The edge's subdivision vertex will be tagged as ON_SubDVertexTag::Smooth + // and both subdivision edges will be tagged as ON_SubDEdgeTag::Smooth. // - // If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth + // If the value of m_edge_tag is ON_SubDEdgeTag::Smooth // and both end vertices are tagged, that is a severe error // condition and the edge is subdivided at its midpoint. // - // If the value of m_edge_tag is ON_SubD::EdgeTag::SmoothX + // If the value of m_edge_tag is ON_SubDEdgeTag::SmoothX // and both end vertices are not tagged, that is a severe error // condition and the edge is subdivided at its midpoint. // @@ -9046,10 +10295,10 @@ public: // The name "sector coefficient" is used because the value is a property of the // vertex's sector (every smooth edge inside a vertex sector has the same value at the tagged vertex). // The sector coefficient does not change which a subdivision is applied. - double m_sector_coefficient[2] = {}; + mutable double m_sector_coefficient[2] = {}; - // If m_edge_tag is not ON_SubD::EdgeTag::Sharp, then m_sharpness is ignored. - // If m_edge_tag is ON_SubD::EdgeTag::Sharp, then m_sharpness controls how hard/soft + // If m_edge_tag is not ON_SubDEdgeTag::Sharp, then m_sharpness is ignored. + // If m_edge_tag is ON_SubDEdgeTag::Sharp, then m_sharpness controls how hard/soft // the edge appears. // The behavior of a "sharp" edge with m_sharpness = 1 is identical to a crease edge. // A "sharp" edge with m_sharpness = 0 is identical to a smooth edge. @@ -9068,8 +10317,6 @@ private: // ClearSavedSubdivisionPoints() zeros // the appropriate bit of m_saved_points_flags. - friend class ON_Internal_SubDFaceMeshFragmentAccumulator; - friend class ON_SubDHeap; mutable class ON_SubDEdgeSurfaceCurve* m_limit_curve = nullptr; public: @@ -9080,7 +10327,52 @@ public: If the edge is valid, this will be 2. */ unsigned int VertexCount() const; - + + /* + Parameters: + evi - [in] + 0 or 1 + Returns: + If evi is 0 or 1 and m_vertex[evi] is not nullptr, then m_vertex[evi]->m_id is returned. Otherwise 0 i returned. + */ + unsigned int VertexId( + unsigned evi + ) const; + + /* + Parameters: + evi - [in] + 0 or 1 + Returns: + If evi is 0 or 1, then m_vertex[evi] is returned. Otherwise nullptr is returned. + */ + const class ON_SubDVertex* Vertex( + unsigned evi + ) const; + + unsigned int VertexArrayIndex( + const class ON_SubDVertex* v + ) const; + + + /* + Description: + Return the vertex at the other end of the edge. + Parameters: + vertex - [in] + A vertex referenced in the edge's m_vertex[] array. + Returns: + If vertex is not nullptr and exactly one of m_vertex[] is equal to vertex, + then the other m_vertex[] pointer is returned. + In any other case, nullptr is returned. + See Also: + ON_SubDEdge.NeighborFace() + */ + const class ON_SubDVertex* OtherEndVertex( + const class ON_SubDVertex* vertex + ) const; + + unsigned int FaceCount() const; /* @@ -9180,32 +10472,6 @@ public: const ON_SubDFace* new_face ); - const class ON_SubDVertex* Vertex( - unsigned int i - ) const; - - unsigned int VertexArrayIndex( - const class ON_SubDVertex* v - ) const; - - - /* - Description: - Return the vertex at the other end of the edge. - Parameters: - vertex - [in] - A vertex referenced in the edge's m_vertex[] array. - Returns: - If vertex is not nullptr and exactly one of m_vertex[] is equal to vertex, - then the other m_vertex[] pointer is returned. - In any other case, nullptr is returned. - See Also: - ON_SubDEdge.NeighborFace() - */ - const ON_SubDVertex* OtherEndVertex( - const ON_SubDVertex* vertex - ) const; - /* Parameters: vertex0 - [in] @@ -9264,7 +10530,7 @@ public: face - [in] A face referenced in the edge's m_face2[] array. bStopAtCrease - [in] - If true and if m_edge_tag = ON_SubD::EdgeTag::Crease, + If true and if m_edge_tag = ON_SubDEdgeTag::Crease, then nullptr is returned. Returns: If the m_face_count = 2, @@ -9285,7 +10551,7 @@ public: face - [in] A face referenced in the edge's m_face2[] array. bStopAtCrease - [in] - If true and if m_edge_tag = ON_SubD::EdgeTag::Crease, + If true and if m_edge_tag = ON_SubDEdgeTag::Crease, then nullptr is returned. Returns: If the m_face_count = 2, @@ -9320,17 +10586,17 @@ public: ) const; /* Returns: - True if m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX. + True if m_edge_tag is ON_SubDEdgeTag::Smooth or ON_SubDEdgeTag::SmoothX. False in all other cases. */ bool IsSmooth() const; /* Returns: - True if m_edge_tag is ON_SubD::EdgeTag::Smooth. + True if m_edge_tag is ON_SubDEdgeTag::Smooth. Remarks: Expert user function. - This is used in rare cases when level 0 edges tagged as ON_SubD::EdgeTag::SmoothX + This is used in rare cases when level 0 edges tagged as ON_SubDEdgeTag::SmoothX need special handling in low level evaluation code. Typical SDK level functions and anything related to runtime user interface should call IsSmooth(). */ @@ -9338,36 +10604,36 @@ public: /* Returns: - True if m_edge_tag is ON_SubD::EdgeTag::SmoothX. + True if m_edge_tag is ON_SubDEdgeTag::SmoothX. Remarks: Expert user function. - This is used in rare cases when level 0 edges tagged as ON_SubD::EdgeTag::SmoothX + This is used in rare cases when level 0 edges tagged as ON_SubDEdgeTag::SmoothX need special handling in low level evaluation code. Typical SDK level functions and anything related to runtime user interface should call IsSmooth(). An edge tagged as "X" can occur at level 0. It is subdivided as a smooth - vertex and both of its end vertices are tagged as ON_SubD::VertexTag::Crease, - ON_SubD::VertexTag::Corner, or ON_SubD::VertexTag::Dart. + vertex and both of its end vertices are tagged as ON_SubDVertexTag::Crease, + ON_SubDVertexTag::Corner, or ON_SubDVertexTag::Dart. This tag cannot appear at level N with N >= 1. */ bool IsSmoothX() const; /* Returns: - True if m_edge_tag is ON_SubD::EdgeTag::Crease. + True if m_edge_tag is ON_SubDEdgeTag::Crease. */ bool IsCrease() const; /* Returns: - True if m_edge_tag is ON_SubD::EdgeTag::Crease and both of its end vertices - are tagged as ON_SubD::VertexTag::Crease, or ON_SubD::VertexTag::Corner. + True if m_edge_tag is ON_SubDEdgeTag::Crease and both of its end vertices + are tagged as ON_SubDVertexTag::Crease, or ON_SubDVertexTag::Corner. */ bool IsHardCrease() const; /* Returns: - True if m_edge_tag is ON_SubD::EdgeTag::Crease and at least one of its end vertices - are tagged as ON_SubD::VertexTag::Dart. + True if m_edge_tag is ON_SubDEdgeTag::Crease and at least one of its end vertices + are tagged as ON_SubDVertexTag::Dart. */ bool IsDartCrease() const; @@ -9431,6 +10697,7 @@ public: unsigned int edge_face_index ) const; + bool EdgeSurfaceCurveIsSet() const; @@ -9482,9 +10749,6 @@ private: ); private: - friend class ON_SubDArchiveIdMap; - friend class ON_SubDVertex; - friend class ON_SubDFace; void CopyFrom( const ON_SubDEdge* src, bool bReverseEdge, @@ -9499,6 +10763,13 @@ private: // class ON_CLASS ON_SubDFace : public ON_SubDComponentBase { +private: + friend class ON_Internal_SubDFaceMeshFragmentAccumulator; + friend class ON_SubDHeap; + friend class ON_SubDArchiveIdMap; + friend class ON_SubDVertex; + friend class ON_SubDEdge; + public: ON_SubDFace() = default; ~ON_SubDFace() = default; @@ -9511,12 +10782,11 @@ public: public: /* Description: - Clears saved subdivision and limit surface information - for this component. + Clears saved subdivision and limit surface information for this face. + Attached edges and vertices are not modifed. */ void ClearSavedSubdivisionPoints() const; - public: static const ON_SubDFace Empty; @@ -9590,35 +10860,322 @@ public: const class ON_SubDFace* m_next_face = nullptr; // linked list of faces on this level private: - // Location of this face's default 2d texture coordinates ("surface parameter") in normaized 2d texture coordinates - mutable double m_texture_coordinate_origin[2] = {}; - mutable double m_texture_coordinate_delta[2] = {}; + unsigned int m_reserved = 0; - enum TextureCoordinateBits : unsigned char +private: + // If non zero, m_pack_id identifies the packed group of faces this faces belongs to. + // Faces that are not quads are never in grouped with other faces. + // When possible, quads are packed into groups that form larger rectangular regions. + // ON_SubD::Packfaces() creates the packing information. + // Packing information is saved in 3dm files. + unsigned int m_pack_id = 0; + + // Location of this face's pack rectin normaized coordinates + // Each face is assigned a unique rectangle in the unit square (0,1)x(0,1). + // Groups of quad faces packed together. + // If faces are added to an existing subd, the pack rects for the entire subd must be recalculated. + // If faces in a quad group are removed from a subd, the pack rects for the entire subd must be recalculated. + double m_pack_rect_origin[2] = {ON_DBL_QNAN}; + double m_pack_rect_size[2] = {ON_DBL_QNAN}; + unsigned int m_packed_rect_u = 0; + unsigned int m_packed_rect_v = 0; + + enum PackStatusBits : unsigned char { - None = 0, + ZeroPackStatusBits = 0, - // 4 ways the texture domain can be rotated for a face when packing is applied. - // These enum values must be 0,1,2,3 + ///////////////////////////////////////////////////////////////////// + // + // Pack rectangle status + // + + // 4 ways the pack rectangle can be rotated for a face when packed texture coordinates are + // calculated from the packing rectangle. + // These enum values must be 0,1,2,3 because these values are used in arithmetic formulae. + // Rotation is used to keep pac rect coordinates continuous in quad packs containing faces + // with inconsistent topological orientations, which is the most common situation. PackingRotate0 = 0, PackingRotate90 = 1, PackingRotate180 = 2, PackingRotate270 = 3, - // bits mask for packing rotation setting + // mask for the bits used to encode the packing rotation PackingRotateMask = 3, - UnusedBit1 = 0x04U, - UnusedBit2 = 0x08U, - UnusedBitsMask = 0x0CU, + // bit used to determine if the values in m_packed_coordinate_origin[] and m_packed_coordinate_delta[] + // are set to valid values. + PackRectSet = 0x04U, - // bits for TextureCoordinateDomainType() - DomainTypeMask = 0xF0U, + PackingBitsMask = 0x07U, + NotPackingBitsMask = 0xF8U, + + // add other non-mutable bits here }; - mutable unsigned char m_texture_coordinate_bits = 0; + + // NOT mutable on purpose + unsigned char m_pack_status_bits = 0; private: - unsigned char m_reserved1 = 0; + enum TextureStatusBits : unsigned char + { + ZeroTextureStatusBits = 0, + + // TexturePoints bits + TexturePointsSet = 0x01U, // set when face has set custom texture coordinates + TexturePointsBitsMask = 0x01U, + NotTexturePointsBitsMask = 0xFEU, + + // add other mutable bits here + }; + // mutable because rendering code frequently modifis const objects in the Rhino. + mutable unsigned char m_texture_status_bits = 0; + +public: + /* + Returns: + 0: unset pack id. + > 0: set pack id. + Remarks: + Faces that share the same PackId() must be neighboring quad faces that form a larger + rectangular grid of quads. Single faces that are not quads and isolated quads cannot + share a pack id with other faces. + */ + unsigned int PackId() const; + + /* + Returns: + true if the pack rect is set. + Remarks: + Faces that share the same PackId() must be neighboring quad faces that form a larger + rectangular grid of quads. Single faces that are not quads and isolated quads cannot + share a pack id with other faces. + */ + bool PackRectIsSet() const; + + /* + Returns + Lower left coordinate of this face's pack rect in normalized pack rect coordinates. + */ + const ON_2dPoint PackRectOrigin() const; + + /* + Returns + Size of this face's pack rect in normalized pack rect coordinates. + */ + const ON_2dVector PackRectSize() const; + + /* + Returns: + 0, 90, 180, or 270 + Remarks: + This rotation is used to keep pack rect coordinates continuous in quad packs + containing faces with inconsistent topological orientations, which is the most + common situation. + */ + unsigned PackRectRotationDegrees() const; + + /* + Returns: + 0, 0.5*ON_PI, ON_PI, or 1.5*ON_PI + Remarks: + This rotation is used to keep pack rect coordinates continuous in quad packs + containing faces with inconsistent topological orientations, which is the most + common situation. + */ + double PackRectRotationRadians() const; + + /* + Parameters: + bGridOrder - [in] + false: counter clockwise quad order. + true: fragment grid order + corner_index - [in] + 0, 1, 2, or 3 + Returns: + Specified pack rectangle corner coordinates. + The pack rect is the (x0,x0+dx) x (y0,y0+dy) rectangle inside the unit square (0,1)x(0,1) + where (x0,y0) = PackRectOrigin() and (dx,dy) = PackRectSize(). + */ + const ON_2dPoint PackRectCorner( + bool bGridOrder, + int corner_index + ) const; + + /* + Parameters: + bGridOrder - [in] + false: counter clockwise quad order. + true: fragment grid order + mesh_fragment_pack_rect_corners - [out] + Pack rectangle corners for the specified mesh fragment are returned here. + face_pack_rect_corners[i] = this->PackRectCorner(bGridOrder,i); + Returns: + True if the input is valid and the face's pack rectangle corner coordinates were returned. + False otherwise. + Remarks: + Compare with ON_SubDFace.GetMeshFragmentPackRectCorners(). + */ + bool GetFacePackRectCorners( + bool bGridOrder, + ON_2dPoint face_pack_rect_corners[4] + ) const; + + /* + Parameters: + bGridOrder - [in] + false: counter clockwise quad order. + true: fragment grid order + fragment_index - [in] + If the face is a quad (EdgeCount() = 4), then fragment_index must be zero. + If the face is a n-gon (EdgeCount() = n and n != 4), + then 0 <= fragment_index < EdgeCount(). + mesh_fragment_pack_rect_corners - [out] + Pack rectangle corners for the specified mesh fragment are returned here. + If the face is a quad, mesh_fragment_pack_rect_corners[] are the pack rect texture points for + the quad corners in the order specified by bGridOrder and standard linear interpolation + between the 4 corners will produce reasonable texture mapping coordinates. + If the face is an n-gon, mesh_fragment_pack_rect_corners[0] is the pack rect texture point + at face->Vertex(fragment_index). For n-gons, the n fragments are assigned non-overlapping rectangular + subsetsof the face's pack rect. Interpolating between corner values will not produce good texture + mapping coordinates. Pack rects are create useful and optimal texture mapping results when the SubD + is renderered from its mesh fragmantes. + Returns: + True if the input is valid and the pack rectangle corner coordinates for the specified mesh fragment were returned. + False otherwise. + Remarks: + A quad face (EdgeCount()=4) is rendered using 1 ON_SubDMeshFragment. + An n-gon face (EdgeCount()=n and n != 4) is rendered using n ON_SubDMeshFragments. These n fragments correspond + the SubD quads the n-gon face would generate with one level of Catmull-Clark subdivision. + */ + bool GetMeshFragmentPackRectCorners( + bool bGridOrder, + unsigned int fragment_index, + ON_2dPoint mesh_fragment_pack_rect_corners[4] + ) const; + + /* + Description: + Sets PackId() to zero. + Remarks: + Does not change the values of ON_SubDFace::PackRectOrigin(), ON_SubDFace::PackRectSize(), + ON_SubDFace::PackRectRotationDegrees(), or ON_SubDFace::TextureCoordinateType() + Use ON_SubDFace::ClearPackRect() to clear the pack rectangle. + */ + void ClearPackId(); + + /* + Description: + Clears the pack rectangle. + Remarks: + Does not change the value of ON_SubDFace::PackId() or ON_SubDFace::TextureCoordinateType() + Use ON_SubDFace::ClearPackId() to clear the pack id. + */ + void ClearPackRect(); + + /* + Parameters: + pack_rect_origin - [in] + Lower left corner. + Valid origins have (0 <= origin.x < 1) and (0 <= origin.y < 1) + pack_rect_size - [in] + vector from lower left corner to upper right corner. + Valid deltas have (0 < delta.x, 0 < delta.y, (origin.x+delta.x) <= 1) and (origin.y+delta.y) <= 1. + packing_rotation_degrees - [in] + Valid packing_rotation_degrees are a mulitple of 90. + Returns: + True if the input parameters define a valid pack rectangle. + */ + static bool IsValidPackRect( + ON_2dPoint pack_rect_origin, + ON_2dVector pack_rect_size, + int packing_rotation_degrees + ); + + /* + Description: + The ON_SubD::PackFaces() function uses this function to set the value of ON_SubDFace::PackId(). + Unless you are an expert and doing something very carefully and very fancy, to not call this function. + You must also set the pack rectangle correctly. + Remarks: + Faces that share the same PackId() must be neighboring quad faces that form a larger + rectangular grid of quads. Single faces that are not quads and isolated quads cannot + share a pack id with other faces. + */ + void SetPackIdForExperts( + unsigned int pack_id + ); + + /* + Description: + The ON_SubD::PackFaces() function uses this function to set the face's pack rectangle + (ON_SubDFace::PackRectOrigin(), ON_SubDFace::PackRectSize(), ON_SubDFace::PackRectRotationDegrees()). + Unless you are an expert and doing something very carefully and very fancy, to not call this function. + The lower left corner will be origin, the upper right corner will be delta. + You must also set the pack id correctly. + Parameters: + pack_rect_origin - [in] + Lower left corner. + Valid origins have (0 <= origin.x < 1) and (0 <= origin.y < 1) + pack_rect_size - [in] + vector from lower left corner to upper right corner. + Valid deltas have (0 < delta.x, 0 < delta.y, (origin.x+delta.x) <= 1) and (origin.y+delta.y) <= 1. + packing_rotation_degrees - [in] + Valid packing_rotation_degrees are a mulitple of 90. + Return: + True if input is valid and the pack rectangle was set. + False if the input was not vaie and the pack rectangle coordinates were set to nan. + */ + bool SetPackRectForExperts( + ON_2dPoint pack_rect_origin, + ON_2dVector pack_rect_size, + int packing_rotation_degrees + ); + + /* + Description: + Calculate how a packing rectangle assigned to an ON_SubDFace will + be subdivided into n sub packing rectanges for an ngon when n >= 5. + Parameters: + ngon_edge_count - [in] + >= 5. + ngon_face_pack_rect_size - [in] + ngon_face_pack_rect_size.x > 0 and ngon_face_pack_rect_size.y > 0 + The width and height of the sizeof the ngon's entire packing rectangle. + This is typically ON_SubD_Face::PackRectSize() once that property is + coorectly set. + ngon_sub_pack_rect_size - [out] + The size of sub pack rects. + If input is not valid, then ON_2dVector::ZeroVector is returned. + ngon_sub_pack_rect_delta - [out] + The delta from one sub pack rect to the next. + If input is not valid, then ON_2dVector::ZeroVector is returned. + Returns: + When the input is valid, ON_2udex(i,j) is returned and specifies the face's packing + rectangle should be divided into i X j sub packing rectangles. + Otherwise, ON_2udex(0,0) is returned and + */ + static const ON_2udex GetNgonSubPackRectSizeAndDelta( + unsigned int ngon_edge_count, + ON_2dVector ngon_face_pack_rect_size, + ON_2dVector& ngon_sub_pack_rect_size, + ON_2dVector& ngon_sub_pack_rect_delta + ); + + /* + Parameters: + pack_rect_distance_in_pixels - [in] + A (normalized pack rect distance) * ON_SubD::TextureImageSuggestedMinimumSize + Returns: + Suggested gap between adjacent pack rects for a texture images + with width and height >= ON_SubD::TextureImageSuggestedMinimumSize. + This value will be 0.0 when pack_rect_distance_in_pixels is too + small to permit a gap of 1 or more pixels without visibly + adverse effects in a texture map clarity. + */ + static double PackRectGapInPixels( + double pack_rect_distance_in_pixels + ); + private: // The application specifies a base ON_Material used to render the subd this face belongs to. @@ -9696,65 +11253,6 @@ public: */ const ON_Color PerFaceColor() const; - - - -public: - const bool TextureDomainIsSet() const; - - /* - Description: - Set the rectangle in 2d texture space that will be used by this face - for generating default "surface mapping" texture coordinates. - The lower left corner will be origin. - The upper right corner will be delta. - Parameters: - origin - [in] - delta - [in] - >= 0.0; - packing_rotation_degrees - [in] - must be a mulitple of 90. - */ - void SetTextureDomain( - ON_SubDTextureDomainType texture_domain_type, - ON_2dPoint origin, - ON_2dVector delta, - int packing_rotation_degrees - ) const; - - const ON_2dPoint TextureDomainOrigin() const; - - const ON_2dVector TextureDomainDelta() const; - - ON_SubDTextureDomainType TextureDomainType() const; - - /* - Returns: - 0, 90, 180, or 270 - */ - unsigned TextureDomainRotationDegrees() const; - - double TextureDomainRotationRadians() const; - /* - Parameters: - bGridOrder - [in] - false: counter clockwise quad order. - true: fragment grid order - bNormalized - [in] - false: corners of the (x0,x0+dx) x (y0,y0+dy) square are returned where - (x0,y0) = TextureCoordinateDomainOrigin() and - (dx,dy) = TextureCoordinateDomainDelta(). - true: corners of the (0,1) x (0,1) square are returned. - corner_index - [in] - Returns: - Specified texture coordinate corner value. - */ - const ON_2dPoint TextureDomainCorner( - bool bGridOrder, - bool bNormalized, - int corner_index - ) const; - public: unsigned int m_level_zero_face_id = 0; // id of level zero face @@ -9821,18 +11319,84 @@ public: bool IsNotPlanar(double planar_tolerance = ON_ZERO_TOLERANCE) const; +public: + /* + Returns: + Number of texture points that can be set on this face. + Remarks: + To allocate texture point storage, call ON_SubD.AddFaceTexturePointCapacity(this). + */ + unsigned int TexturePointsCapacity() const; + + /* + Returns: + If texture points are set, then true is returned. + Otherwise, false is returned. + */ + bool TexturePointsAreSet() const; + + /* + Description: + Set the texture point at the specified face vertex. + Parameters: + i - [in] + 0 <= 0 < this->EdgeCount() + Parameters: + texture_point - [in] + Returns: + If this->TexturePointsCanBeSet() is true and i is a valid index, + then the texture point is set and true is returned. + Otherwise, false is returned. + Remarks: + To allocate texture point storage, call ON_SubD.AddFaceTexturePointStorage(this). + Texture points are a mutable property on ON_SubDFace. + */ + bool SetTexturePoint( + unsigned i, + ON_3dPoint texture_point + ) const; + + /* + Description: + Remove all texture points. + Remarks: + Texture points are a mutable property on ON_SubDFace. + */ + void ClearTexturePoints() const; + + /* + Description: + Get the texture point at the specified face vertex. + Parameters: + i - [in] + 0 <= 0 < this->EdgeCount() + Returns: + If texture + */ + const ON_3dPoint TexturePoint( + unsigned int i + ) const; + + const ON_3dPoint TextureCenterPoint() const; + +private: + // If m_texture_points is not nullptr, it has capacity 4 + m_edgex_capacity. + // Custom texture coordinates are stored in m_texture_points. + // When texture coodinates are packed or come from a mapping, + // m_texture_points is not used. Typically m_texture_points + // is used when an ON_SubD is created from an ON_Mesh and the mesh + // has custom texture coordinates. Here "custom" means + // not from a mapping. + // https://mcneel.myjetbrains.com/youtrack/issue/RH-59465 + mutable ON_3dPoint* m_texture_points = nullptr; + private: // If 0 != ON_SUBD_CACHE_LIMIT_FLAG(m_saved_points_flags), then - // m_limit_mesh_fragments is a linked list of m_edge_count - // fragments available from MeshFragments() and managed - // by the parent ON_SubD. - // If 0 == ON_SUBD_CACHE_LIMIT_FLAG(m_saved_points_flags), - // then any information in m_limit_mesh_fragments is dirty - // and should not be used. - // ClearSavedSubdivisionPoints() zeros - // the appropriate bit of m_saved_points_flags. - friend class ON_Internal_SubDFaceMeshFragmentAccumulator; - friend class ON_SubDHeap; + // m_mesh_fragments is a linked list of (4==m_edge_count?1:m_edge_count) + // fragments available from MeshFragments() and managed by the parent ON_SubD. + // If 0 == ON_SUBD_CACHE_LIMIT_FLAG(m_saved_points_flags), then any information + // in m_limit_mesh_fragments is dirty and should not be used. + // ClearSavedSubdivisionPoints() zeros the appropriate bit of m_saved_points_flags. // Mesh fragment(s) for this face mutable class ON_SubDMeshFragment* m_mesh_fragments = nullptr; @@ -10121,10 +11685,6 @@ public: ) const; private: - friend class ON_SubDArchiveIdMap; - friend class ON_SubDVertex; - friend class ON_SubDEdge; - void CopyFrom( const ON_SubDFace* src, bool bCopyEdgeArray @@ -11372,6 +12932,7 @@ private: mutable unsigned int m_half_size_fragment_count = 0; }; + ////////////////////////////////////////////////////////////////////////// // // ON_SubDSectorIterator @@ -11387,6 +12948,7 @@ public: ON_SubDSectorIterator(const ON_SubDSectorIterator&) = default; ON_SubDSectorIterator& operator=(const ON_SubDSectorIterator&) = default; + /* Parameters: center_vertex - [in] @@ -11531,13 +13093,13 @@ public: /// /// Sector iteration will terminate when the edge being crossed does not have two faces - /// or that edge is tagged as ON_SubD::EdgeTag::Crease. + /// or that edge is tagged as ON_SubDEdgeTag::Crease. /// AnyCrease = 1, /// /// Sector iteration will terminate when the edge being crossed does not have two faces - /// or that edge is tagged as ON_SubD::EdgeTag::Crease and has no dart vertices. + /// or that edge is tagged as ON_SubDEdgeTag::Crease and has no dart vertices. /// HardCrease = 2, }; @@ -11810,39 +13372,36 @@ public: // No interior creases and no corners. static const ON_SubDFromMeshParameters Smooth; - - // Create an interior sub-D crease along coincident input mesh edges - // where the vertex normal directions at one end differ by at - // least 30 degrees. - static const ON_SubDFromMeshParameters InteriorCreaseAtMeshCrease; - // Create an interior sub-D crease along all coincident input mesh edges. - static const ON_SubDFromMeshParameters InteriorCreaseAtMeshEdge; + + // Create an interior sub-D crease along all input mesh double edges. + static const ON_SubDFromMeshParameters InteriorCreases; + + // Look for convex corners at sub-D vertices with 2 edges + // that have an included angle <= 90 degrees. + static const ON_SubDFromMeshParameters ConvexCornersAndInteriorCreases; /////////////////////////////////////////////////////////////////////////////////////// // // Custom interior crease options // -#pragma region RH_C_SHARED_ENUM [SubD::InteriorCreaseOption] [Rhino.Geometry.SubDCreationOptions.InteriorCreaseOption] [nested:byte] +#pragma region RH_C_SHARED_ENUM [ON_SubDFromMeshParameters::InteriorCreaseOption] [Rhino.Geometry.SubDCreationOptions.InteriorCreaseOption] [nested:byte] /// - /// Defines how interior creases are treated. + /// Specifies the test used to determine when an interior mesh edge generates an interior SubD creased edge. /// enum class InteriorCreaseOption : unsigned char { - ///The interior creases option is not defined. + ///The interior crease test is not defined. Unset = 0, ///No interior creases. None = 1, - ///An interior subd crease will appear along coincident - ///mesh edges where the angle between coindident vertex - ///normals >= MinimumCreaseAngleRadians(). - AtMeshCrease = 2, - - ///An interior subd crease will appear all coincident mesh edges. - ///Input mesh vertex normals are ignored. - AtMeshEdge = 3 + ///An interior mesh double edge will create an interior SubD creased edge. + /// An interior mesh double edge occurs when the sides of two mesh faces have + /// have distinct vertex indices and identical vertex locations. + /// + AtMeshDoubleEdge = 2, }; #pragma endregion @@ -11862,48 +13421,11 @@ public: Returns: The interior crease option. */ - ON_SubDFromMeshParameters::InteriorCreaseOption InteriorCreaseTest() const; - + ON_SubDFromMeshParameters::InteriorCreaseOption GetInteriorCreaseOption() const; /* Description: - When the interior crease option is - ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshCreases, - the value of MinimumCreaseAngleRadians() determines which - coincident input mesh edges generate sub-D creases. - - If the input mesh has vertex normals, and the angle between - vertex normals is > MinimumCreaseAngleRadians() at an end - of a coincident input mesh edge, the the correspondeing sub-D - edge will be a crease. - - Parameters: - minimum_crease_angle_radians - [in] - >= 0.0 and < ON_PI - */ - void SetMinimumCreaseAngleRadians( - double minimum_crease_angle_radians - ); - - /* - Description: - When the interior crease option is - ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshCreases, - the value of MinimumCreaseAngleRadians() determines which - coincident input mesh edges generate sub-D creases. - - If the input mesh has vertex normals, and the angle between - vertex normals is > MinimumCreaseAngleRadians() at an end - of a coincident input mesh edge, the the correspondeing sub-D - edge will be a crease. - Returns: - Current value of - */ - double MinimumCreaseAngleRadians() const; - - /* - Description: - Copy all interior crease option settings from source_options to this. + Copy all settings that control how interior crease edges are created. Parameters: source_options - [in] Returns: @@ -11918,16 +13440,7 @@ public: // // Convex corner options // - - // Look for convex corners at sub-D vertices with 2 edges - // that have an included angle <= 90 degrees. - static const ON_SubDFromMeshParameters ConvexCornerAtMeshCorner; - - /////////////////////////////////////////////////////////////////////////////////////// - // - // Custom convex corner options - // -#pragma region RH_C_SHARED_ENUM [SubD::ConvexCornerOption] [Rhino.Geometry.SubDCreationOptions.ConvexCornerOption] [nested:byte] +#pragma region RH_C_SHARED_ENUM [ON_SubDFromMeshParameters::ConvexCornerOption] [Rhino.Geometry.SubDCreationOptions.ConvexCornerOption] [nested:byte] /// /// Defines how convex corners are treated. /// @@ -11939,7 +13452,7 @@ public: ///No convex coners. None = 1, - ///A convext subd corner will appear at input mesh/ boundary vertices + ///A convex subd corner will appear at input mesh boundary vertices /// where the corner angle <= MaximumConvexCornerAngleRadians() and /// the number of edges the end at the vertex is <= MaximumConvexCornerEdgeCount(). /// @@ -11963,7 +13476,7 @@ public: Returns: The currently selected convex corner option. */ - ON_SubDFromMeshParameters::ConvexCornerOption ConvexCornerTest() const; + ON_SubDFromMeshParameters::ConvexCornerOption GetConvexCornerOption() const; /* Description: @@ -12016,7 +13529,7 @@ public: /* Description: - Copy all convex corner option settings from source_options to this. + Copy all settings that control how convex corner vertices are created. Parameters: source_options - [in] Returns: @@ -12026,6 +13539,110 @@ public: ON_SubDFromMeshParameters source_parameters ); + + /////////////////////////////////////////////////////////////////////////////////////// + // + // Concave corner options + // +#pragma region RH_C_SHARED_ENUM [ON_SubDFromMeshParameters::ConcaveCornerOption] [Rhino.Geometry.SubDCreationOptions.ConcaveCornerOption] [nested:byte] + /// + /// Defines how concave corners are treated. + /// + enum class ConcaveCornerOption : unsigned char + { + ///The option is not set. + Unset = 0, + + ///No concave coners. In general, this is the best choice. + None = 1, + + ///A concave subd corner will appear at input mesh boundary vertices + /// where the corner angle >= MinimumConcaveCornerAngleRadians() and + /// the number of edges the end at the vertex is >= MinimumConcaveCornerEdgeCount(). + /// + AtMeshCorner = 2 + }; +#pragma endregion + + static ON_SubDFromMeshParameters::ConcaveCornerOption ConcaveCornerOptionFromUnsigned( + unsigned int concave_corner_option_as_unsigned + ); + + /* + Parameters: + concave_corner_option - [in] + */ + void SetConcaveCornerOption( + ON_SubDFromMeshParameters::ConcaveCornerOption concave_corner_option + ); + + /* + Returns: + The currently selected concave corner option. + */ + ON_SubDFromMeshParameters::ConcaveCornerOption GetConcaveCornerOption() const; + + /* + Description: + If ConcaveCornerTest() = ConcaveCornerOption::AtMeshConcaveCorner, then an + input mesh boundary vertex becomes a sub-D corner when the number of + edges that end at the vertex is >= MinimumConcaveCornerEdgeCount() edges + and the corner angle is >= MinimumConcaveCornerAngleRadians(). + Parameters: + minimum_concave_corner_edge_count - [in] + */ + void SetMinimumConcaveCornerEdgeCount( + unsigned int minimum_concave_corner_edge_count + ); + + /* + Description: + If ConcaveCornerTest() ConcaveCornerOption::AtMeshConcaveCorner, then an + input mesh boundary vertex becomes a sub-D corner when the number of + edges that end at the vertex is >= MinimumConcaveCornerEdgeCount() edges + and the corner angle is >= MinimumConcaveCornerAngleRadians(). + Returns: + The minimum number of edges at a concave corner vertex. + */ + unsigned int MinimumConcaveCornerEdgeCount() const; + + /* + Description: + If ConcaveCornerTest() ConcaveCornerOption::AtMeshConcaveCorner, then an + input mesh boundary vertex becomes a sub-D corner when the number of + edges that end at the vertex is >= MinimumConcaveCornerEdgeCount() edges + and the corner angle is >= MinimumConcaveCornerAngleRadians(). + Parameters: + minimum_concave_corner_angle_radians - [in] + > ON_PI and <= ON_2PI + */ + void SetMinimumConcaveCornerAngleRadians( + double minimum_concave_corner_angle_radians + ); + + /* + Description: + If ConcaveCornerTest() ConcaveCornerOption::AtMeshConcaveCorner, then an + input mesh boundary vertex becomes a sub-D corner when the number of + edges that end at the vertex is >= MinimumConcaveCornerEdgeCount() edges + and the corner angle is >= MinimumConcaveCornerAngleRadians(). + Returns: + The minimum corner angle. + */ + double MinimumConcaveCornerAngleRadians() const; + + /* + Description: + Copy all settings that control concave corner vertices are created. + Parameters: + source_options - [in] + Returns: + The currently selected concave corner option. + */ + ON_SubDFromMeshParameters::ConcaveCornerOption CopyConcaveCornerTest( + ON_SubDFromMeshParameters source_parameters + ); + /* Returns: false - In ON_SubD::CreateFromMesh(), input mesh vertex locations will be used to set subd vertex control net locations. @@ -12052,6 +13669,76 @@ public: bool bInterpolateMeshVertices ); + + /////////////////////////////////////////////////////////////////////////////////////// + // + // Texture coordinates + // +#pragma region RH_C_SHARED_ENUM [ON_SubDFromMeshParameters::TextureCoordinatesOption] [Rhino.Geometry.SubDCreationOptions.TextureCoordinateOption] [nested:byte] + /// + /// Specifies how texture coordinate information is transfered from the mesh to the SubD. + /// + enum class TextureCoordinatesOption : unsigned char + { + ///The option is not set. + Unset = 0, + + ///No texture coordianate information is transfered from the mesh. + None = 1, + + /// + /// If the mesh has a mapping, then TextureCoordinatesOption::CopyMapping is used. + /// Otherwise if the mesh has texture coordinates, then TextureCoordinatesOption::CopyCoordinates is used. + /// Otherwise TextureCoordinatesOption::Packed is used. + /// + Automatic = 2, + + /// + /// No texture coordianate information is transfered from the mesh. + /// The SubD faces are packed. + /// + Packed = 3, + + ///Texture coordinates mapping information is copied from the mesh. + /// Generally this is the best choice because common mappings, like planar, + /// will appear as most people expect on the SubD. + /// + CopyMapping = 4, + + /// + /// If a mesh has custom texture coordinates, the custom texture coordinates + /// are transfered to the SubD. This requires more memory resources, + /// slows subdivision evaluation, often produces unattractive + /// results on n-gons, and distorts the texture when comes from a common mapping + /// technique, like planar. This option may be useful when the mesh contains only + /// triangles and quads and the custom texture coordinates are of high quality. + /// + CopyCoordinates = 5, + }; +#pragma endregion + + + /* + Description: + Set the texture coordinates option. + Parameters: + texture_coordinates_opton - [in] + */ + void SetTextureCoordinatesOption( + ON_SubDFromMeshParameters::TextureCoordinatesOption texture_coordinates_opton + ); + + /* + Returns: + The texture coorindates option. + */ + ON_SubDFromMeshParameters::TextureCoordinatesOption GetTextureCoordinatesOption() const; + + static ON_SubDFromMeshParameters::TextureCoordinatesOption TextureCoordinatesOptionFromUnsigned( + unsigned int texture_coordinates_opton_as_unsigned + ); + + /* Returns: true - In ON_SubD::CreateFromMesh(), colinear boundary edges belonging to the same face are merged into a single edge. @@ -12107,13 +13794,20 @@ private: ON_SubDFromMeshParameters::InteriorCreaseOption m_interior_crease_option = ON_SubDFromMeshParameters::InteriorCreaseOption::None; ON_SubDFromMeshParameters::ConvexCornerOption m_convex_corner_option = ON_SubDFromMeshParameters::ConvexCornerOption::None; + ON_SubDFromMeshParameters::ConcaveCornerOption m_concave_corner_option = ON_SubDFromMeshParameters::ConcaveCornerOption::None; + ON_SubDFromMeshParameters::TextureCoordinatesOption m_texture_coordinates_option = ON_SubDFromMeshParameters::TextureCoordinatesOption::None; unsigned short m_maximum_convex_corner_edge_count = 2U; - unsigned short m_reserved3 = 0; + unsigned short m_minimum_concave_corner_edge_count = 4U; - double m_minimum_crease_angle_radians = ON_PI/6.0; // 30 degrees in radians - double m_maximum_convex_corner_angle_radians = 0.501*ON_PI; // 90 degrees (+ a smidge) in radians + unsigned short m_reserved2 = 0; + unsigned int m_reserved3 = 0; + + double m_reserved4 = 0.0; + + double m_maximum_convex_corner_angle_radians = 120.0 * ON_DEGREES_TO_RADIANS; // 120 degrees + double m_minimum_concave_corner_angle_radians = 240.0 * ON_DEGREES_TO_RADIANS; // 240 degrees }; ////////////////////////////////////////////////////////////////////////// @@ -12344,7 +14038,7 @@ public: Returns: Number of vertices with the specified tag. */ - int VertexCount(ON_SubD::VertexTag vertex_tag) const; + int VertexCount(ON_SubDVertexTag vertex_tag) const; /* Returns: @@ -12356,7 +14050,7 @@ public: Returns: Number of edges with the specified tag. */ - int EdgeCount(ON_SubD::EdgeTag edge_tag) const; + int EdgeCount(ON_SubDEdgeTag edge_tag) const; /* @@ -12426,6 +14120,46 @@ public: const ON_SubDComponentPoint* a, const ON_SubDComponentPoint* b ); + + /* + Description: + Compare the pick events to determine the component the user was most likely aiming at. + + Parameters: + pick_type - [in] + When pick_type is ON_PickType::PointPick, several biases may be applied to favor vertices and edges. + + vertex_depth_bias - [in] + When in doubt pass 0.0. + A positive value increases vertex bias in some situations; otherwise vertex_depth_bias is ignored. + When pick_type is ON_PickType::PointPick and either + an edge and a vertex of that edge or a face and a vertex of that face are being compared, + then then vertex_depth_bias is added to the vertex hit depth before comparing depths. + When the pick is happening in a perspective view, it is important to choose a vertex_depth_bias + appropriate for the depth in the view frustum. + + edge_depth_bias - [in] + When in doubt pass 0.0. + A positive value increases edge bias in some situations; otherwise vertex_depth_bias is ignored. + When pick_type is ON_PickType::PointPick and a face and an edge of that face are being compared, + then then edge_depth_bias is added to the edge hit depth before comparing depths. + When the pick is happening in a perspective view, it is important to choose an edge_depth_bias + appropriate for the depth in the view frustum. + + A - [in] + B - [in] + Returns: + A pick point for the component the user was most likely aiming at + with distance and depth settings that will lead to conistent improvment + if more than two points are being compared. + */ + static const ON_SubDComponentPoint BestPickPoint( + ON_PickType pick_type, + double vertex_depth_bias, + double edge_depth_bias, + const ON_SubDComponentPoint& A, + const ON_SubDComponentPoint& B + ); // m_component_ptr will be face, edge or vertex ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null; @@ -12858,13 +14592,13 @@ public: Parameters: v0 - [in] v0_sector_weight - [in] - If v0 null or ON_SubD::VertexTag::Smooth == v0->m_vertex_tag, and v1 is null or tagged, + If v0 null or ON_SubDVertexTag::Smooth == v0->m_vertex_tag, and v1 is null or tagged, then m_sector_weight[0] is set to v0_sector_weight. In all other cases the value of v0_sector_weight is ignored and m_sector_weight[0] is set to ON_SubDSectorType::IgnoredSectorCoefficient. v1 - [in] v1_sector_weight - [in] - If v1 null or ON_SubD::VertexTag::Smooth == v1->m_vertex_tag, and v0 is null or tagged, + If v1 null or ON_SubDVertexTag::Smooth == v1->m_vertex_tag, and v0 is null or tagged, then m_sector_weight[1] is set to v1_sector_weight. In all other cases the value of v1_sector_weight is ignored and m_sector_weight[1] is set to ON_SubDSectorType::IgnoredSectorCoefficient. @@ -12873,8 +14607,8 @@ public: The vertex parameter information is used to set the ON_SubDEdge.m_vertex[] and ON_SubDEdge.m_sector_weight[] values. If v0 and v1 are not null and both are tagged, then ON_SubDEdge.m_edge_tag is - set to ON_SubD::EdgeTag::Crease. - In all other cases, ON_SubDEdge.m_edge_tag is set to ON_SubD::EdgeTag::Smooth. + set to ON_SubDEdgeTag::Crease. + In all other cases, ON_SubDEdge.m_edge_tag is set to ON_SubDEdgeTag::Smooth. If v0 or v1 is not null, then ON_SubDEdge.m_level is set to the maximum of v0->m_level or v1->m_level. */ @@ -13678,15 +15412,15 @@ public: void ClearFaceTopologyFilter(); - bool AcceptVertexTag(ON_SubD::VertexTag vertex_tag) const; + bool AcceptVertexTag(ON_SubDVertexTag vertex_tag) const; - void AddAcceptedVertexTag(ON_SubD::VertexTag vertex_tag); + void AddAcceptedVertexTag(ON_SubDVertexTag vertex_tag); void ClearVertexTagFilter(); - bool AcceptEdgeTag(ON_SubD::EdgeTag edge_tag) const; + bool AcceptEdgeTag(ON_SubDEdgeTag edge_tag) const; - void AddAcceptedEdgeTag(ON_SubD::EdgeTag edge_tag); + void AddAcceptedEdgeTag(ON_SubDEdgeTag edge_tag); void ClearEdgeTagFilter(); @@ -13704,11 +15438,11 @@ public: private: bool m_bRejectVertices = false; ON_SubDComponentFilter::Topology m_vertex_topology_filter = ON_SubDComponentFilter::Topology::Unset; - ON_SubD::VertexTag m_vertex_tag_filter[4] = {}; + ON_SubDVertexTag m_vertex_tag_filter[4] = {}; bool m_bRejectEdges = false; ON_SubDComponentFilter::Topology m_edge_topology_filter = ON_SubDComponentFilter::Topology::Unset; - ON_SubD::EdgeTag m_edge_tag_filter[2] = {}; + ON_SubDEdgeTag m_edge_tag_filter[2] = {}; bool m_bRejectFaces = false; ON_SubDComponentFilter::Topology m_face_topology_filter = ON_SubDComponentFilter::Topology::Unset; @@ -13717,6 +15451,8 @@ private: }; + + #if defined(ON_COMPILING_OPENNURBS) /* The ON_SubDAsUserData class is used to attach a subd to it proxy mesh diff --git a/opennurbs_subd_archive.cpp b/opennurbs_subd_archive.cpp index fc29dade..d0546ed7 100644 --- a/opennurbs_subd_archive.cpp +++ b/opennurbs_subd_archive.cpp @@ -964,24 +964,24 @@ bool ON_SubDFace::Write( } // write 34 byte texture domain - const bool bWriteTextureDomain = TextureDomainIsSet(); - if (false == Internal_WriteComponentAdditionSize(bWriteTextureDomain, archive, 34)) + const bool bWritePackRect = PackRectIsSet(); + if (false == Internal_WriteComponentAdditionSize(bWritePackRect, archive, 34)) break; - if (bWriteTextureDomain) + if (bWritePackRect) { - const unsigned char domain_type = static_cast(TextureDomainType()); - if (!archive.WriteChar(domain_type)) + const unsigned char obsolete_per_face_texture_coordinate_type = ON_SubD::ObsoleteTextureDomainTypeFromTextureCoordinateType(ON_SubDTextureCoordinateType::Packed); + if (!archive.WriteChar(obsolete_per_face_texture_coordinate_type)) break; - const unsigned packing_rot = TextureDomainRotationDegrees(); + const unsigned packing_rot = PackRectRotationDegrees(); const unsigned char packing_rot_dex = (unsigned char)(packing_rot/90U); if (!archive.WriteChar(packing_rot_dex)) break; - const ON_2dPoint texture_domain_origin = TextureDomainOrigin(); - if (!archive.WriteDouble(2, &texture_domain_origin.x)) + const ON_2dPoint pack_rect_origin = PackRectOrigin(); + if (!archive.WriteDouble(2, &pack_rect_origin.x)) break; - const ON_2dVector texture_domain_delta = TextureDomainDelta(); - if (!archive.WriteDouble(2, &texture_domain_delta.x)) + const ON_2dVector pack_rect_size = PackRectSize(); + if (!archive.WriteDouble(2, &pack_rect_size.x)) break; } @@ -1007,6 +1007,54 @@ bool ON_SubDFace::Write( break; } + // PackId + const unsigned pack_id = PackId(); + const bool bPackId = (pack_id > 0U); + if (false == Internal_WriteComponentAdditionSize(bPackId, archive, 4)) + break; + if (bPackId) + { + if (!archive.WriteInt(pack_id)) + break; + } + + // Custom texture coordintes + const bool bTexturePoints = this->TexturePointsAreSet(); + if (false == Internal_WriteComponentAdditionSize(bTexturePoints, archive, 4)) + break; + if (bTexturePoints) + { + // The number of texture points varies from face to face (EdgeCount()). + // The maximum size that can be saved in a single component addition is 253 bytes. + // So, texture points are saved in 10 point chunks ( 10*sizeof(ON_3dPoint) = 240 <= 243. + const unsigned texture_point_count = this->EdgeCount(); + const unsigned ten_point_chunk_count = texture_point_count / 10; + const unsigned left_over_points_count = texture_point_count % 10; + const ON_3dPoint* a = this->m_texture_points; + const unsigned char sizeof_ten_points = (unsigned char)(10 * sizeof(ON_3dPoint)); // sizeof_ten_points = 240 <= 243 + bool bContinue = archive.WriteInt(ten_point_chunk_count); + if ( false == bContinue) + break; + + // write the 10 point chunks + for (unsigned i = 0; bContinue && i < ten_point_chunk_count; ++i) + { + bContinue = Internal_WriteComponentAdditionSize(true, archive, sizeof_ten_points); + bContinue = bContinue && archive.WriteDouble(30, (const double*)a); + a += 10; + } + + // write the "left over" points + if (bContinue && left_over_points_count > 0) + { + const unsigned char sizeof_left_over_points = (unsigned char)(left_over_points_count * sizeof(ON_3dPoint)); // sizeof_left_over_points < 240 + bContinue = Internal_WriteComponentAdditionSize(true, archive, sizeof_left_over_points); + bContinue = bContinue && archive.WriteDouble(3* left_over_points_count, (const double*)a); + } + if (false == bContinue) + break; + } + return Internal_FinishWritingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); @@ -1016,14 +1064,14 @@ bool ON_SubDFace::Read( class ON_BinaryArchive& archive, class ON_SubD& subd, class ON_SubDFace*& face - ) +) { face = nullptr; for (;;) { ON_SubDimple* subdimple = const_cast(subd.SubDimple()); - if ( nullptr == subdimple) + if (nullptr == subdimple) break; ON_SubDComponentBase base = ON_SubDComponentBase::Unset; @@ -1031,7 +1079,7 @@ bool ON_SubDFace::Read( unsigned int obsolete_parent_face_id = 0; unsigned short edge_count = 0; - if (!ReadBase(archive,base)) + if (!ReadBase(archive, base)) break; if (!archive.ReadInt(&level_zero_face_id)) break; @@ -1044,19 +1092,19 @@ bool ON_SubDFace::Read( base.m_id, // serialzation must preserve ON_SubDFace.m_id base.SubdivisionLevel(), edge_count - ); + ); - if ( nullptr == f ) + if (nullptr == f) break; f->ON_SubDComponentBase::operator=(base); f->m_level_zero_face_id = level_zero_face_id; - if (!ReadEdgePtrList(archive,edge_count,sizeof(f->m_edge4)/sizeof(f->m_edge4[0]),f->m_edge4,f->m_edgex_capacity,f->m_edgex)) + if (!ReadEdgePtrList(archive, edge_count, sizeof(f->m_edge4) / sizeof(f->m_edge4[0]), f->m_edge4, f->m_edgex_capacity, f->m_edgex)) break; f->m_edge_count = edge_count; - + face = f; if (archive.Archive3dmVersion() < 70) @@ -1069,29 +1117,31 @@ bool ON_SubDFace::Read( // read additions unsigned char sz; - + sz = 0; if (false == Internal_ReadComponentAdditionSize(archive, 34, &sz)) break; if (ON_SubDComponentArchiveAdditionEndMark == sz) return true; // end of additions - if ( 0 != sz) + if (0 != sz) { // 34 bytes of texture domain information - unsigned char domain_type; - if (!archive.ReadChar(&domain_type)) + unsigned char obsolete_per_face_texture_coordinate_type = 0; + if (!archive.ReadChar(&obsolete_per_face_texture_coordinate_type)) break; unsigned char packing_rot_dex = 0; if (!archive.ReadChar(&packing_rot_dex)) break; const unsigned packing_rot = ((unsigned int)packing_rot_dex) * 90U; - ON_2dPoint texture_domain_origin(ON_2dPoint::Origin); - if (!archive.ReadDouble(2, &texture_domain_origin.x)) + ON_2dPoint pack_rect_origin(ON_2dPoint::Origin); + if (!archive.ReadDouble(2, &pack_rect_origin.x)) break; - ON_2dVector texture_domain_delta(ON_2dVector::ZeroVector); - if (!archive.ReadDouble(2, &texture_domain_delta.x)) + ON_2dVector pack_rect_delta(ON_2dVector::ZeroVector); + if (!archive.ReadDouble(2, &pack_rect_delta.x)) break; - f->SetTextureDomain(ON_SubD::TextureDomainTypeFromUnsigned(domain_type), texture_domain_origin, texture_domain_delta, packing_rot); + + if (ON_SubDFace::IsValidPackRect(pack_rect_origin, pack_rect_delta, packing_rot) ) + f->SetPackRectForExperts(pack_rect_origin, pack_rect_delta, packing_rot); } sz = 0; @@ -1122,6 +1172,85 @@ bool ON_SubDFace::Read( f->SetPerFaceColor(per_face_color); } + + // PackId + sz = 0; + if (false == Internal_ReadComponentAdditionSize(archive, 4, &sz)) + break; + if (ON_SubDComponentArchiveAdditionEndMark == sz) + return true; // end of additions + if (0 != sz) + { + // 4 bytes of pack id + unsigned pack_id = 0U; + if (false == archive.ReadInt(&pack_id)) + break; + f->m_pack_id = pack_id; + } + + + // Custom texture coordintes + sz = 0; + if (false == Internal_ReadComponentAdditionSize(archive, 4, &sz)) + break; + if (ON_SubDComponentArchiveAdditionEndMark == sz) + return true; // end of additions + if (0 != sz) + { + // The number of texture points varies from face to face (EdgeCount()). + // The maximum size that can be saved in a single component addition is 253 bytes. + // So, texture points are saved in 10 point chunks ( 10*sizeof(ON_3dPoint) = 240 <= 243. + const unsigned texture_point_count = f->EdgeCount(); + unsigned ten_point_chunk_count = 0xFFFFFFFFU; + if (false == archive.ReadInt(&ten_point_chunk_count)) + break; + if (ten_point_chunk_count != texture_point_count / 10) + break; + const unsigned left_over_points_count = texture_point_count % 10; + const unsigned char sizeof_ten_points = (unsigned char)(10 * sizeof(ON_3dPoint)); // sizeof_ten_points = 240 <= 243 + ON_3dPoint a[10]; + bool bContinue = true; + + // Even if allocaion fails, we need to read the points so we can get get + // future information that is after the points out of the archive. + subdimple->AllocateFaceTexturePoints(f); + ON_3dPoint* tp = f->m_texture_points; + + // read the 10 point chunks + for (unsigned i = 0; bContinue && i < ten_point_chunk_count; ++i) + { + sz = 0; + bContinue = Internal_ReadComponentAdditionSize(archive, sizeof_ten_points, &sz); + bContinue = bContinue && (sizeof_ten_points == sz); + bContinue = bContinue && archive.ReadDouble(30, (double*)a); + if (bContinue && nullptr != tp) + { + for (unsigned j = 0; j < 10; ++j) + *tp++ = a[j]; + } + } + + // read the "left over" points. + if (bContinue && left_over_points_count > 0) + { + const unsigned char sizeof_left_over_points = (unsigned char)(left_over_points_count * sizeof(ON_3dPoint)); // sizeof_left_over_points < 240 + sz = 0; + bContinue = Internal_ReadComponentAdditionSize(archive, sizeof_left_over_points, &sz); + bContinue = bContinue && (sizeof_left_over_points == sz); + bContinue = bContinue && archive.ReadDouble(3 * left_over_points_count, (double*)a); + if (bContinue && nullptr != tp) + { + for (unsigned j = 0; j < left_over_points_count; ++j) + *tp++ = a[j]; + } + } + if (false == bContinue) + break; + if ( nullptr != tp) + f->m_texture_status_bits |= ON_SubDFace::TextureStatusBits::TexturePointsSet; + } + + return Internal_FinishReadingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); @@ -1532,8 +1661,8 @@ bool ON_SubDimple::Write( } // minor version = 1 addtions - const unsigned char texture_domain_type = static_cast(TextureDomainType()); - if (false == archive.WriteChar(texture_domain_type)) + const unsigned char obsolete_texture_domain_type = ON_SubD::ObsoleteTextureDomainTypeFromTextureCoordinateType(TextureCoordinateType()); + if (false == archive.WriteChar(obsolete_texture_domain_type)) break; if (false == m_texture_mapping_tag.Write(archive)) @@ -1544,7 +1673,7 @@ bool ON_SubDimple::Write( break; // runtime content number used to compare with the on on m_symmetry - if (false == archive.WriteBigInt(ContentSerialNumber())) + if (false == archive.WriteBigInt(GeometryContentSerialNumber())) break; rc = true; @@ -1616,10 +1745,10 @@ bool ON_SubDimple::Read( if (minor_version >= 1) { - unsigned char texture_domain_type = static_cast(ON_SubDTextureDomainType::Unset); - if (false == archive.ReadChar(&texture_domain_type)) - break; - m_texture_domain_type = ON_SubD::TextureDomainTypeFromUnsigned(texture_domain_type); + unsigned char obsolete_texture_domain_type = 0; + if (false == archive.ReadChar(&obsolete_texture_domain_type)) + break; + m_texture_coordinate_type = ON_SubD::TextureCoordinateTypeFromObsoleteTextureDomainType(obsolete_texture_domain_type); if (false == m_texture_mapping_tag.Read(archive)) break; @@ -1656,10 +1785,55 @@ bool ON_SubDimple::Read( && saved_content_serial_number == m_symmetry.SymmetricObjectContentSerialNumber() ; - ChangeContentSerialNumber(false); + if (archive.ArchiveOpenNURBSVersion() < 2382394661) + { + // try to get texture information set correctly when it comes from old files + const ON_MappingTag file_tag = this->TextureMappingTag(true); + ON_MappingTag new_tag = file_tag; + + const ON_SubDTextureCoordinateType file_type = this->TextureCoordinateType(); + ON_SubDTextureCoordinateType new_type = file_type; + + if (ON_TextureMapping::TYPE::srfp_mapping == file_tag.m_mapping_type) + { + new_tag = ON_MappingTag::SurfaceParameterMapping; + if (ON_SubDTextureCoordinateType::FromMapping == file_type || ON_SubDTextureCoordinateType::Unset == file_type) + new_type = ON_SubDTextureCoordinateType::Packed; + } + else + { + const bool bTagIsSet + = ON_TextureMapping::TYPE::srfp_mapping != file_tag.m_mapping_type + && ON_TextureMapping::TYPE::no_mapping != file_tag.m_mapping_type + && file_tag.IsSet() + ; + if (ON_SubDTextureCoordinateType::Unset == file_type) + { + if (bTagIsSet) + new_type = ON_SubDTextureCoordinateType::FromMapping; + else + new_tag = ON_MappingTag::Unset; + } + else if (ON_SubDTextureCoordinateType::FromMapping == file_type) + { + if (false == bTagIsSet) + { + new_tag = ON_MappingTag::Unset; + new_type = ON_SubDTextureCoordinateType::Packed; + } + } + } + + if (0 != ON_MappingTag::CompareAll(file_tag, new_tag)) + this->SetTextureMappingTag(new_tag); + if (file_type != new_type) + this->SetTextureCoordinateType(new_type); + } + + ChangeGeometryContentSerialNumber(false); if ( bSetSymmetricObjectContentSerialNumber ) - m_symmetry.SetSymmetricObjectContentSerialNumber(ContentSerialNumber()); + m_symmetry.SetSymmetricObjectContentSerialNumber(GeometryContentSerialNumber()); else m_symmetry.ClearSymmetricObjectContentSerialNumber(); @@ -1810,7 +1984,7 @@ ON_Mesh* ON_SubDMeshProxyUserData::MeshProxyFromSubD( subd_copy = new ON_SubD(*subd); if (nullptr == subd_copy) break; - mesh = subd_copy->GetControlNetMesh(nullptr); + mesh = subd_copy->GetControlNetMesh(nullptr, ON_SubDGetControlNetMeshPriority::Geometry); if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh)) break; ON_SubDMeshProxyUserData* ud = new ON_SubDMeshProxyUserData(); diff --git a/opennurbs_subd_copy.cpp b/opennurbs_subd_copy.cpp index 41e43b27..335c3881 100644 --- a/opennurbs_subd_copy.cpp +++ b/opennurbs_subd_copy.cpp @@ -177,7 +177,7 @@ ON_SubDFace* ON_SubDArchiveIdMap::CopyFace( { if ( nullptr == source_face ) return ON_SUBD_RETURN_ERROR(nullptr); - ON_SubDFace* face = subdimple.AllocateFace( source_face->m_id, source_face->m_level,source_face->m_edge_count); + ON_SubDFace* face = subdimple.AllocateFace( source_face->m_id, source_face->m_level,source_face->m_edge_count, source_face->TexturePointsAreSet()); if ( nullptr == face ) return ON_SUBD_RETURN_ERROR(nullptr); @@ -640,9 +640,14 @@ void ON_SubD::CopyHelper(const ON_SubD& src) subdimple->SetManagedMeshSubDWeakPointers(m_subdimple_sp); } -ON__UINT64 ON_SubDimple::ContentSerialNumber() const +ON__UINT64 ON_SubDimple::GeometryContentSerialNumber() const { - return m_subd_content_serial_number; + return m_subd_geometry_content_serial_number; +} + +ON__UINT64 ON_SubDimple::RenderContentSerialNumber() const +{ + return m_subd_render_content_serial_number; } ON__UINT64 ON_SubDimple::ComponentStatusSerialNumber() const @@ -655,24 +660,31 @@ const ON_AggregateComponentStatusEx ON_SubDimple::AggregateComponentStatus() con return ActiveLevel().AggregateComponentStatus(); } -ON__UINT64 ON_SubDimple::ChangeContentSerialNumber( +ON__UINT64 ON_SubDimple::ChangeGeometryContentSerialNumber( bool bChangePreservesSymmetry ) const { const bool bUpdateSymmetricObjectContentSerialNumber = bChangePreservesSymmetry - && m_subd_content_serial_number > 0 + && m_subd_geometry_content_serial_number > 0 && m_symmetry.IsSet() - && m_subd_content_serial_number == m_symmetry.SymmetricObjectContentSerialNumber() + && m_subd_geometry_content_serial_number == m_symmetry.SymmetricObjectContentSerialNumber() ; - m_subd_content_serial_number = ON_NextContentSerialNumber(); + m_subd_geometry_content_serial_number = ON_NextContentSerialNumber(); + m_subd_render_content_serial_number = m_subd_geometry_content_serial_number; // changing content automatically changes render content. if (bUpdateSymmetricObjectContentSerialNumber) - m_symmetry.SetSymmetricObjectContentSerialNumber(m_subd_content_serial_number); + m_symmetry.SetSymmetricObjectContentSerialNumber(m_subd_geometry_content_serial_number); else m_symmetry.ClearSymmetricObjectContentSerialNumber(); - return m_subd_content_serial_number; + return m_subd_geometry_content_serial_number; +} + +ON__UINT64 ON_SubDimple::ChangeRenderContentSerialNumber() const +{ + m_subd_render_content_serial_number = ON_NextContentSerialNumber(); + return m_subd_render_content_serial_number; } void ON_SubDimple::SetManagedMeshSubDWeakPointers( @@ -756,15 +768,15 @@ ON_SubDimple::ON_SubDimple(const ON_SubDimple& src) } m_subd_appearance = src.m_subd_appearance; - m_texture_domain_type = src.m_texture_domain_type; + m_texture_coordinate_type = src.m_texture_coordinate_type; m_texture_mapping_tag = src.m_texture_mapping_tag; m_symmetry = src.m_symmetry; - ChangeContentSerialNumber(false); + ChangeGeometryContentSerialNumber(false); - if (src.m_subd_content_serial_number == src.m_symmetry.SymmetricObjectContentSerialNumber()) - m_symmetry.SetSymmetricObjectContentSerialNumber(ContentSerialNumber()); + if (src.m_subd_geometry_content_serial_number == src.m_symmetry.SymmetricObjectContentSerialNumber()) + m_symmetry.SetSymmetricObjectContentSerialNumber(GeometryContentSerialNumber()); else m_symmetry.ClearSymmetricObjectContentSerialNumber(); } diff --git a/opennurbs_subd_data.cpp b/opennurbs_subd_data.cpp index 1febaec1..32506b71 100644 --- a/opennurbs_subd_data.cpp +++ b/opennurbs_subd_data.cpp @@ -46,7 +46,7 @@ ON_SubDimple::~ON_SubDimple() void ON_SubDimple::Clear() { m_subd_appearance = ON_SubD::DefaultSubDAppearance; - m_texture_domain_type = ON_SubDTextureDomainType::Unset; + m_texture_coordinate_type = ON_SubDTextureCoordinateType::Unset; m_texture_mapping_tag = ON_MappingTag::Unset; for (unsigned i = 0; i < m_levels.UnsignedCount(); ++i) delete m_levels[i]; @@ -63,7 +63,7 @@ void ON_SubDimple::ClearLevelContents( return; if (level == m_active_level) - ChangeContentSerialNumber(false); + ChangeGeometryContentSerialNumber(false); level->ResetFaceArray(); level->ResetEdgeArray(); @@ -115,7 +115,7 @@ unsigned int ON_SubDimple::ClearHigherSubdivisionLevels( if ( level_count > max_level_index ) { m_active_level = m_levels[max_level_index]; - ChangeContentSerialNumber(false); + ChangeGeometryContentSerialNumber(false); } } @@ -156,7 +156,7 @@ unsigned int ON_SubDimple::ClearLowerSubdivisionLevels( if (nullptr != m_active_level && m_active_level->m_level_index < min_level_index) { m_active_level = m_levels[min_level_index]; - ChangeContentSerialNumber(false); + ChangeGeometryContentSerialNumber(false); } for ( unsigned int level_index = 0; level_index < min_level_index; level_index++) @@ -223,7 +223,8 @@ void ON_SubDimple::Destroy() } m_levels.Destroy(); m_heap.Destroy(); - m_subd_content_serial_number = 0; + m_subd_geometry_content_serial_number = 0; + m_subd_render_content_serial_number = 0; } ON_SubDLevel* ON_SubDimple::ActiveLevel(bool bCreateIfNeeded) @@ -232,7 +233,7 @@ ON_SubDLevel* ON_SubDimple::ActiveLevel(bool bCreateIfNeeded) { unsigned int level_index = (m_levels.UnsignedCount() > 0) ? (m_levels.UnsignedCount()-1) : 0U; m_active_level = SubDLevel(level_index,bCreateIfNeeded && 0 == m_levels.UnsignedCount()); - ChangeContentSerialNumber(false); + ChangeGeometryContentSerialNumber(false); } return m_active_level; } @@ -253,7 +254,7 @@ class ON_SubDLevel* ON_SubDimple::SubDLevel( if (nullptr == m_active_level) { m_active_level = level; - ChangeContentSerialNumber(false); + ChangeGeometryContentSerialNumber(false); } } @@ -625,7 +626,7 @@ bool ON_SubDVertex::Transform( // TODO: // If the vertex - // is tagged as ON_SubD::VertexTag::Corner + // is tagged as ON_SubDVertexTag::Corner // and bTransformationSavedSubdivisionPoint is true, // and the corner sector(s) contains interior smooth edges, // and the transformation changes the angle between a corner sector's crease boundary, @@ -815,7 +816,7 @@ bool ON_SubDimple::Transform( const ON_Xform& xform ) { - const ON__UINT64 content_serial_number0 = ContentSerialNumber(); + const ON__UINT64 geometry_content_serial_number0 = GeometryContentSerialNumber(); if (false == xform.IsValid()) return false; @@ -865,7 +866,7 @@ bool ON_SubDimple::Transform( const ON_Symmetry symmetry0 = m_symmetry; m_symmetry = m_symmetry.TransformConditionally(xform); bool bSetContentSerialNumber = false; - if (content_serial_number0 == symmetry0.SymmetricObjectContentSerialNumber()) + if (geometry_content_serial_number0 == symmetry0.SymmetricObjectContentSerialNumber()) { // see if the transformed object will still be symmetric. if (ON_Symmetry::Coordinates::Object == m_symmetry.SymmetryCoordinates()) @@ -881,7 +882,7 @@ bool ON_SubDimple::Transform( } } if (bSetContentSerialNumber) - m_symmetry.SetSymmetricObjectContentSerialNumber(ContentSerialNumber()); + m_symmetry.SetSymmetricObjectContentSerialNumber(GeometryContentSerialNumber()); else m_symmetry.ClearSymmetricObjectContentSerialNumber(); } diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h index 783001b4..6dc05434 100644 --- a/opennurbs_subd_data.h +++ b/opennurbs_subd_data.h @@ -137,7 +137,7 @@ public: // true if the limit surface of the center quad is a smooth bi-cubic surface with CVs // at the m_vertex_grid[][] locations. bool m_bIsCubicPatch = false; - unsigned char m_initial_subdivision_level = 0; // parent SubD subdivsion level at source + unsigned char m_initial_subdivision_level = 0; // Subdivsion level of the quad from the original SubD. (When the original SubD face is an n-gon, m_initial_subdivision_level=1) unsigned char m_current_subdivision_level = 0; // current subdivision level of contents // m_face_grid[1][1] is extraordinary and interpolation through the limit point @@ -154,19 +154,10 @@ public: bool m_bExactQuadrantPatch[4] = {}; // m_bBoundaryCrease[] = true if the corresponding m_center_edges[] is a crease - // and both end vertices are not darts. + // AND neither end vertex is a dart. unsigned char m_boundary_crease_count = 0; bool m_bBoundaryCrease[4] = {}; - // If m_bCenterEdgeLimitPoint[n] is true, then m_center_edge0_limit_point[n] is set. - bool m_bCenterEdgeLimitPoint[4] = {}; - - // If ON_SubDQuadNeighborhood is being used to calculate NURBS patches, - // and there is a single exceptional crease vertex, - // then m_center_edge_limit_point[n] = limit point of the middle of m_center_edges[n] - // This information is needed to get NURBS patches near exceptional crease vertices. - ON_SubDSectorSurfacePoint m_center_edge_limit_point[4]; - public: const ON_SubDVertex* m_vertex_grid[4][4] = {}; // vertex net m_quad_face corners = ([1][1], [2][1], [2][2], [1][2]) @@ -214,7 +205,7 @@ public: /* Parameters: vertex_tag_filter - [in] - If vertex_tag_filter is not ON_SubD::VertexTag::Unset, then the + If vertex_tag_filter is not ON_SubDVertexTag::Unset, then the indexed vertex must have the specified tag. minimum_edge_count_filter - [in] The index vertex must have at least this many edges. @@ -226,7 +217,7 @@ public: ON_UNSET_UINT_INDEX (otherwise) */ unsigned int ExtraordinaryCenterVertexIndex( - ON_SubD::VertexTag vertex_tag_filter, + ON_SubDVertexTag vertex_tag_filter, unsigned int minimum_edge_count_filter ) const; @@ -312,7 +303,8 @@ public: private: unsigned int SetLimitSubSurfaceExactCVs( - unsigned int quadrant_index // 0,1,2,3 sets quadrant, 4 sets all non extraordinary patches + bool bEnableApproximatePatch, + unsigned int quadrant_index // 0,1,2,3 sets quadrant, 4 sets all non extraordinary patches, ); private: @@ -1101,7 +1093,7 @@ public: unsigned int UpdateEdgeSectorCoefficients( bool bUnsetEdgeSectorCoefficientsOnly - ); + ) const; /* Description: @@ -1163,8 +1155,8 @@ public: void ClearEvaluationCache() const; - void ClearNeighborhoodEvaluationCache(const ON_SubDVertex * vertex, bool bTagChanged) const; - + bool CopyEvaluationCacheForExperts(const ON_SubDLevel& src, class ON_SubDHeap& this_heap); + void ClearTopologicalAttributes() const { m_aggregates.ClearTopologicalAttributes(); @@ -1440,6 +1432,11 @@ public: const class ON_SubDMeshFragment& src_fragment ); + class ON_SubDMeshFragment* CopyMeshFragments( + const class ON_SubDFace* source_face, + const class ON_SubDFace* destination_face + ); + /* Parameters: fragment - [in] @@ -1462,7 +1459,7 @@ public: /* Description: - Allocates limit curves reference by ON_SubDEdge.m_limit_curve + Allocates limit curves reference by ON_SubDEdge.m_limit_curve for edges in this SubD. Parameters: cv_capacity - [in] @@ -1472,6 +1469,11 @@ public: unsigned int cv_capacity ); + class ON_SubDEdgeSurfaceCurve* CopyEdgeSurfaceCurve( + const class ON_SubDEdge* source_edge, + const class ON_SubDEdge* desination_edge + ); + /* Parameters: limit_curve - [in] @@ -1491,6 +1493,13 @@ public: const class ON_SubDEdge* edge ); + unsigned int AllocateFaceTexturePoints( + const class ON_SubDFace* face + ); + + void ReturnFaceTexturePoints( + const class ON_SubDFace* face + ); /* @@ -1612,7 +1621,17 @@ private: static size_t OversizedElementCapacity( size_t count ); + class ON_3dPoint* Allocate3dPointArray( + size_t point_capacity + ); + void Return3dPointArray( + class ON_3dPoint* point_array + ); +public: + static unsigned int Managed3dPointArrayCapacity(class ON_3dPoint* point_array); + +private: static size_t m_offset_vertex_id; static size_t m_offset_edge_id; static size_t m_offset_face_id; @@ -1647,7 +1666,7 @@ public: A runtime serial number that is incremented every time a the active level, vertex location, vertex or edge flag, or subd topology is chaned. */ - ON__UINT64 ContentSerialNumber() const; + ON__UINT64 GeometryContentSerialNumber() const; /* Returns: @@ -1660,7 +1679,9 @@ public: /* Description: - Change the content serial number. + Change the geoemtry content serial number to indicate something affecting + the geometric shape of the subd has changed. This includes topologial changes, + vertex and edge tag changes, and changes to vertex control net locations. Parameters: bChangePreservesSymmetry - [in] When in doubt, pass false. @@ -1670,16 +1691,40 @@ public: Transformations do not preserve symmetries that are set with respect to world coordinate systems. Returns: - The new value of ConentSerialNumber(). + The new value of GeometryConentSerialNumber(). Remarks: - The value can change by any amount. + The value can change by any amount. + Changing the geometry content serial number automatically changes + the render content serial number. */ - ON__UINT64 ChangeContentSerialNumber( + ON__UINT64 ChangeGeometryContentSerialNumber( bool bChangePreservesSymmetry ) const; + /* + Description: + The render content serial number changes whenever a change the might effect + rendered appearance changes. This includes both geometry changes and + changes that affect rendered appeance including changes to per face colors, + per face materials, texture coordinates, and texture mappings. + */ + ON__UINT64 RenderContentSerialNumber() const; + + /* + Description: + Change the render content serial number to indicate something affecting + only rendered appearance has changed. This includes changes to per face colors, + per face materials, texture coordinates, and texture mappings. + Remarks: + Changing the geometry content serial number automatically changes + the render content serial number. If you call ChangeGeometryContentSerialNumber(), + there is no need to also call ChangeRenderContentSerialNumber(). + */ + ON__UINT64 ChangeRenderContentSerialNumber() const; + private: - mutable ON__UINT64 m_subd_content_serial_number = 0; + mutable ON__UINT64 m_subd_geometry_content_serial_number = 0; + mutable ON__UINT64 m_subd_render_content_serial_number = 0; public: @@ -1771,7 +1816,7 @@ public: public: class ON_SubDEdge* AddEdge( - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, @@ -1780,7 +1825,7 @@ public: class ON_SubDEdge* AddEdge( unsigned int candidate_edge_id, - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, @@ -1817,6 +1862,27 @@ public: const ON_SubDEdgePtr* edge ); + /* + Returns: + Capacity of allocated face->m_texture_points[] + */ + unsigned int AllocateFaceTexturePoints( + const class ON_SubDFace* face + ) const; + + void ReturnFaceTexturePoints( + const class ON_SubDFace* face + ) const; + + /* + Description: + Delete texture points from all faces. + Returns: + Number of faces that had texture points. + */ + unsigned int ClearTexturePoints() const; + + /* Description: Split a face into two faces by inserting and edge connecting the @@ -1842,7 +1908,7 @@ public: ); class ON_SubDVertex* AllocateVertex( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int level, const double* P ) @@ -1852,7 +1918,7 @@ public: class ON_SubDVertex* AllocateVertex( unsigned int candidate_id, - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int level, const double* P, unsigned int edge_capacity, @@ -1894,7 +1960,7 @@ public: } ON_SubDEdge* AllocateEdge( - ON_SubD::EdgeTag edge_tag + ON_SubDEdgeTag edge_tag ) { ON_SubDEdge* e = m_heap.AllocateEdgeAndSetId(0U); @@ -1904,7 +1970,7 @@ public: ON_SubDEdge* AllocateEdge( unsigned int candidate_edge_id, - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, unsigned int level, unsigned int face_capacity ) @@ -1939,19 +2005,31 @@ public: class ON_SubDFace* f = m_heap.AllocateFaceAndSetId( 0U); return f; } - + class ON_SubDFace* AllocateFace( unsigned int candidate_face_id, unsigned int level, unsigned int edge_capacity - ) + ) + { + return AllocateFace(candidate_face_id, level, edge_capacity, false); + } + + class ON_SubDFace* AllocateFace( + unsigned int candidate_face_id, + unsigned int level, + unsigned int edge_capacity, + bool bAllocateTexturePoints + ) { class ON_SubDFace* f = m_heap.AllocateFaceAndSetId(candidate_face_id); if (nullptr != f) { f->SetSubdivisionLevel(level); - if (edge_capacity > sizeof(f->m_edge4)/sizeof(f->m_edge4[0]) && edge_capacity <= ON_SubDFace::MaximumEdgeCount) - m_heap.GrowFaceEdgeArray(f,edge_capacity); + if (edge_capacity > sizeof(f->m_edge4) / sizeof(f->m_edge4[0]) && edge_capacity <= ON_SubDFace::MaximumEdgeCount) + m_heap.GrowFaceEdgeArray(f, edge_capacity); + if (bAllocateTexturePoints) + m_heap.AllocateFaceTexturePoints(f); } return f; } @@ -2072,6 +2150,8 @@ public: size_t capacity ); + bool CopyEvaluationCacheForExperts(const ON_SubDimple& src); + private: friend class ON_Internal_SubDFaceMeshFragmentAccumulator; ON_SubDHeap m_heap; @@ -2095,20 +2175,50 @@ public: ) const; - const ON_MappingTag TextureMappingTag() const; + const ON_MappingTag TextureMappingTag(bool bIgnoreTextureCoordinateType) const; void SetTextureMappingTag(const ON_MappingTag& mapping_tag) const; - enum ON_SubDTextureDomainType TextureDomainType() const; - void SetTextureDomainType( - ON_SubDTextureDomainType texture_domain_type + enum ON_SubDTextureCoordinateType TextureCoordinateType() const; + void SetTextureCoordinateType( + ON_SubDTextureCoordinateType texture_coordinate_type ) const; + const ON_SHA1_Hash FragmentTextureCoordinatesTextureSettingsHash() const + { + return m_fragment_texture_settings_hash; + } + + void Internal_SetFragmentTextureCoordinatesTextureSettingsHash(ON_SHA1_Hash hash) const + { + m_fragment_texture_settings_hash = hash; + } + + const ON_SHA1_Hash FragmentColorsSettingsHash() const + { + return m_fragment_colors_settings_hash; + } + + void Internal_SetFragmentColorsSettingsHash(ON_SHA1_Hash hash) const + { + m_fragment_colors_settings_hash = hash; + } + + const ON_MappingTag FragmentColorsMappingTag() const; + void SetFragmentColorsMappingTag(const ON_MappingTag& mapping_tag) const; + private: mutable ON_SubDComponentLocation m_subd_appearance = ON_SubD::DefaultSubDAppearance; - mutable ON_SubDTextureDomainType m_texture_domain_type = ON_SubDTextureDomainType::Unset; + mutable ON_SubDTextureCoordinateType m_texture_coordinate_type = ON_SubDTextureCoordinateType::Unset; unsigned short m_reserved = 0; mutable ON_MappingTag m_texture_mapping_tag; + mutable ON_MappingTag m_fragment_colors_mapping_tag; + + // hash of the settings used to create the current fragment texture coordinates + mutable ON_SHA1_Hash m_fragment_texture_settings_hash = ON_SHA1_Hash::EmptyContentHash; + + // hash of the settings used to create the current fragment vertex colors + mutable ON_SHA1_Hash m_fragment_colors_settings_hash = ON_SHA1_Hash::EmptyContentHash; ON_SimpleArray< ON_SubDLevel* > m_levels; ON_SubDLevel* m_active_level = nullptr; // m_active_level = nullptr or m_active_level = m_levels[m_active_level->m_level_index]. @@ -2164,12 +2274,17 @@ public: { return (nullptr != m_active_level) ? m_active_level->m_level_index : 0; } - + ON_SubDLevel* ActiveLevelPointer() { return m_active_level; } + const ON_SubDLevel* ActiveLevelConstPointer() const + { + return m_active_level; + } + bool SetActiveLevel( unsigned int level_index ) @@ -2542,7 +2657,7 @@ private: ON_RTree* m_fragment_tree = nullptr; private: - // A fixed sized memory pool that allocates enough memory for a fragment and its points and normals. + // A fixed sized memory pool that allocates enough memory for a fragment and its points, normals, textures, and colors. // The fragments never get returned because the pool itself is destroyed in ~ON_SubDMeshImpl(). ON_FixedSizePool m_fsp; }; diff --git a/opennurbs_subd_eval.cpp b/opennurbs_subd_eval.cpp index e632d20f..01c58e84 100644 --- a/opennurbs_subd_eval.cpp +++ b/opennurbs_subd_eval.cpp @@ -37,50 +37,50 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( if (F != N && F + 1 != N) return ON_SUBD_RETURN_ERROR(nullptr); - const ON_SubD::VertexTag vertex_tag = VertexTag(); + const ON_SubDVertexTag vertex_tag = VertexTag(); const unsigned int ring_ei_delta = 2; if (nullptr == subd) subd = new ON_SubD; - ON_SubD::VertexTag vertex_tag0; - ON_SubD::VertexTag vertex_tag1; - ON_SubD::EdgeTag edge_tag0; - ON_SubD::EdgeTag edge_tag1; + ON_SubDVertexTag vertex_tag0; + ON_SubDVertexTag vertex_tag1; + ON_SubDEdgeTag edge_tag0; + ON_SubDEdgeTag edge_tag1; switch (vertex_tag) { - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: sector_angle_radians = 2.0*ON_PI; - vertex_tag0 = ON_SubD::VertexTag::Smooth; - vertex_tag1 = ON_SubD::VertexTag::Smooth; - edge_tag0 = ON_SubD::EdgeTag::Smooth; - edge_tag1 = ON_SubD::EdgeTag::Smooth; + vertex_tag0 = ON_SubDVertexTag::Smooth; + vertex_tag1 = ON_SubDVertexTag::Smooth; + edge_tag0 = ON_SubDEdgeTag::Smooth; + edge_tag1 = ON_SubDEdgeTag::Smooth; break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: if ( !(sector_angle_radians > 0.0 && sector_angle_radians < 2.0*ON_PI) ) sector_angle_radians = 0.5*ON_PI; - vertex_tag0 = ON_SubD::VertexTag::Crease; - vertex_tag1 = ON_SubD::VertexTag::Crease; - edge_tag0 = ON_SubD::EdgeTag::Crease; - edge_tag1 = ON_SubD::EdgeTag::Crease; + vertex_tag0 = ON_SubDVertexTag::Crease; + vertex_tag1 = ON_SubDVertexTag::Crease; + edge_tag0 = ON_SubDEdgeTag::Crease; + edge_tag1 = ON_SubDEdgeTag::Crease; break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: sector_angle_radians = CornerSectorAngleRadians(); - vertex_tag0 = ON_SubD::VertexTag::Crease; - vertex_tag1 = ON_SubD::VertexTag::Crease; - edge_tag0 = ON_SubD::EdgeTag::Crease; - edge_tag1 = ON_SubD::EdgeTag::Crease; + vertex_tag0 = ON_SubDVertexTag::Crease; + vertex_tag1 = ON_SubDVertexTag::Crease; + edge_tag0 = ON_SubDEdgeTag::Crease; + edge_tag1 = ON_SubDEdgeTag::Crease; break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: sector_angle_radians = 2.0*ON_PI; - vertex_tag0 = ON_SubD::VertexTag::Crease; - vertex_tag1 = ON_SubD::VertexTag::Smooth; - edge_tag0 = ON_SubD::EdgeTag::Crease; - edge_tag1 = ON_SubD::EdgeTag::Smooth; + vertex_tag0 = ON_SubDVertexTag::Crease; + vertex_tag1 = ON_SubDVertexTag::Smooth; + edge_tag0 = ON_SubDEdgeTag::Crease; + edge_tag1 = ON_SubDEdgeTag::Smooth; break; default: @@ -109,7 +109,7 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( for (unsigned int vi = 0; vi < R; vi++) { - ON_SubD::VertexTag vertex_tag_vi; + ON_SubDVertexTag vertex_tag_vi; if ( 0 == vi ) vertex_tag_vi = vertex_tag; // center vertex else if ( 1 == vi ) @@ -117,7 +117,7 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( else if ( R == vi+1 && N > F ) vertex_tag_vi = vertex_tag1; // last edge else - vertex_tag_vi = ON_SubD::VertexTag::Smooth; // interior edge or an outer face vertex + vertex_tag_vi = ON_SubDVertexTag::Smooth; // interior edge or an outer face vertex if (radius > 0.0) { @@ -143,15 +143,15 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( for (unsigned int vei = 0; vei < N; vei++) { - ON_SubD::EdgeTag edge_tag_vei; + ON_SubDEdgeTag edge_tag_vei; if ( 0 == vei ) edge_tag_vei = edge_tag0; // first edge else if ( vei+1 == N ) edge_tag_vei = edge_tag1; // last edge else - edge_tag_vei = ON_SubD::EdgeTag::Smooth; // interior edge + edge_tag_vei = ON_SubDEdgeTag::Smooth; // interior edge - double w0 = (ON_SubD::EdgeTag::Smooth == edge_tag_vei) ? smooth_edge_w0 : ON_SubDSectorType::IgnoredSectorCoefficient; + double w0 = (ON_SubDEdgeTag::Smooth == edge_tag_vei) ? smooth_edge_w0 : ON_SubDSectorType::IgnoredSectorCoefficient; unsigned int ev1i = 1 + vei*ring_ei_delta; E.Append( subd->AddEdgeWithSectorCoefficients( @@ -178,8 +178,8 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( f_edgeptr[0] = ON_SubDEdgePtr::Create(f_edge[0], 0); f_edgeptr[3] = ON_SubDEdgePtr::Create(f_edge[3], 1); f_vertex[2] = V[2 + 2 * vfi]; - f_edge[1] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[1], ON_SubDSectorType::IgnoredSectorCoefficient, f_vertex[2], ON_SubDSectorType::IgnoredSectorCoefficient); - f_edge[2] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[2], ON_SubDSectorType::IgnoredSectorCoefficient, f_vertex[3], ON_SubDSectorType::IgnoredSectorCoefficient); + f_edge[1] = subd->AddEdgeWithSectorCoefficients(ON_SubDEdgeTag::Smooth, f_vertex[1], ON_SubDSectorType::IgnoredSectorCoefficient, f_vertex[2], ON_SubDSectorType::IgnoredSectorCoefficient); + f_edge[2] = subd->AddEdgeWithSectorCoefficients(ON_SubDEdgeTag::Smooth, f_vertex[2], ON_SubDSectorType::IgnoredSectorCoefficient, f_vertex[3], ON_SubDSectorType::IgnoredSectorCoefficient); f_edgeptr[1] = ON_SubDEdgePtr::Create(f_edge[1], 0); f_edgeptr[2] = ON_SubDEdgePtr::Create(f_edge[2], 0); subd->AddFace(f_edgeptr,4); @@ -473,7 +473,7 @@ static bool GetSectorLimitPointHelper( if ( false == bUndefinedNormalIsPossible - && ON_SubD::VertexTag::Crease == SM.m_sector_type.VertexTag() + && ON_SubDVertexTag::Crease == SM.m_sector_type.VertexTag() && R >= 5 && *((const ON_3dPoint*)(point_ring+ point_ring_stride)) == *((const ON_3dPoint*)(point_ring + (R-1)* point_ring_stride)) ) @@ -661,9 +661,55 @@ const ON_3dPoint ON_SubDVertex::SurfacePoint() const { ON_3dPoint limit_point(ON_3dPoint::NanPoint); return GetSurfacePoint(&limit_point.x) ? limit_point : ON_3dPoint::NanPoint; - } +const ON_3dVector ON_SubDVertex::SurfaceNormal( + const ON_SubDFace* sector_face, + bool bUndefinedNormalPossible +) const +{ + for (;;) + { + if (m_face_count < 1 || nullptr == m_faces) + { + ON_ERROR("No faces on this vertex."); + break; + } + + if (nullptr == sector_face && IsCreaseOrCorner()) + { + const ON_SubDComponentPtrPair crease_pair = this->CreasedEdgePair(false); + const ON_SubDEdge* e[2] = { crease_pair.First().Edge(), crease_pair.Second().Edge() }; + if (nullptr == e[0] || 1 != e[0]->m_face_count || nullptr == e[1] || 1 != e[1]->m_face_count) + { + ON_ERROR("sector_face must be specified in this case."); + break; + } + } + + if (nullptr == sector_face) + sector_face = m_faces[0]; + ON_SubDSectorSurfacePoint limit_point; + bool rc = ON_SubDVertex::GetSurfacePoint( + sector_face, + bUndefinedNormalPossible, + limit_point + ); + if (false == rc) + break; + const ON_3dVector N(limit_point.m_limitN); + if (false == bUndefinedNormalPossible && N.IsZero()) + break; + return N; + } + + return ON_3dVector::NanVector; +} + +const ON_SubDSectorSurfacePoint& ON_SubDVertex::SectorSurfacePointForExperts() const +{ + return this->m_limit_point; +} const ON_Plane ON_SubDVertex::VertexFrame( ON_SubDComponentLocation subd_appearance diff --git a/opennurbs_subd_fragment.cpp b/opennurbs_subd_fragment.cpp index 3d0c07e7..a096d5fc 100644 --- a/opennurbs_subd_fragment.cpp +++ b/opennurbs_subd_fragment.cpp @@ -63,34 +63,95 @@ unsigned ON_SubDMeshFragment::VertexCapacity() const return (m_vertex_capacity_etc & ON_SubDMeshFragment::ValueMask); } +bool ON_SubDMeshFragment::Internal_ManagedArraysAreInterlaced() +{ + return ON_SubDMeshFragment::ManagedDoublesPerVertex == ON_SubDMeshFragment::Managed_3d_stride; +} + +size_t ON_SubDMeshFragment::Internal_Managed3dArrayOffset(size_t vertex_capacity) +{ + return (vertex_capacity > 0) + ? (3 * (ON_SubDMeshFragment::Internal_ManagedArraysAreInterlaced() ? 1 : vertex_capacity)) + : 0; +} + +void ON_SubDMeshFragment::Internal_LayoutArrays( + bool bManagedArray, + double* PNTC_array, + size_t vertex_capacity +) +{ + this->SetVertexCount(0); + + if (nullptr == PNTC_array || vertex_capacity < 4 || vertex_capacity > ((size_t)ON_SubDMeshFragment::MaximumVertexCount) ) + { + PNTC_array = nullptr; + vertex_capacity = 0; + bManagedArray = false; + } + if (bManagedArray) + { + unsigned short etc = (m_vertex_capacity_etc & ON_SubDMeshFragment::EtcMask); + etc |= ON_SubDMeshFragment::EtcManagedArraysBit; + m_vertex_capacity_etc = (unsigned short)vertex_capacity; + m_vertex_capacity_etc |= etc; + } + else + { + this->SetUnmanagedVertexCapacity(vertex_capacity); + } + + const size_t offset_3d = (nullptr != PNTC_array) ? ON_SubDMeshFragment::Internal_Managed3dArrayOffset(vertex_capacity) : 0; + m_P = PNTC_array; + m_P_stride = ON_SubDMeshFragment::Managed_3d_stride; + m_N = m_P + offset_3d; + m_N_stride = ON_SubDMeshFragment::Managed_3d_stride; + m_T = m_N + offset_3d; + m_T_stride = ON_SubDMeshFragment::Managed_3d_stride; + m_C = (ON_Color*)(m_T + offset_3d); + m_C_stride = ON_SubDMeshFragment::Managed_color_stride; +} + bool ON_SubDMeshFragment::ManagedArrays() const { - const size_t capacity = (ON_SubDMeshFragment::ValueMask & m_vertex_capacity_etc); + const size_t vertex_capacity = (ON_SubDMeshFragment::ValueMask & m_vertex_capacity_etc); + const size_t offset_3d = ON_SubDMeshFragment::Internal_Managed3dArrayOffset(vertex_capacity); return ( 0 != (ON_SubDMeshFragment::EtcManagedArraysBit & m_vertex_capacity_etc) - && capacity > 0 - && 3 == m_P_stride && 3 == m_N_stride && 3 == m_T_stride + && vertex_capacity > 0 + && ON_SubDMeshFragment::Managed_3d_stride == m_P_stride + && ON_SubDMeshFragment::Managed_3d_stride == m_N_stride + && ON_SubDMeshFragment::Managed_3d_stride == m_T_stride + && ON_SubDMeshFragment::Managed_color_stride == m_C_stride && nullptr != m_P - && m_N == m_P + (capacity * 3) - && m_T == m_N + (capacity * 3) + && m_N == m_P + offset_3d + && m_T == m_N + offset_3d + && m_C == (const ON_Color*)(m_T + offset_3d) ) ? true : false; } bool ON_SubDMeshFragment::DeleteManagedArrays() { + DeleteManagedCurvatureCapacity(); if (ManagedArrays()) { - void* ptr = m_P; - m_vertex_count_etc &= ON_SubDMeshFragment::EtcMask; - m_vertex_capacity_etc &= ON_SubDMeshFragment::EtcMask; - m_vertex_capacity_etc &= ~ON_SubDMeshFragment::EtcManagedArraysBit; + double* managed_array = m_P; + m_vertex_count_etc &= ON_SubDMeshFragment::EtcControlNetQuadBit; + m_vertex_capacity_etc = 0; m_P = nullptr; m_N = nullptr; m_T = nullptr; + m_C = nullptr; + m_K = nullptr; m_P_stride = 0; m_N_stride = 0; m_T_stride = 0; - onfree(ptr); + m_C_stride = 0; + if (nullptr != managed_array) + { + // Allocated in ON_SubDMeshFragment::ReserveManagedVertexCapacity() + delete[] managed_array; + } return true; } return false; @@ -137,16 +198,33 @@ bool ON_SubDMeshFragment::ReserveManagedVertexCapacity(size_t vertex_capacity) return true; DeleteManagedArrays(); } - unsigned short etc = m_vertex_capacity_etc &= ON_SubDMeshFragment::EtcMask; - etc |= ON_SubDMeshFragment::EtcManagedArraysBit; - double* p = (double*)onmalloc(9 * vertex_capacity); - m_P = p; - m_P_stride = 3; - m_N = m_P + (vertex_capacity*m_P_stride); - m_N_stride = 3; - m_T = m_N + (vertex_capacity*m_N_stride); - m_T_stride = 3; - return true; + + // p will be deleted in ON_SubDMeshFragment::DeleteManagedArrays(). + double* managed_doubles = new(std::nothrow) double[ON_SubDMeshFragment::ManagedDoublesPerVertex * vertex_capacity]; + Internal_LayoutArrays(true, managed_doubles, vertex_capacity); + return (this->VertexCapacity() >= vertex_capacity); +} + +bool ON_SubDMeshFragment::ReserveManagedCurvatureCapacity() const +{ + const size_t current_capacity = (size_t)(ON_SubDMeshFragment::ValueMask & m_vertex_capacity_etc); + if (nullptr == m_K && current_capacity > 0) + { + m_K = new(std::nothrow) ON_SurfaceCurvature[current_capacity]; + m_vertex_capacity_etc &= ~ON_SubDMeshFragment::EtcCurvaturesExistBit; // m_K[] content is unset. + } + return (nullptr != m_K && current_capacity > 0); +} + + +void ON_SubDMeshFragment::DeleteManagedCurvatureCapacity() const +{ + if (nullptr != m_K) + { + delete[] m_K; + m_K = nullptr; + m_vertex_capacity_etc &= ~ON_SubDMeshFragment::EtcCurvaturesExistBit; // m_K[] content is unset. + } } unsigned ON_SubDMeshFragment::VertexCount() const @@ -158,7 +236,7 @@ bool ON_SubDMeshFragment::SetVertexCount(size_t vertex_count) { if (vertex_count < 0 || vertex_count > VertexCapacity()) return ON_SUBD_RETURN_ERROR(false); - unsigned short etc = m_vertex_count_etc & ON_SubDMeshFragment::EtcMask; + const unsigned short etc = m_vertex_count_etc & ON_SubDMeshFragment::EtcMask; m_vertex_count_etc = ((unsigned short)vertex_count) | etc; return true; } @@ -173,6 +251,16 @@ unsigned int ON_SubDMeshFragment::NormalCount() const return (nullptr != m_N && m_N_stride >= 3) ? VertexCount() : 0U; } +unsigned int ON_SubDMeshFragment::CurvatureCount() const +{ + return (nullptr != m_K) ? VertexCount() : 0U; +} + +unsigned int ON_SubDMeshFragment::ColorCount() const +{ + return (nullptr != m_C && m_C_stride >= 1) ? VertexCount() : 0U; +} + unsigned int ON_SubDMeshFragment::PointCapacity() const { return (nullptr != m_P && m_P_stride >= 3) ? VertexCapacity() : 0U; @@ -183,6 +271,17 @@ unsigned int ON_SubDMeshFragment::NormalCapacity() const return (nullptr != m_N && m_N_stride >= 3) ? VertexCapacity() : 0U; } + +unsigned int ON_SubDMeshFragment::CurvatureCapacity() const +{ + return (nullptr != m_K) ? VertexCapacity() : 0U; +} + +unsigned int ON_SubDMeshFragment::ColorCapacity() const +{ + return (nullptr != m_C && m_C_stride >= 1) ? VertexCapacity() : 0U; +} + const double* ON_SubDMeshFragment::PointArray(ON_SubDComponentLocation subd_appearance)const { return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? &m_ctrlnetP[0][0] : m_P; @@ -213,6 +312,262 @@ unsigned ON_SubDMeshFragment::NormalArrayCount(ON_SubDComponentLocation subd_app return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 4U : NormalCount(); } +const ON_SurfaceCurvature* ON_SubDMeshFragment::CurvatureArray(ON_SubDComponentLocation subd_appearance)const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? &m_ctrlnetK[0] : m_K; +} + +unsigned ON_SubDMeshFragment::CurvatureArrayCount(ON_SubDComponentLocation subd_appearance) const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 4U : CurvatureCount(); +} + + +const ON_Color* ON_SubDMeshFragment::ColorArray(ON_SubDComponentLocation subd_appearance) const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? &m_ctrlnetC[0] : m_C; +} + +size_t ON_SubDMeshFragment::ColorArrayStride(ON_SubDComponentLocation subd_appearance) const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 1 : m_C_stride; +} + + +unsigned ON_SubDMeshFragment::ColorArrayCount(ON_SubDComponentLocation subd_appearance) const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 4U : ColorCount(); +} + +bool ON_SubDMeshFragment::ColorsExist() const +{ + return (nullptr != m_C && m_C_stride > 0 && 0 != (m_vertex_capacity_etc & ON_SubDMeshFragment::EtcColorsExistBit)); +} + +void ON_SubDMeshFragment::SetColorsExist(bool bColorsExist) const +{ + if (bColorsExist) + m_vertex_capacity_etc |= ON_SubDMeshFragment::EtcColorsExistBit; + else + m_vertex_capacity_etc &= ~ON_SubDMeshFragment::EtcColorsExistBit; +} + +bool ON_SubDMeshFragment::CurvaturesExist() const +{ + return (nullptr != m_K && 0 != (m_vertex_capacity_etc & ON_SubDMeshFragment::EtcCurvaturesExistBit)); +} + +void ON_SubDMeshFragment::SetCurvaturesExist(bool bSetCurvaturesExist) const +{ + if (bSetCurvaturesExist) + m_vertex_capacity_etc |= ON_SubDMeshFragment::EtcCurvaturesExistBit; + else + m_vertex_capacity_etc &= ~ON_SubDMeshFragment::EtcCurvaturesExistBit; +} + +bool ON_SubDMeshFragment::SetColors(ON_Color color) const +{ + bool bColorsExist = false; + for (;;) + { + m_ctrlnetC[0] = color; + m_ctrlnetC[1] = color; + m_ctrlnetC[2] = color; + m_ctrlnetC[3] = color; + const ON_SubDComponentLocation subd_appearance = ON_SubDComponentLocation::Surface; + + const unsigned count = PointArrayCount(subd_appearance); + if (count <= 0) + break; + if (count != ColorArrayCount(subd_appearance)) + break; + + ON_Color* C = const_cast(ColorArray(subd_appearance)); + if (nullptr == C) + break; + + bColorsExist = (ON_Color::UnsetColor != color); + for (const ON_Color* C1 = C + count; C < C1; ++C) + *C = color; + break; + } + SetColorsExist(bColorsExist); + return ColorsExist(); +} + +bool ON_SubDMeshFragment::SetColorsFromCallback( + const ON_MappingTag& fragment_colors_mapping_tag, + const ON_SubD& subd, + ON__UINT_PTR callback_context, + const ON_Color(*color_callback)( + ON__UINT_PTR callback_context, + const ON_MappingTag& mapping_tag, + const ON_SubD& subd, + ON_SubDComponentPtr cptr, + const ON_3dPoint& P, + const ON_3dVector& N, + const ON_3dPoint& T, + const ON_SurfaceCurvature& K + ) +) const +{ + bool bColorsExist = false; + for (;;) + { + m_ctrlnetC[0] = ON_Color::UnsetColor; + m_ctrlnetC[1] = ON_Color::UnsetColor; + m_ctrlnetC[2] = ON_Color::UnsetColor; + m_ctrlnetC[3] = ON_Color::UnsetColor; + + if (nullptr == color_callback) + { + // removing vertex colors. + break; + } + + const ON_SubDComponentLocation subd_appearance = ON_SubDComponentLocation::Surface; + const unsigned count = PointArrayCount(subd_appearance); + if (count <= 0) + break; + if (count != ColorArrayCount(subd_appearance)) + break; + + const double* P = PointArray(subd_appearance); + const size_t Pstride = PointArrayStride(subd_appearance); + if (nullptr == P || Pstride < 3) + break; + + ON_Color* C = const_cast(ColorArray(subd_appearance)); + if (nullptr == C) + break; + + const double nan3[3] = { ON_DBL_QNAN, ON_DBL_QNAN, ON_DBL_QNAN }; + + const double* N = NormalArray(subd_appearance); + const size_t Nstride = (nullptr != N) ? NormalArrayStride(subd_appearance) : 0; + if (nullptr == N) + N = nan3; + + const double* T = TextureCoordinateArray(subd_appearance); + const size_t Tstride = (nullptr != T) ? TextureCoordinateArrayStride(subd_appearance) : 0; + if (nullptr == T) + T = nan3; + + const ON_SurfaceCurvature* K = CurvatureArray(subd_appearance); + const size_t Kstride = (nullptr != K) ? 1 : 0; + if (nullptr == K) + K = &ON_SurfaceCurvature::Nan; + + const ON_SubDComponentPtr cptr = ON_SubDComponentPtr::Create(m_face); + for (const double* P1 = P + Pstride * count; P < P1; P += Pstride, ++C) + { + // Never give the callback access to fragment array pointers. + const ON_3dPoint callbackP(P); + const ON_3dVector callbackN(N); + const ON_3dPoint callbackT(T); + const ON_SurfaceCurvature callbackK(*K); + const ON_Color callbackC = color_callback(callback_context, fragment_colors_mapping_tag, subd, cptr, callbackP, callbackN, callbackT, callbackK); + if (callbackC != ON_Color::UnsetColor) + bColorsExist = true; + *C = callbackC; + N += Nstride; + T += Tstride; + K += Kstride; + } + break; + } + + SetColorsExist(bColorsExist); + if (bColorsExist) + { + m_ctrlnetC[0] = CornerColor(0); + m_ctrlnetC[1] = CornerColor(1); + m_ctrlnetC[2] = CornerColor(2); + m_ctrlnetC[3] = CornerColor(3); + } + + return ColorsExist(); +} + +bool ON_SubD::SetFragmentColorsFromCallback( + bool bLazySet, + ON_SHA1_Hash fragment_colors_settings_hash, + ON_MappingTag fragment_colors_mapping_tag, + ON__UINT_PTR callback_context, + const ON_Color(*color_callback)( + ON__UINT_PTR callback_context, + const ON_MappingTag& mapping_tag, + const ON_SubD& subd, + ON_SubDComponentPtr cptr, + const ON_3dPoint& P, + const ON_3dVector& N, + const ON_3dPoint& T, + const ON_SurfaceCurvature& K) +) const +{ + if (bLazySet && fragment_colors_settings_hash == FragmentColorsSettingsHash()) + return true; + + bool bFragmentVetexColorsSet = false; + const ON_SubDimple* subdimple = this->SubDimple(); + if (nullptr != subdimple) + { + ON_SubDMeshFragmentIterator fragit(*this); + for (const ON_SubDMeshFragment* frag = fragit.FirstFragment(); nullptr != frag; frag = fragit.NextFragment()) + { + const bool b = frag->SetColorsFromCallback( + fragment_colors_mapping_tag, + *this, + callback_context, + color_callback + ); + if (b) + bFragmentVetexColorsSet = true; + } + if (bFragmentVetexColorsSet) + { + subdimple->Internal_SetFragmentColorsSettingsHash(fragment_colors_settings_hash); + SetFragmentColorsMappingTag(fragment_colors_mapping_tag); + ChangeRenderContentSerialNumber(); + } + else + { + subdimple->Internal_SetFragmentColorsSettingsHash(ON_SHA1_Hash::EmptyContentHash); + this->SetFragmentColorsMappingTag(ON_MappingTag::Unset); + } + } + + return bFragmentVetexColorsSet; +} + +void ON_SubD::ClearFragmentColors( + bool bClearFragmentColorsMappingTag +) +{ + if (bClearFragmentColorsMappingTag) + SetFragmentColorsMappingTag(ON_MappingTag::Unset); + SetFragmentColorsFromCallback(false, ON_SHA1_Hash::EmptyContentHash, ON_MappingTag::Unset, 0, nullptr ); +} + +const ON_SHA1_Hash ON_SubD::FragmentColorsSettingsHash() const +{ + const ON_SubDimple* subdimple = this->SubDimple(); + return (nullptr != subdimple) ? subdimple->FragmentColorsSettingsHash() : ON_SHA1_Hash::EmptyContentHash; +} + +void ON_SubD::SetFragmentColorsMappingTag(const ON_MappingTag& colors_mapping_tag) const +{ + const ON_SubDimple* dimple = SubDimple(); + if (nullptr != dimple) + dimple->SetFragmentColorsMappingTag(colors_mapping_tag); +} + +const ON_MappingTag ON_SubD::FragmentColorsMappingTag() const +{ + const ON_SubDimple* dimple = SubDimple(); + return (nullptr != dimple) ? dimple->FragmentColorsMappingTag() : ON_MappingTag::Unset; +} + const ON_3dPoint ON_SubDMeshFragment::ControlNetQuadPoint( bool bGridOrder, unsigned point_index @@ -245,6 +600,8 @@ const ON_SubDMeshFragment ON_SubDMeshFragment::ControlNetQuadFragmentForExperts( q.m_vertex_count_etc |= (ON_SubDMeshFragment::EtcTextureCoordinatesExistBit&m_vertex_count_etc); q.m_vertex_capacity_etc = 4; + q.m_vertex_capacity_etc |= (ON_SubDMeshFragment::EtcColorsExistBit & m_vertex_capacity_etc); + q.m_vertex_capacity_etc |= (ON_SubDMeshFragment::EtcCurvaturesExistBit & m_vertex_capacity_etc); // 4 quad corner points in grid order with stride = 3 q.m_P = const_cast(&m_ctrlnetP[0][0]); @@ -258,6 +615,11 @@ const ON_SubDMeshFragment ON_SubDMeshFragment::ControlNetQuadFragmentForExperts( q.m_T = const_cast(&m_ctrlnetT[0][0]); q.m_T_stride = 3; + q.m_C = const_cast(&m_ctrlnetC[0]); + q.m_C_stride = 1; + + q.m_K = const_cast(&m_ctrlnetK[0]); + // The grid is a single quad q.m_grid = ON_SubDMeshFragmentGrid::OneQuadGrid; @@ -480,7 +842,7 @@ ON_SubDManagedMeshFragment& ON_SubDManagedMeshFragment::operator=( ON_SubDManage bool ON_SubDManagedMeshFragment::ReserveCapacity( unsigned int display_density - ) ON_NOEXCEPT +) ON_NOEXCEPT { Clear(); @@ -508,8 +870,10 @@ bool ON_SubDManagedMeshFragment::ReserveCapacity( const size_t P_stride = 3; const size_t N_stride = 3; const size_t T_stride = 3; + const size_t C_stride = 1; - size_t storage_capacity = (P_stride + N_stride + T_stride)*V_capacity; + // storage_capacity holds = 3 point coords + 3 normal coords + 3 texture point coords + an ON_Color + unused 4 bytes. + size_t storage_capacity = ON_SubDMeshFragment::ManagedDoublesPerVertex * V_capacity; if (storage_capacity > m_storage_capacity || nullptr == m_storage) { if (m_storage_capacity > 0 && nullptr != m_storage) @@ -525,17 +889,12 @@ bool ON_SubDManagedMeshFragment::ReserveCapacity( } UnsetControlNetQuad(); - SetUnmanagedVertexCapacity(V_capacity); + // ON_SubDManagedMeshFragment manages m_storage and m_P[]/m_N[]/m_T[]/m_C[] are not managed by ON_SubDMeshFragment. + // (ON_SubDManagedMeshFragment is a legacy class that should not be used and this confusing situation is + // an artifact from early code.) + Internal_LayoutArrays(false, m_storage, V_capacity); SetVertexCount(V_capacity); - m_P_stride = P_stride; - m_N_stride = N_stride; - m_T_stride = T_stride; - - m_P = m_storage; - m_N = m_storage + (3*V_capacity); - m_T = m_storage + (6*V_capacity); - m_surface_bbox = ON_BoundingBox::NanBoundingBox; m_grid = ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity(display_density,0U); @@ -556,6 +915,7 @@ void ON_SubDManagedMeshFragment::Destroy() ON_NOEXCEPT memset(this, 0, sizeof(*this)); if ( nullptr != p) delete[] p; + this->DeleteManagedCurvatureCapacity(); } void ON_SubDManagedMeshFragment::CopyHelper(const ON_SubDMeshFragment& src) @@ -571,6 +931,8 @@ void ON_SubDManagedMeshFragment::CopyHelper(const ON_SubDMeshFragment& src) m_N_stride = 0; m_T = nullptr; m_T_stride = 0; + m_C = nullptr; + m_C_stride = 0; m_surface_bbox = ON_BoundingBox::NanBoundingBox; const size_t V_count = src.VertexCount(); if (0 == V_count) @@ -597,18 +959,50 @@ void ON_SubDManagedMeshFragment::CopyHelper(const ON_SubDMeshFragment& src) return; } + const unsigned C_count = src.ColorCount(); + if (0 != C_count && V_count != C_count) + { + ON_SUBD_ERROR("invalid counts"); + return; + } + m_ctrlnetT[0][0] = src.m_ctrlnetT[0][0]; m_ctrlnetT[0][1] = src.m_ctrlnetT[0][1]; + m_ctrlnetT[0][2] = src.m_ctrlnetT[0][2]; m_ctrlnetT[1][0] = src.m_ctrlnetT[1][0]; m_ctrlnetT[1][1] = src.m_ctrlnetT[1][1]; + m_ctrlnetT[1][2] = src.m_ctrlnetT[1][2]; m_ctrlnetT[2][0] = src.m_ctrlnetT[2][0]; m_ctrlnetT[2][1] = src.m_ctrlnetT[2][1]; + m_ctrlnetT[2][2] = src.m_ctrlnetT[2][2]; m_ctrlnetT[3][0] = src.m_ctrlnetT[3][0]; m_ctrlnetT[3][1] = src.m_ctrlnetT[3][1]; + m_ctrlnetT[3][2] = src.m_ctrlnetT[3][2]; + + m_ctrlnetC[0] = src.m_ctrlnetC[0]; + m_ctrlnetC[1] = src.m_ctrlnetC[1]; + m_ctrlnetC[2] = src.m_ctrlnetC[2]; + m_ctrlnetC[3] = src.m_ctrlnetC[3]; + + m_ctrlnetK[0] = src.m_ctrlnetK[0]; + m_ctrlnetK[1] = src.m_ctrlnetK[1]; + m_ctrlnetK[2] = src.m_ctrlnetK[2]; + m_ctrlnetK[3] = src.m_ctrlnetK[3]; + + m_pack_rect[0][0] = src.m_pack_rect[0][0]; + m_pack_rect[0][1] = src.m_pack_rect[0][1]; + m_pack_rect[1][0] = src.m_pack_rect[1][0]; + m_pack_rect[1][1] = src.m_pack_rect[1][1]; + m_pack_rect[2][0] = src.m_pack_rect[2][0]; + m_pack_rect[2][1] = src.m_pack_rect[2][1]; + m_pack_rect[3][0] = src.m_pack_rect[3][0]; + m_pack_rect[3][1] = src.m_pack_rect[3][1]; + m_surface_bbox = src.m_surface_bbox; m_grid = src.m_grid; - const size_t storage_capacity = 9U * V_count; + // 10 = 3 point coordinates + 3 normal coordinates + 3 texture coordinates + a double to provide double aligned color memory + const size_t storage_capacity = 10U * V_count; m_storage = new(std::nothrow) double[storage_capacity]; if (nullptr == m_storage) { @@ -697,6 +1091,31 @@ void ON_SubDManagedMeshFragment::CopyHelper(const ON_SubDMeshFragment& src) src_p += src_p_stride; } + const ON_Color* src_c; + size_t src_c_stride; + if (C_count > 0) + { + src_c = src.m_C; + src_c_stride = src.m_C_stride; + } + else + { + src_c = &ON_Color::UnsetColor; + src_c_stride = 0; + } + m_C = (ON_Color*)(m_storage + (9 * V_count)); + m_C_stride = 1; + ON_Color* c = m_C; + ON_Color* c1 = c + (m_C_stride * V_count); + while (c < c1) + { + *c = *src_c; + c += m_C_stride; + src_c += src_c_stride; + } + + // TODO - copy m_K as needed + ON_3dPoint quad_points[4]; ON_3dVector quad_normal; src.GetControlNetQuad(false, quad_points, quad_normal); @@ -1115,6 +1534,57 @@ const ON_3dPoint ON_SubDMeshFragment::CornerPoint( return ON_3dPoint(m_P + (i*m_P_stride)); } +const ON_Color ON_SubDMeshFragment::CornerColor( + unsigned int grid_corner_index +) const +{ + if (grid_corner_index >= 4 || nullptr == m_C || m_C_stride <= 0 || nullptr == m_grid.m_S) + return ON_Color::UnsetColor; + + const unsigned int i = m_grid.m_S[grid_corner_index * m_grid.m_side_segment_count]; + + return m_C[i * m_C_stride]; +} + +const ON_SurfaceCurvature ON_SubDMeshFragment::CornerCurvature(unsigned int grid_corner_index) const +{ + if (grid_corner_index >= 4 || nullptr == m_K || nullptr == m_grid.m_S) + return ON_SurfaceCurvature::Nan; + + const unsigned int i = m_grid.m_S[grid_corner_index * m_grid.m_side_segment_count]; + + return m_K[i]; +} + +const ON_3dPoint ON_SubDMeshFragment::TextureCoordinateCorner( + unsigned int grid_corner_index +) const +{ + return + (grid_corner_index < 4) + ? ON_3dPoint(m_ctrlnetT[grid_corner_index][0], m_ctrlnetT[grid_corner_index][1], m_ctrlnetT[grid_corner_index][2]) + : ON_3dPoint::NanPoint; +} + +const ON_2dPoint ON_SubDMeshFragment::PackRectCorner( + unsigned int grid_corner_index +) const +{ + return + (grid_corner_index < 4) + ? ON_2dPoint(m_pack_rect[grid_corner_index][0], m_pack_rect[grid_corner_index][1]) + : ON_2dPoint::NanPoint; +} + +const ON_2dPoint ON_SubDMeshFragment::PackRectCenter() const +{ + return ON_2dPoint( + 0.25 * (m_pack_rect[0][0] + m_pack_rect[1][0] + m_pack_rect[2][0] + m_pack_rect[3][0]), + 0.25 * (m_pack_rect[0][1] + m_pack_rect[1][1] + m_pack_rect[2][1] + m_pack_rect[3][1]) + ); +} + + const ON_3dVector ON_SubDMeshFragment::CornerNormal(unsigned int grid_corner_index) const { if ( grid_corner_index >= 4 || nullptr == m_N || m_N_stride <= 0 || nullptr == m_grid.m_S ) @@ -1184,6 +1654,27 @@ const ON_3dVector ON_SubDMeshFragment::CenterNormal() const return ON_3dVector(m_N + (i*m_N_stride)); } + +const ON_3dPoint ON_SubDMeshFragment::CenterTextureCoordinate() const +{ + if (nullptr == m_T || (m_T_stride != 0 && m_T_stride < 3) || m_grid.m_side_segment_count <= 0 || nullptr == m_grid.m_S) + return ON_3dPoint::NanPoint; + + if (1 == m_grid.m_side_segment_count) + { + const ON_3dPoint T( + (m_T[0] + m_T[m_T_stride] + m_T[2 * m_T_stride] + m_T[3 * m_T_stride]), + (m_T[1] + m_T[1 + m_T_stride] + m_T[1 + 2 * m_T_stride] + m_T[1 + 3 * m_T_stride]), + (m_T[2] + m_T[2 + m_T_stride] + m_T[2 + 2 * m_T_stride] + m_T[2 + 3 * m_T_stride]) + ); + return T; + } + + const unsigned int i = (m_grid.m_side_segment_count * (m_grid.m_side_segment_count + 2)) / 2; + + return ON_3dPoint(m_T + (i * m_T_stride)); +} + bool ON_SubDMeshFragment::Internal_GetFrameHelper( unsigned int P_dex, unsigned int Q_dex, @@ -1693,14 +2184,17 @@ bool ON_SubDMeshImpl::ReserveCapacity( if ( subd_fragment_count < 1 ) return ON_SUBD_RETURN_ERROR(false); - size_t sizeof_PNT = 9*fragment_point_count*sizeof(double); + // note sizeof(double) = 2*sizeof(ON_Color) so we have + // room for 3d poitns, 3d normals, 3d texture points, a color array + // and a currently unused array of 32 bit elements. + size_t sizeof_PNTC = ON_SubDMeshFragment::ManagedDoublesPerVertex*fragment_point_count*sizeof(double); size_t sizeof_fragment = sizeof(ON_SubDMeshFragment); if (0 != sizeof_fragment % sizeof(double)) { sizeof_fragment = (1 + sizeof_fragment / sizeof(double))*sizeof(double); } - if( false == m_fsp.Create(sizeof_fragment + sizeof_PNT,subd_fragment_count,0)) + if( false == m_fsp.Create(sizeof_fragment + sizeof_PNTC,subd_fragment_count,0)) return ON_SUBD_RETURN_ERROR(false); m_display_density = display_density; @@ -1709,7 +2203,10 @@ bool ON_SubDMeshImpl::ReserveCapacity( return true; } -size_t ON_SubDMeshFragment::SizeofFragment(unsigned int display_density) +size_t ON_SubDMeshFragment::SizeofFragment( + unsigned int display_density, + bool bCurvatureArray +) { if (display_density > ON_SubDDisplayParameters::MaximumDensity) return ON_SUBD_RETURN_ERROR(0); @@ -1722,7 +2219,13 @@ size_t ON_SubDMeshFragment::SizeofFragment(unsigned int display_density) size_t sz = sizeof(ON_SubDMeshFragment); while (0 != (sz % sizeof(double))) ++sz; - sz += vertex_capacity * 9 * sizeof(double); // 8=3+3+3 (3d points in m_P[], 3d vectors in m_N[], and 3d points in m_T[]). + + const size_t doubles_per_vertex + = 10 // 6 = 3+3 = 3d points in m_P[] and 3d vectors in m_N[] and 3d points in m_T[] and a color in m_C[] (sizeof(m_C[0]) <= sizeof(double)) + + (size_t)((bCurvatureArray ? sizeof(ON_SurfaceCurvature)/sizeof(double) : 0)) + ; + const size_t sizeof_doubles = doubles_per_vertex * sizeof(double); + sz += vertex_capacity * sizeof_doubles; return sz; } @@ -1760,19 +2263,35 @@ bool ON_SubDMeshFragment::CopyFrom( m_ctrlnetT[0][0] = src_fragment.m_ctrlnetT[0][0]; m_ctrlnetT[0][1] = src_fragment.m_ctrlnetT[0][1]; m_ctrlnetT[0][2] = src_fragment.m_ctrlnetT[0][2]; - m_ctrlnetT[1][0] = src_fragment.m_ctrlnetT[1][0]; m_ctrlnetT[1][1] = src_fragment.m_ctrlnetT[1][1]; m_ctrlnetT[1][2] = src_fragment.m_ctrlnetT[1][2]; - m_ctrlnetT[2][0] = src_fragment.m_ctrlnetT[2][0]; m_ctrlnetT[2][1] = src_fragment.m_ctrlnetT[2][1]; m_ctrlnetT[2][2] = src_fragment.m_ctrlnetT[2][2]; - m_ctrlnetT[3][0] = src_fragment.m_ctrlnetT[3][0]; m_ctrlnetT[3][1] = src_fragment.m_ctrlnetT[3][1]; m_ctrlnetT[3][2] = src_fragment.m_ctrlnetT[3][2]; + m_ctrlnetC[0] = src_fragment.m_ctrlnetC[0]; + m_ctrlnetC[1] = src_fragment.m_ctrlnetC[1]; + m_ctrlnetC[2] = src_fragment.m_ctrlnetC[2]; + m_ctrlnetC[3] = src_fragment.m_ctrlnetC[3]; + + m_ctrlnetK[0] = src_fragment.m_ctrlnetK[0]; + m_ctrlnetK[1] = src_fragment.m_ctrlnetK[1]; + m_ctrlnetK[2] = src_fragment.m_ctrlnetK[2]; + m_ctrlnetK[3] = src_fragment.m_ctrlnetK[3]; + + m_pack_rect[0][0] = src_fragment.m_pack_rect[0][0]; + m_pack_rect[0][1] = src_fragment.m_pack_rect[0][1]; + m_pack_rect[1][0] = src_fragment.m_pack_rect[1][0]; + m_pack_rect[1][1] = src_fragment.m_pack_rect[1][1]; + m_pack_rect[2][0] = src_fragment.m_pack_rect[2][0]; + m_pack_rect[2][1] = src_fragment.m_pack_rect[2][1]; + m_pack_rect[3][0] = src_fragment.m_pack_rect[3][0]; + m_pack_rect[3][1] = src_fragment.m_pack_rect[3][1]; + if ( display_density > 8 && ON_UNSET_UINT_INDEX != display_density) return ON_SUBD_RETURN_ERROR(false); @@ -1952,6 +2471,35 @@ bool ON_SubDMeshFragment::CopyFrom( ON_SubDMeshFragment::Internal_Set3dPointArrayToNan(m_T, V_count, m_T_stride); } } + + if (V_count <= ColorCapacity()) + { + // copy m_C[] + if (V_count <= src_fragment.ColorCount()) + { + SetColorsExist(true); + ON_Color*c = m_C; + const size_t c_stride = m_C_stride; + i_stride = di * src_fragment.m_C_stride; + j_stride = m * dj * src_fragment.m_C_stride; + for (unsigned int j = 0; j < m; j += dj) + { + const ON_Color* src_c = src_fragment.m_C + j * j_stride; + for (unsigned int i = 0; i < m; i += di) + { + *c = *src_c; + c += c_stride; + src_c += i_stride; + } + } + } + else + { + ON_Color* c = m_C; + for (ON_Color* c1 = c + V_count * m_C_stride; c < c1; c += m_C_stride) + *c = ON_Color::UnsetColor; + } + } } SetVertexCount(V_count); @@ -1973,24 +2521,13 @@ ON_SubDMeshFragment* ON_SubDMeshImpl::CopyCallbackFragment( if ( 0 == callback_fragment->PointCount() || 0 == callback_fragment->NormalCount() ) return ON_SUBD_RETURN_ERROR(nullptr); - // m_fsp.AllocateElement() allocates room for the fragment and the m_P[], m_N[], and m_T[] arrays. + // m_fsp.AllocateElement() allocates room for the fragment and the m_P[], m_N[], m_T[], and m_C[] arrays. ON_SubDMeshFragment* fragment = (ON_SubDMeshFragment*)m_fsp.AllocateElement(); if ( nullptr == fragment) return ON_SUBD_RETURN_ERROR(nullptr); - double* P = (double*)(fragment + 1); - double* N = P + 3 * m_fragment_point_count; - double* T = N + 3 * m_fragment_point_count; - - fragment->SetUnmanagedVertexCapacity(m_fragment_point_count); // m_fsp manages array memory - fragment->SetVertexCount(m_fragment_point_count); - fragment->m_P = P; - fragment->m_P_stride = 3; - fragment->m_N = N; - fragment->m_N_stride = 3; - fragment->m_T = T; - fragment->m_T_stride = 3; - + // The memory for the arrays in this case is managed by m_fsp[]. + fragment->Internal_LayoutArrays(false, (double*)(fragment + 1), m_fragment_point_count); fragment->CopyFrom(*callback_fragment); ChangeContentSerialNumber(); @@ -2710,6 +3247,50 @@ void ON_SubDMesh::Swap( } } +void ON_SubDDisplayParameters::Dump(class ON_TextLog& text_log) const +{ + text_log.Print(L"DisplayDensity = %u", DisplayDensity()); + switch (DisplayDensity()) + { + case ON_SubDDisplayParameters::ExtraCoarseDensity: + text_log.Print(L" (ExtraCoarse)"); + break; + case ON_SubDDisplayParameters::CoarseDensity: + text_log.Print(L" (Coarse)"); + break; + case ON_SubDDisplayParameters::MediumDensity: + text_log.Print(L" (Medium)"); + break; + case ON_SubDDisplayParameters::FineDensity: + text_log.Print(L" (Fine)"); + break; + case ON_SubDDisplayParameters::ExtraFineDensity: + text_log.Print(L" (ExtraFine)"); + break; + case ON_SubDDisplayParameters::MaximumDensity: + text_log.Print(L" (Maximum)"); + break; + } + text_log.PrintNewLine(); + + text_log.Print(L"MeshLocation = "); + switch (MeshLocation()) + { + case ON_SubDComponentLocation::Unset: + text_log.Print(L"Unset\n"); + break; + case ON_SubDComponentLocation::ControlNet: + text_log.Print(L"ControlNet\n"); + break; + case ON_SubDComponentLocation::Surface: + text_log.Print(L"Surface\n"); + break; + default: + text_log.Print(L"invalid (%u)\n",static_cast(MeshLocation())); + break; + } +} + const ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensity( unsigned int display_density ) @@ -2722,6 +3303,76 @@ const ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensit return limit_mesh_parameters; } +const ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromMeshDensity( + double normalized_mesh_density +) +{ + normalized_mesh_density = ON_MeshParameters::ClampMeshDensityValue(normalized_mesh_density); + + if (normalized_mesh_density >= 0.0 && normalized_mesh_density <= 1.0) + { + if (0.0 == normalized_mesh_density) + return ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::MinimumDensity); + if (1.0 == normalized_mesh_density) + return ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::MaximumDensity); + + + unsigned int subd_display_density = ON_SubDDisplayParameters::Default.DisplayDensity(); + + + // The assignment of slider_value to subd_display_density was arrived at with a long testing period + // including all of Rhino 6 limited subd support and ending with Rhino 7 WIP in June 2020. + // June 2020 mapping of slide_value to subd_display_density + // + // [0.0, ON_ZERO_TOLERANCE] -> 1 = ExtraCoarseDensity + // (ON_ZERO_TOLERANCE, 0.17) -> 2 = CoarseDensity + // [0.17, 0.33) -> 3 = MediumDensity + // [0.33, 0.75] -> 4 = FineDensity + // (0.75, 1 - ON_ZERO_TOLERANCE) -> 5 = ExtraFineDensity + // [1 - ON_ZERO_TOLERANCE, 1.0] -> 6 = MaximumDensity + // Invalid input -> ON_SubDDisplayParameters::DefaultDensity; + // + // Beginning June 20, 2020 the values were slightly adjusted so "simple mesh" UI documentation + // can use more friendly looking values. + // + // [0.0, ON_ZERO_TOLERANCE] -> 1 = ExtraCoarseDensity + // (ON_ZERO_TOLERANCE, 0.20) -> 2 = CoarseDensity + // [0.20, 0.35) -> 3 = MediumDensity + // [0.35, 0.75] -> 4 = FineDensity + // (0.75, 1 - ON_ZERO_TOLERANCE) -> 5 = ExtraFineDensity + // [1 - ON_ZERO_TOLERANCE, 1.0] -> 6 = MaximumDensity + // Invalid input -> ON_SubDDisplayParameters::DefaultDensity; + // + // If you make changes, you must provide name and YouTrack issues link so there is a clear + // history of the reasons the changes were made. You must also update the comments in the + // function declaration. + // Always include the SubD Rhino Logo in your tests. + // + // Be conservative about returning values > 4. + // 5 generates 1024 mesh quads per subd quad and 6 generates 4096 mesh quads per subd quad. + // + // Values of subd_display_density < 4 are visibly chunky on simple subd models (spheres, Rhino subd logo, ...) + // + if (normalized_mesh_density <= ON_ZERO_TOLERANCE) + subd_display_density = ON_SubDDisplayParameters::ExtraCoarseDensity; + else if (normalized_mesh_density < 0.20) + subd_display_density = ON_SubDDisplayParameters::CoarseDensity; + else if (normalized_mesh_density < 0.35) + subd_display_density = ON_SubDDisplayParameters::MediumDensity; + else if (normalized_mesh_density <= 0.75) + subd_display_density = ON_SubDDisplayParameters::FineDensity; + else if (normalized_mesh_density <= 1.0 - ON_ZERO_TOLERANCE) + subd_display_density = ON_SubDDisplayParameters::ExtraFineDensity; + else if (normalized_mesh_density >= 1.0 - ON_ZERO_TOLERANCE) + subd_display_density = ON_SubDDisplayParameters::MaximumDensity; + + return ON_SubDDisplayParameters::CreateFromDisplayDensity(subd_display_density); + } + + ON_ERROR("Invalid slider_value parameter."); + return ON_SubDDisplayParameters::Default; +} + unsigned int ON_SubDDisplayParameters::DisplayDensity() const { return m_display_density; @@ -2743,34 +3394,12 @@ void ON_SubDDisplayParameters::SetMeshLocation(ON_SubDComponentLocation mesh_loc m_bControlNetMesh = (ON_SubDComponentLocation::ControlNet == mesh_location) ? true : false; } -bool ON_SubDDisplayParameters::AddNgons() const -{ - return m_bSkipMeshNgons ? false : true; -} - -void ON_SubDDisplayParameters::SetAddNgons(bool bAddNgons) -{ - m_bSkipMeshNgons = bAddNgons ? false : true; -} - -bool ON_SubDDisplayParameters::AddFakeEvaluationParameters() const -{ - return m_bSkipFakeEvaluationParameters ? false : true; -} - -void ON_SubDDisplayParameters::SetAddFakeEvaluationParameters(bool bAddFakeEvaluationParameters) -{ - m_bSkipFakeEvaluationParameters = bAddFakeEvaluationParameters ? false : true; -} - unsigned char ON_SubDDisplayParameters::EncodeAsUnsignedChar() const { unsigned char encoded_parameters; if ( ON_SubDDisplayParameters::Default.m_display_density == m_display_density && ON_SubDDisplayParameters::Default.MeshLocation() == MeshLocation() - && ON_SubDDisplayParameters::Default.m_bSkipMeshNgons == m_bSkipMeshNgons - && ON_SubDDisplayParameters::Default.m_bSkipFakeEvaluationParameters == m_bSkipFakeEvaluationParameters ) { // use defaults @@ -2792,24 +3421,12 @@ unsigned char ON_SubDDisplayParameters::EncodeAsUnsignedChar() const ? ON_SubDDisplayParameters::subd_mesh_location_bit : 0; - const unsigned char skip_ngons - = (m_bSkipMeshNgons) - ? ON_SubDDisplayParameters::subd_mesh_skip_ngons_bit - : 0; - - const unsigned char skip_fakeevalparams - = (m_bSkipFakeEvaluationParameters) - ? ON_SubDDisplayParameters::subd_mesh_skip_fakeevalparams_bit - : 0; - const unsigned char nondefault_settings = ON_SubDDisplayParameters::subd_mesh_nondefault_bit; encoded_parameters = nondefault_settings | density_as_char | location - | skip_ngons - | skip_fakeevalparams ; } return encoded_parameters; @@ -2825,28 +3442,22 @@ const ON_SubDDisplayParameters ON_SubDDisplayParameters::DecodeFromUnsignedChar( { const unsigned char density = (ON_SubDDisplayParameters::subd_mesh_density_mask & encoded_parameters); const unsigned char location = (ON_SubDDisplayParameters::subd_mesh_location_bit & encoded_parameters); - const unsigned char skip_ngons = (ON_SubDDisplayParameters::subd_mesh_skip_ngons_bit & encoded_parameters); - const unsigned char skip_fakeevalparams = (ON_SubDDisplayParameters::subd_mesh_skip_fakeevalparams_bit & encoded_parameters); p.m_display_density = (unsigned int)density; if (0 != location) p.SetMeshLocation( ON_SubDComponentLocation::ControlNet ); - if (0 != skip_ngons) - p.m_bSkipMeshNgons = true; - if (0 != skip_fakeevalparams) - p.m_bSkipFakeEvaluationParameters = true; } return p; } -bool ON_SubDDisplayParameters::UseMultipleThreads() const +ON_SubDDisplayParameters::Context ON_SubDDisplayParameters::ContextForExperts() const { - return m_bUseMultipleThreads; + return m_context; } -void ON_SubDDisplayParameters::SetUseMultipleThreads(bool bUseMultipleThreads) +void ON_SubDDisplayParameters::SetContextForExperts(ON_SubDDisplayParameters::Context context) { - m_bUseMultipleThreads = bUseMultipleThreads ? true : false; + m_context = context; } ON_Terminator * ON_SubDDisplayParameters::Terminator() const diff --git a/opennurbs_subd_frommesh.cpp b/opennurbs_subd_frommesh.cpp index 3a8371ae..2aab9faa 100644 --- a/opennurbs_subd_frommesh.cpp +++ b/opennurbs_subd_frommesh.cpp @@ -26,81 +26,208 @@ //////////////////////////////////////////////////////////////// */ -struct ON_MeshNGonEdge +class ON_MeshNGonEdge { - unsigned int i; - unsigned int j; - unsigned int Ni; - unsigned int Nj; - unsigned int ngon_index; - ON_SubD::EdgeTag edge_tag; - class ON_SubDEdge* e; +public: + // In ON_Mesh to ON_SubD, there is a ON_MeshNGonEdge for each mesh ngon edge. + // So, for an interior edge, there will be 2 ON_MeshNGonEdge values, one + // for each attached ngon. + // Sorting ON_MeshNGonEdge by m_u.mesh_edge_id will group the these + // edges together. + static const ON_MeshNGonEdge Unset; + static const ON_MeshNGonEdge Create( + unsigned candidate_subd_face_id, + unsigned mesh_Vi, + unsigned mesh_Vj, + const unsigned int* mesh_point_id + ); + + const ON_MeshNGonEdge Reversed() const + { + if (IsSet()) + { + ON_MeshNGonEdge e = *this; + + e.m_mesh_Vi = m_mesh_Vj; + e.m_mesh_Vj = m_mesh_Vi; + + if (2 == m_u_status) + { + e.m_u.subd_edgeptr = m_u.subd_edgeptr.Reversed(); + } + + return e; + } + return ON_MeshNGonEdge::Unset; + } + + static int CompareMeshEdgeTopologyId(const void* lhs, const void* rhs); + + /* + Returns: + True if a and b reference the same topological edge and the normals + at both ends of the edge are different + */ + static bool TagEdgeAsCrease( + const ON_MeshNGonEdge& a, + const ON_MeshNGonEdge& b, + const unsigned int* mesh_point_id + ); + + const ON_2udex PointIds( + const unsigned int* mesh_point_id + ) const + { + return (nullptr != mesh_point_id) ? ON_2udex(mesh_point_id[m_mesh_Vi], mesh_point_id[m_mesh_Vj]) : ON_2udex::Zero; + } + + + unsigned int m_sud_face_id; // likely ON_SubDFace.m_id value (unless invlalid input causes some to be skipped) + unsigned int m_mesh_Vi; // ON_Mesh vertex index + unsigned int m_mesh_Vj; // ON_Mesh vertex index + +private: + unsigned char m_u_status; // 0 = u is unset, 1 = m_u.mesh_edge_topology_id is set, 2 = m_u.subd_edgeptr is set + union + { + ON_2udex mesh_edge_topology_id; // uniquely identifies mesh edge toplogically - independent of vertex order + ON_SubDEdgePtr subd_edgeptr; + } m_u; + +public: + + const ON_2udex EdgeTopologyId() const + { + return 1 == m_u_status ? m_u.mesh_edge_topology_id : ON_2udex::Zero; + } + + const ON_SubDEdgePtr EdgePtr() const + { + return 2 == m_u_status ? m_u.subd_edgeptr : ON_SubDEdgePtr::Null; + } + + bool SetEdgePtr(ON_SubDEdgePtr eptr) + { + if (1 == m_u_status) + { + m_u_status = eptr.IsNull() ? 0 : 2; + m_u.subd_edgeptr = eptr; + return true; + } + return ON_SUBD_RETURN_ERROR(false); + } + + bool IsSet() const + { + return 0 != m_u_status; + } + }; -static int compareUnorderedEdge(const void* a, const void* b) +static ON_MeshNGonEdge Internal_CreateUnsetMeshNGonEdge() +{ + ON_MeshNGonEdge unset; + memset(&unset, 0, sizeof(unset)); + return unset; +} + +const ON_MeshNGonEdge ON_MeshNGonEdge::Unset = Internal_CreateUnsetMeshNGonEdge(); + +const ON_MeshNGonEdge ON_MeshNGonEdge::Create( + unsigned candidate_sud_face_id, + unsigned mesh_Vi, + unsigned mesh_Vj, + const unsigned int* mesh_point_id +) +{ + for(;;) + { + if (candidate_sud_face_id <= 0) + break; + if (mesh_Vi == mesh_Vj) + break; + ON_MeshNGonEdge e = ON_MeshNGonEdge::Unset; + const unsigned a = mesh_point_id[mesh_Vi]; + const unsigned b = mesh_point_id[mesh_Vj]; + if (a < b) + { + e.m_u.mesh_edge_topology_id.i = a; + e.m_u.mesh_edge_topology_id.j = b; + e.m_u_status = 1; + } + else if (a > b) + { + e.m_u.mesh_edge_topology_id.i = b; + e.m_u.mesh_edge_topology_id.j = a; + e.m_u_status = 1; + } + else + break; + e.m_mesh_Vi = mesh_Vi; + e.m_mesh_Vj = mesh_Vj; + e.m_sud_face_id = candidate_sud_face_id; + return e; + } + return ON_SUBD_RETURN_ERROR(ON_MeshNGonEdge::Unset); +} + + + +int ON_MeshNGonEdge::CompareMeshEdgeTopologyId(const void* lhs, const void* rhs) { // compare location ids - unsigned int ea[2] = { ((const unsigned int*)a)[0], ((const unsigned int*)a)[1] }; - unsigned int eb[2] = { ((const unsigned int*)b)[0], ((const unsigned int*)b)[1] }; + const ON_2udex lhs_edge_topology_id = ((const ON_MeshNGonEdge*)lhs)->EdgeTopologyId(); + const ON_2udex rhs_edge_topology_id = ((const ON_MeshNGonEdge*)rhs)->EdgeTopologyId(); - // unordered compare - unsigned int k; - if (ea[0] > ea[1]) - { - k = ea[0]; - ea[0] = ea[1]; - ea[1] = k; - } - if (eb[0] > eb[1]) - { - k = eb[0]; - eb[0] = eb[1]; - eb[1] = k; - } - - // compare - if (ea[0] < eb[0]) + if (lhs_edge_topology_id.i < rhs_edge_topology_id.i) return -1; - if (ea[0] > eb[0]) + if (lhs_edge_topology_id.i > rhs_edge_topology_id.i) return 1; - if (ea[1] < eb[1]) + if (lhs_edge_topology_id.j < rhs_edge_topology_id.j) return -1; - if (ea[1] > eb[1]) + if (lhs_edge_topology_id.j > rhs_edge_topology_id.j) return 1; return 0; } -static bool TagCoincidentEdgeAsCrease( +bool ON_MeshNGonEdge::TagEdgeAsCrease( const ON_MeshNGonEdge& a, - const ON_MeshNGonEdge& b + const ON_MeshNGonEdge& b, + const unsigned int* mesh_point_id ) { - if (a.i == b.i && a.j == b.j) + if (1 != a.m_u_status || 1 != b.m_u_status + || a.m_u.mesh_edge_topology_id.i != b.m_u.mesh_edge_topology_id.i + || a.m_u.mesh_edge_topology_id.j != b.m_u.mesh_edge_topology_id.j + ) { - // a and b are coincident and have the same direction - if (a.Ni != b.Ni && a.Nj != b.Nj) - return true; + // a and b should currently have equal topology ids + return ON_SUBD_RETURN_ERROR(false); } - else if (a.i == b.j && a.j == b.i) + + const bool bFlipA = (a.m_u.mesh_edge_topology_id.i != mesh_point_id[a.m_mesh_Vi]) ? true : false; + const bool bFlipB = (b.m_u.mesh_edge_topology_id.i != mesh_point_id[b.m_mesh_Vi]) ? true : false; + + if (bFlipA == bFlipB) { - // a and b are coincident and have opposite directions - if (a.Ni != b.Nj && a.Nj != b.Ni) - return true; + // a and b have the same direction + if (a.m_mesh_Vi != b.m_mesh_Vi && a.m_mesh_Vj != b.m_mesh_Vj) + return true; // ON_Mesh vertex indices are different at both ends } else { - // a and b are not coincident - // The calling code expects a and be to be coninicdent - ON_SubDIncrementErrorCount(); + // a and b have opposite directions + if (a.m_mesh_Vi != b.m_mesh_Vj && a.m_mesh_Vj != b.m_mesh_Vi) + return true; // ON_Mesh vertex indices are different at both ends } return false; } static bool Internal_CandidateTagIsBetterCreaseEnd( - ON_SubD::VertexTag current_tag, + ON_SubDVertexTag current_tag, const ON_SubDVertex* candidate ) { @@ -108,23 +235,23 @@ static bool Internal_CandidateTagIsBetterCreaseEnd( return false; switch(current_tag) { - case ON_SubD::VertexTag::Unset: - if (ON_SubD::VertexTag::Unset != candidate->m_vertex_tag ) + case ON_SubDVertexTag::Unset: + if (ON_SubDVertexTag::Unset != candidate->m_vertex_tag ) return true; break; - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: if (candidate->IsDartOrCreaseOrCorner()) return true; break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: if (candidate->IsCreaseOrCorner()) return true; break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: if (candidate->IsCorner()) return true; break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: break; default: break; @@ -162,7 +289,7 @@ static bool Internal_CreateFromMesh_ValidateNonmanifoldVertexSector( const ON_SubDVertex* v1 = e1->OtherEndVertex(v); if (nullptr == v1) { - ON_SUBD_ERROR("invalid subd topology."); + ON_SUBD_ERROR("invalid SubD topology."); return false; // invalid topology } const double d = dir * e1->ControlNetDirectionFrom(v); @@ -182,7 +309,7 @@ static bool Internal_CreateFromMesh_ValidateNonmanifoldVertexSector( if (nullptr == other_crease) { - ON_SUBD_ERROR("bug in nonmanifold mesh to subd code."); + ON_SUBD_ERROR("bug in nonmanifold mesh to SubD code."); return false; } if (other_crease != e) @@ -190,21 +317,21 @@ static bool Internal_CreateFromMesh_ValidateNonmanifoldVertexSector( if (nullptr == best_candidate_edge) { - ON_SUBD_ERROR("bug in nonmanifold mesh to subd code."); + ON_SUBD_ERROR("bug in nonmanifold mesh to SubD code."); return false; } // make best_candidate_edge a crease so corner sector is valid - const_cast(best_candidate_edge)->m_edge_tag = ON_SubD::EdgeTag::Crease; + const_cast(best_candidate_edge)->m_edge_tag = ON_SubDEdgeTag::Crease; const ON_SubDVertexEdgeProperties best_ep = best_canditate_v1->EdgeProperties(); - ON_SubD::VertexTag vtag; + ON_SubDVertexTag vtag; if ( 1 == best_ep.m_crease_edge_count && 2 == best_ep.m_min_edge_face_count && 2 == best_ep.m_max_edge_face_count) - vtag = ON_SubD::VertexTag::Dart; + vtag = ON_SubDVertexTag::Dart; else if ( 2 == best_ep.m_crease_edge_count && best_ep.m_max_edge_face_count <= 2 ) - vtag = ON_SubD::VertexTag::Crease; + vtag = ON_SubDVertexTag::Crease; else - vtag = ON_SubD::VertexTag::Corner; + vtag = ON_SubDVertexTag::Corner; if (false == Internal_CandidateTagIsBetterCreaseEnd(vtag, best_canditate_v1)) const_cast(best_canditate_v1)->m_vertex_tag = vtag; @@ -218,7 +345,7 @@ static void Internal_CreateFromMesh_ValidateNonmanifoldVertex( { if ( nullptr == v - || ON_SubD::VertexTag::Corner != v->m_vertex_tag + || ON_SubDVertexTag::Corner != v->m_vertex_tag ) return; @@ -227,7 +354,7 @@ static void Internal_CreateFromMesh_ValidateNonmanifoldVertex( const ON_SubDEdge* e = v->Edge(vei); if ( nullptr == e - || ON_SubD::EdgeTag::Crease != e->m_edge_tag + || ON_SubDEdgeTag::Crease != e->m_edge_tag || e->m_face_count <= 2 ) continue; @@ -244,7 +371,7 @@ static void Internal_CreateFromMesh_ValidateNonmanifoldVertex( sit.Initialize(f, 1, v); if (e != sit.CurrentEdge(0)) { - ON_SUBD_ERROR("bug in nonmanifold mesh to subd code."); + ON_SUBD_ERROR("bug in nonmanifold mesh to SubD code."); continue; } } @@ -347,8 +474,34 @@ ON_SubD* ON_SubD::CreateFromMesh( return subd_from_mesh; } +static double Internal_FaceCornerAngleRadians(const ON_SubDVertex* v, const ON_SubDFace* f) +{ + for (;;) + { + if (nullptr == f) + break; + + if (false == f->IsConvex()) + break; + + // We could remove the f->IsConvex() test, but it might make make a bad situaton worse. + // As of May 2020, nobody has complained about this approach. + // the code below assumes the face is convex + + const unsigned fvi = f->VertexIndex(v); + const ON_SubDComponentPtrPair pair = f->VertexEdgePair(fvi); + if (false == pair.BothAreNotNull()) + break; + const double a = ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(pair.First().EdgePtr().Reversed(), pair.Second().EdgePtr()); + if (false == (a > 0.0 && a < ON_PI)) + break; + return a; + } + return ON_DBL_QNAN; +} + ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( - const class ON_Mesh* level_zero_mesh, + const class ON_Mesh* mesh, const class ON_SubDFromMeshParameters* from_mesh_options, ON_SubD* subd ) @@ -360,7 +513,7 @@ ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( subdimple->Clear(); } - if (nullptr == level_zero_mesh) + if (nullptr == mesh) return nullptr; ON_Workspace ws; @@ -368,120 +521,106 @@ ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( if (nullptr == from_mesh_options) from_mesh_options = &ON_SubDFromMeshParameters::Smooth; - ON_3dPointListRef mesh_points(level_zero_mesh); - const unsigned int mesh_point_count = mesh_points.PointCount(); + ON_3dPointListRef mesh_points(mesh); + const unsigned mesh_point_count = mesh_points.PointCount(); if (mesh_point_count < 3) return nullptr; - const ON_MeshFaceList mesh_face_list(level_zero_mesh); + const ON_MeshFaceList mesh_face_list(mesh); const unsigned int mesh_face_count = mesh_face_list.FaceCount(); if ( mesh_face_count < 1 ) return nullptr; - const ON_3fVector* pointNormal - = level_zero_mesh->HasVertexNormals() - ? level_zero_mesh->m_N.Array() - : nullptr; - - const_cast(level_zero_mesh)->NgonMap(true); - ON_MeshNgonIterator ngonit(level_zero_mesh); + const_cast(mesh)->NgonMap(true); + ON_MeshNgonIterator ngonit(mesh); if (nullptr == ngonit.FirstNgon()) return nullptr; - unsigned int* Vindex = (unsigned int*)ws.GetIntMemory(mesh_point_count); - unsigned int* Vid = level_zero_mesh->GetVertexLocationIds(0, (unsigned int*)ws.GetIntMemory(mesh_point_count), Vindex); - if (nullptr == Vid) + //////////////////////////////////////////////////////////////////////////////////////// + // + // Conceptually sort the vertices of mesh into groups that are at the same 3d location. + // For each group of mesh vertices at a particular spot, there will typically be + // one SubD vertex. However, mesh vertices that are not referenced by mesh faces + // are ignored so it is possible that some mesh vertices will not have a corresponding + // SubD vertex. + // + // mesh_point_count = mesh->MeshVertexCount(); + // mesh_point_id[] + // mesh_point_id[] has mesh_point_count values. + // mesh_point_id[i] = mesh_point_id[j] if and only if mesh->m_V[i] and mesh->m_V[j] are coincident. + // Values in mesh_point_id[] run from 0 to mesh_point_id_count-1. + // There are mesh_point_id_count unique locations. + // mesh_point_map[] is a permutation of (0, ..., mesh_point_count-1). + // mesh_point_map[] sorts mesh->m_V[] into groups of vertices with the same mesh_id / location. + // 0 == mesh_point_id[mesh_point_map[0]] <= ... <= mesh_point_id[mesh_point_map[mesh_point_count-1]] = mesh_point_id_count-1. + // mesh_vertex_status[] mesh_point_id_count char values. + // mesh_vertex_status[mesh_vi] = 1 if that mesh vertex id/location has a corresponding SubD vertex. 0 if the mesh vertex will be ignored. + unsigned int* buffer1 = (unsigned int*)ws.GetIntMemory(mesh_point_count); + const unsigned int* mesh_point_id = mesh->GetVertexLocationIds(0, (unsigned int*)ws.GetIntMemory(mesh_point_count), buffer1); + if (nullptr == mesh_point_id) return nullptr; - unsigned int VidCount = Vid[Vindex[mesh_point_count - 1]] + 1; - unsigned char* vertexIsReferenced = (unsigned char*)ws.GetMemory(VidCount*sizeof(vertexIsReferenced[0])); - memset(vertexIsReferenced, 0, VidCount*sizeof(vertexIsReferenced[0])); - // Vid[] - // Vid[] has mesh_point_count values. - // Vid[i] = Vid[j] if and only if mesh->m_V[i] and mesh->m_V[j] are coincident. - // Values in Vid[] run from 0 to VidCount-1. - // There are VidCount unique locations. - // Vindex[] is a permutation of (0, ..., mesh_point_count-1) - // 0 == Vid[Vindex[0]] <= ... <= Vid[Vindex[mesh_point_count-1]] = VidCount-1. + const unsigned int* mesh_point_map = buffer1; + // mesh_point_id_count = number of unique vertex locations in mesh->m_V[] array. + const unsigned mesh_point_id_count = mesh_point_id[mesh_point_map[mesh_point_count - 1]] + 1; - //const bool bConcaveCornerTest - // = nullptr != crease_parameters - // && crease_parameters->ConcaveCornerTestIsEnabled(); - - //const double min_cos_concave_corner_angle - // = bConcaveCornerTest - // ? (crease_parameters->MaximumConcaveCornerAngleRadians() < ON_PI ? cos(crease_parameters->MaximumConcaveCornerAngleRadians()) : -2.0) - // : 2.0; - - double max_cos_crease_angle = ON_UNSET_VALUE; - double min_crease_angle_radians = -ON_UNSET_VALUE; + unsigned char* mesh_vertex_status = (unsigned char*)ws.GetMemory(mesh_point_id_count*sizeof(mesh_vertex_status[0])); + memset(mesh_vertex_status, 0, mesh_point_id_count*sizeof(mesh_vertex_status[0])); ON_SubDFromMeshParameters::InteriorCreaseOption crease_test = (nullptr != from_mesh_options) - ? from_mesh_options->InteriorCreaseTest() + ? from_mesh_options->GetInteriorCreaseOption() : ON_SubDFromMeshParameters::InteriorCreaseOption::None; - if (ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshCrease == crease_test && nullptr != pointNormal ) - { - double min_angle = from_mesh_options->MinimumCreaseAngleRadians(); - if (min_angle >= 0.0 && min_angle < ON_PI) - { - min_crease_angle_radians = min_angle; - if ( 0.0 == min_crease_angle_radians) - max_cos_crease_angle = 1.0; - else - { - max_cos_crease_angle = cos(min_crease_angle_radians); - if ( max_cos_crease_angle >= 1.0 ) - max_cos_crease_angle = 1.0-ON_EPSILON; - } - } - else - { - crease_test = ON_SubDFromMeshParameters::InteriorCreaseOption::None; - } - } - else if (ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshEdge != crease_test) + if (ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshDoubleEdge != crease_test) { crease_test = ON_SubDFromMeshParameters::InteriorCreaseOption::None; } - - // Get sub-D edge list + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Get mesh edge list + // unsigned int subd_vertex_count = 0; unsigned int mesh_edge_count = 0; unsigned int max_subd_face_edge_count = 0; - ON_SimpleArray mesh_edges(4 * level_zero_mesh->m_F.UnsignedCount()); - struct ON_MeshNGonEdge mesh_edge = {}; + ON_SimpleArray mesh_edges(4 * mesh->m_F.UnsignedCount()); unsigned int quad_vi[4]; ON_MeshNGonEdge quad_edges[4] = {}; bool bMergeColinearEdges = false; - const ON_MeshFaceList level_zero_mesh_face_list(level_zero_mesh); - - unsigned int subd_face_index = 0; + unsigned int subd_face_count = 0; + unsigned int mesh_Vi; + unsigned int mesh_Vj; for (const ON_MeshNgon* ngon = ngonit.FirstNgon(); nullptr != ngon; ngon = ngonit.NextNgon()) { if (ngon->m_Vcount < 3 || ngon->m_Fcount < 1) continue; - const int ngon_orientation = ngon->Orientation(level_zero_mesh_face_list, false); + const int ngon_orientation = ngon->Orientation(mesh_face_list, false); if (0 != ngon_orientation) { - mesh_edge.ngon_index = subd_face_index; - unsigned int ngon_edge_count = 0; - mesh_edge.j = ngon->m_vi[0]; + mesh_Vj = ngon->m_vi[0]; for (unsigned int nvi = 1; nvi <= ngon->m_Vcount; nvi++) { - mesh_edge.i = mesh_edge.j; - mesh_edge.j = ngon->m_vi[nvi % ngon->m_Vcount]; - if (Vid[mesh_edge.i] == Vid[mesh_edge.j]) + mesh_Vi = mesh_Vj; + mesh_Vj = ngon->m_vi[nvi % ngon->m_Vcount]; + if (mesh_point_id[mesh_Vi] == mesh_point_id[mesh_Vj]) + continue; // coincident vertex locations + ON_MeshNGonEdge& mesh_edge = mesh_edges.AppendNew(); + mesh_edge = ON_MeshNGonEdge::Create(subd_face_count+1, mesh_Vi, mesh_Vj, mesh_point_id); + if (mesh_edge.IsSet()) + { + + ngon_edge_count++; continue; - mesh_edges.Append(mesh_edge); - ngon_edge_count++; + } + ngon_edge_count = 0; + break; } if (ngon_edge_count < 3) { @@ -497,29 +636,19 @@ ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( unsigned int i1 = mesh_edge_count + ngon_edge_count - 1; while (i0 < i1) { - mesh_edge = mesh_edges[i0]; - mesh_edges[i0] = mesh_edges[i1]; - int k = mesh_edge.i; - mesh_edge.i = mesh_edge.j; - mesh_edge.j = k; - mesh_edges[i1] = mesh_edge; - k = mesh_edges[i0].i; - mesh_edges[i0].i = mesh_edges[i0].j; - mesh_edges[i0].j = k; + const ON_MeshNGonEdge mesh_edge = mesh_edges[i0]; + mesh_edges[i0] = mesh_edges[i1].Reversed(); + mesh_edges[i1] = mesh_edge.Reversed(); i0++; i1--; } // Flip middle edge if odd number of edges if (i0 == i1) - { - int k = mesh_edges[i0].i; - mesh_edges[i0].i = mesh_edges[i0].j; - mesh_edges[i0].j = k; - } + mesh_edges[i0] = mesh_edges[i0].Reversed(); } // the ngon created a single subd face - subd_face_index++; + ++subd_face_count; if (ngon_edge_count >= 4) bMergeColinearEdges = true; @@ -538,21 +667,27 @@ ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( continue; unsigned int quad_edge_count = 0; - mesh_edge.ngon_index = subd_face_index; - mesh_edge.j = quad_vi[0]; + mesh_Vj = quad_vi[0]; for (unsigned int fvi = 1; fvi <= 4; fvi++) { - mesh_edge.i = mesh_edge.j; - mesh_edge.j = quad_vi[fvi % 4]; - if (Vid[mesh_edge.i] == Vid[mesh_edge.j]) + mesh_Vi = mesh_Vj; + mesh_Vj = quad_vi[fvi % 4]; + if (mesh_point_id[mesh_Vi] == mesh_point_id[mesh_Vj]) + continue; // coincident vertex locations (always happens on a tri face and can happen on invalid faces) + quad_edges[quad_edge_count] = ON_MeshNGonEdge::Create(subd_face_count+1, mesh_Vi, mesh_Vj, mesh_point_id); + if (quad_edges[quad_edge_count].IsSet()) + { + ++quad_edge_count; continue; - quad_edges[quad_edge_count++] = mesh_edge; + } + quad_edge_count = 0; + break; } if (quad_edge_count >= 3) { // each quad/triangle in the ON_Mesh ngon created a subd face mesh_edges.Append(quad_edge_count,quad_edges); - subd_face_index++; + ++subd_face_count; if( quad_edge_count > max_subd_face_edge_count) max_subd_face_edge_count = quad_edge_count; } @@ -565,22 +700,20 @@ ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( for (/*empty init*/; mesh_edge_count < mesh_edges.UnsignedCount(); mesh_edge_count++) { - mesh_edge = mesh_edges[mesh_edge_count]; - if (0 == vertexIsReferenced[Vid[mesh_edge.i]]) + const ON_MeshNGonEdge& mesh_edge = mesh_edges[mesh_edge_count]; + if (0 == mesh_vertex_status[mesh_point_id[mesh_edge.m_mesh_Vi]]) { - vertexIsReferenced[Vid[mesh_edge.i]] = 1; + mesh_vertex_status[mesh_point_id[mesh_edge.m_mesh_Vi]] = 1; subd_vertex_count++; } - if (0 == vertexIsReferenced[Vid[mesh_edge.j]]) + if (0 == mesh_vertex_status[mesh_point_id[mesh_edge.m_mesh_Vj]]) { - vertexIsReferenced[Vid[mesh_edge.j]] = 1; + mesh_vertex_status[mesh_point_id[mesh_edge.m_mesh_Vj]] = 1; subd_vertex_count++; } } } - const unsigned int subd_face_count = subd_face_index; - if (subd_vertex_count < 3 || mesh_edge_count < 3 || subd_face_count < 1) return nullptr; @@ -602,234 +735,232 @@ ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( bool bHasTaggedVertices = false; bool bHasNonmanifoldCornerVertices = false; - unsigned int* Nid = nullptr; - unsigned int nextNid = 0; - if (ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshCrease == crease_test) - { - Nid = (unsigned int*)ws.GetIntMemory(mesh_point_count); - memset(Nid, 0, mesh_point_count*sizeof(Nid[0])); - nextNid = 1; - } - - ON_SimpleArray< ON_SubDVertex* > V(subd_vertex_count); - VidCount = 0; - + ////////////////////////////////////////////////////////////////////// + // + // create subd vertices + // + // subd_V[mesh_vi] = subd vertex that corresponds to mesh->m_V[]. + // Note that when mesh-m_V[i] and mesh->m_V[j] are the same point, + // then subd_V[i] = subd_V[j]. + // This is common and it happens when mesh_point_id_count < mesh_point_count + // (some distinct mesh points in the mesh->m_V[] array have the same location). + ON_SubDVertex** subd_V = (ON_SubDVertex**)ws.GetMemory(mesh_point_count * sizeof(subd_V[0])); + memset(subd_V, 0, mesh_point_count * sizeof(subd_V[0])); for (unsigned int i = 0; i < mesh_point_count;/*empty iterator*/) { - const unsigned int vid0 = Vid[Vindex[i]]; + const unsigned int vid0 = mesh_point_id[mesh_point_map[i]]; + if (0 == mesh_vertex_status[vid0]) + { + // no edges reference this vertex. + ++i; + continue; + } + unsigned int j; for (j = i + 1; j < mesh_point_count; j++) { - if (vid0 != Vid[Vindex[j]]) + if (vid0 != mesh_point_id[mesh_point_map[j]]) break; } - if (1 == vertexIsReferenced[vid0]) + const ON_3dPoint P = mesh_points[mesh_point_map[i]]; + ON_SubDVertex* subd_vertex = new_subd->AddVertex(ON_SubDVertexTag::Smooth, &P.x); + while (i < j) + subd_V[mesh_point_map[i++]] = subd_vertex; + } + + + //////////////////////////////////////////////////////////////////////// + // + // If we are adding interior crease, set the mesh_edge_ref.m_mesh_Ni / m_mesh_Nj values used to detect creases. + // + if ( ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshDoubleEdge != crease_test ) + { + crease_test = ON_SubDFromMeshParameters::InteriorCreaseOption::None; + } + + ////////////////////////////////////////////////////////////////////// + // + // Create SubD edges. + // + // An subd interior edge will have 2 consecutive element in mesh_edges[]. + // A subd non-manifold edge with k faces with have k consecutive element in mesh_edges[]. + // A subd boundary edge will have 1 element in mesh_edges[]. + // A subd wire edge will have 1 element in mesh_edges[]. + // + + // mesh_edge_map[] is used to sort the sort mesh_edges[] into groups that correspond to the same SubD edge. + // The order of mesh_edges[] cannot be changed because the current order is neede to efficiently create the SubD faces. + unsigned int* mesh_edge_map = (unsigned int*)ws.GetMemory(mesh_edges.UnsignedCount() * sizeof(mesh_edge_map[0])); + ON_Sort( + ON::sort_algorithm::quick_sort, + mesh_edge_map, mesh_edges.Array(), + mesh_edges.UnsignedCount(), + sizeof(ON_MeshNGonEdge), + ON_MeshNGonEdge::CompareMeshEdgeTopologyId + ); + + for (unsigned int i = 0; i < mesh_edges.UnsignedCount(); /*empty iterator*/) + { + const ON_MeshNGonEdge& mesh_edge0 = mesh_edges[mesh_edge_map[i]]; + const ON_2udex topology_id0 = mesh_edge0.EdgeTopologyId(); + + // get the group of mesh_edges[] that will use the same SubD edge. + // and determine if that edge should be tagged as a crease. + ON_SubDEdgeTag edge_tag = ON_SubDEdgeTag::Smooth; + unsigned j = i + 1; + for (/*empty init*/; j < mesh_edges.UnsignedCount(); ++j) { - // vertex is referenced by an edge - if (nullptr != Nid) + ON_MeshNGonEdge& mesh_edge1 = mesh_edges[mesh_edge_map[j]]; + const ON_2udex topology_id1 = mesh_edge1.EdgeTopologyId(); + if (topology_id0.i != topology_id1.i || topology_id0.j != topology_id1.j) + break; + if (ON_SubDFromMeshParameters::InteriorCreaseOption::None != crease_test) { - // When there are 2 or more coincident vertices, - // set normal ids used to detect creased edges. - // This for loop finds normals that should be considered "equal" because - // the angle between them is <= crease_parameters->MinimumCreaseAngleRadians() - for (unsigned int k = i; k < j; k++) + if (ON_MeshNGonEdge::TagEdgeAsCrease(mesh_edge0, mesh_edge1, mesh_point_id)) + edge_tag = ON_SubDEdgeTag::Crease; + } + } + if ( j-i != 2 ) + edge_tag = ON_SubDEdgeTag::Crease; // wire, boundary, or non-manifold edge + + // create the SubD edge. + ON_SubDVertex* v0[2] = { subd_V[mesh_edge0.m_mesh_Vi], subd_V[mesh_edge0.m_mesh_Vj] }; + ON_SubDEdge* e + = (nullptr != v0[0] && nullptr != v0[1] && v0[0]->m_id != v0[1]->m_id) + ? new_subd->AddEdgeWithSectorCoefficients(edge_tag, v0[0], ON_SubDSectorType::IgnoredSectorCoefficient, v0[1], ON_SubDSectorType::IgnoredSectorCoefficient) + : nullptr; + + // Change the mesh_edges[].m_u from the topology id to an ON_SubDEdgePtr. + // We need the ON_SubDEdgePtr below to create SubD faces. + ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Null; + for (/*empty init*/; i < j; ++i) + { + ON_MeshNGonEdge& mesh_edge = mesh_edges[mesh_edge_map[i]]; + if (nullptr != e) + { + const ON_SubDVertex* v[2] = { subd_V[mesh_edge.m_mesh_Vi], subd_V[mesh_edge.m_mesh_Vj] }; + if (v0[0] == v[0] && v0[1] == v[1]) + eptr = ON_SubDEdgePtr::Create(e, 0); + else if (v0[0] == v[1] && v0[1] == v[0]) + eptr = ON_SubDEdgePtr::Create(e, 1); + else { - if (ON_UNSET_UINT_INDEX == Nid[Vindex[k]]) - continue; - - ON_3dVector N0 = pointNormal[Vindex[k]]; - if (false == N0.Unitize()) - { - Nid[Vindex[k]] = ON_UNSET_UINT_INDEX; - continue; - } - - unsigned int thisNid = Nid[Vindex[k]]; - - // search for "equal" normals - for (unsigned int n = k + 1; n < j; n++) - { - if (0 != Nid[Vindex[n]] && 0 != thisNid) - continue; - ON_3dVector N1 = pointNormal[Vindex[n]]; - if (false == N1.Unitize()) - { - Nid[Vindex[k]] = ON_UNSET_UINT_INDEX; - continue; - } - double cos_N0_N1_angle = (N0 == N1) ? 1.0 : N0*N1; - if (cos_N0_N1_angle >= max_cos_crease_angle) - { - // Angle between N0 and N1 is <= crease_parameters->MinimumCreaseAngleRadians() - // so they must have the same id. - if (0 == thisNid) - { - if (0 == Nid[Vindex[n]]) - { - thisNid = nextNid++; - Nid[Vindex[n]] = thisNid; - } - else - { - thisNid = Nid[Vindex[n]]; - } - Nid[Vindex[k]] = thisNid; - } - continue; - } - } - - if (0 == thisNid) - Nid[Vindex[k]] = nextNid++; + ON_SUBD_ERROR("There is a bug in the code above. This should not happen."); + eptr = ON_SubDEdgePtr::Null; } } - - const ON_3dPoint P = mesh_points[Vindex[i]]; - ON_SubDVertex* subd_vertex = new_subd->AddVertex(ON_SubD::VertexTag::Smooth, &P.x); - V.Append(subd_vertex); - while (i < j) - Vid[Vindex[i++]] = VidCount; - VidCount++; - } - else - { - // unreferenced vertex - while (i < j) - Vid[Vindex[i++]] = ON_UNSET_UINT_INDEX; - } - } - - // change mesh_edges[].i and .j from mesh vertex index to to sub-D vertex id. - for (unsigned int i = 0; i < mesh_edges.UnsignedCount(); i++) - { - // set the normal ids from the ON_Mesh m_V[] indices - struct ON_MeshNGonEdge& mesh_edge_ref = mesh_edges[i]; - if (ON_SubDFromMeshParameters::InteriorCreaseOption::AtMeshEdge == crease_test) - { - // All coincident mesh vertices generate interior creases - mesh_edge_ref.Ni = mesh_edge_ref.i; - mesh_edge_ref.Nj = mesh_edge_ref.j; - } - else if (nullptr != Nid) - { - // Coincident mesh vertices with different vertex normals generate interior creases - mesh_edge_ref.Ni = Nid[mesh_edge_ref.i]; - mesh_edge_ref.Nj = Nid[mesh_edge_ref.j]; - } - else - { - // no interior creases - mesh_edge_ref.Ni = 0; - mesh_edge_ref.Nj = 0; - } - - // convert ON_Mesh m_V[] indices into sub-D vertex ids. - mesh_edge_ref.i = Vid[mesh_edge_ref.i]; - mesh_edge_ref.j = Vid[mesh_edge_ref.j]; - } - - // sort the edges - unsigned int* mesh_edge_map = (unsigned int*)ws.GetMemory(mesh_edges.UnsignedCount()*sizeof(mesh_edge_map[0])); - ON_Sort(ON::sort_algorithm::quick_sort, mesh_edge_map, mesh_edges.Array(), mesh_edges.UnsignedCount(), sizeof(mesh_edge), compareUnorderedEdge); - - // change mesh_edges[].e to a temporary edge id - ON__UINT_PTR subd_edge_index = 0; - for (unsigned int i = 0; i < mesh_edges.UnsignedCount(); /*empty iterator*/) - { - // first instance of a new edge - mesh_edge = mesh_edges[mesh_edge_map[i]]; - mesh_edge.edge_tag = ON_SubD::EdgeTag::Smooth; - mesh_edges[mesh_edge_map[i]].e = (ON_SubDEdge*)subd_edge_index; - - unsigned int i0 = i; - for (i++; i < mesh_edges.UnsignedCount() && 0 == compareUnorderedEdge(&mesh_edge, &mesh_edges[mesh_edge_map[i]]); i++) - { - // There were multiple ON_Mesh vertices at at least one end of this edge. - // If the crease_parmeters specified to search for a crease and the - // angle between ON_Mesh vertex normals exceeded the crease tolerance, - // then the edge will be a crease. - if (ON_SubD::EdgeTag::Smooth == mesh_edge.edge_tag) + else { - if (TagCoincidentEdgeAsCrease(mesh_edges[mesh_edge_map[i]],mesh_edge)) - mesh_edge.edge_tag = ON_SubD::EdgeTag::Crease; + eptr = ON_SubDEdgePtr::Null; } - mesh_edges[mesh_edge_map[i]].e = (ON_SubDEdge*)subd_edge_index; // duplicate edge - } - - while (i0 < i) - mesh_edges[mesh_edge_map[i0++]].edge_tag = mesh_edge.edge_tag; - - subd_edge_index++; - } - - // Create the sub-D edges. - for (unsigned int i = 0; i < mesh_edges.UnsignedCount(); /*empty iterator*/) - { - mesh_edge = mesh_edges[mesh_edge_map[i]]; - subd_edge_index = (ON__UINT_PTR)mesh_edge.e; - // Later, some of the ON_SubD::EdgeTag::Smooth tags are changed to ON_SubD::EdgeTag::Crease or ON_SubD::EdgeTag::SmoothX. - mesh_edge.e - = (mesh_edge.i <= mesh_edge.j) - ? new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorCoefficient, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorCoefficient) - : new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorCoefficient, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorCoefficient); - mesh_edges[mesh_edge_map[i]].e = mesh_edge.e; - for (i++; i < mesh_edges.UnsignedCount(); i++) - { - if (subd_edge_index == (ON__UINT_PTR)mesh_edges[mesh_edge_map[i]].e) - { - mesh_edges[mesh_edge_map[i]].e = mesh_edge.e; - continue; - } - break; + mesh_edge.SetEdgePtr(eptr); } } - // Create the sub-D faces. + ////////////////////////////////////////////////////////////////////// + // + // Create the SubD faces. + // + // mesh_edges[] is ordered in groups that form the boundary of each SubD face. + // All the edges that form a boundary of a face have the same m_sud_face_id value. + // If the input mesh is completely valid, this will also be ON_SubDFace m_id value. + // If the input mesh is damaged, some boundaries will not generate a corresponding SubD face. + // + const ON_SubDFromMeshParameters::TextureCoordinatesOption texture_coordinates_option + = nullptr != from_mesh_options + ? from_mesh_options->GetTextureCoordinatesOption() + : ON_SubDFromMeshParameters::TextureCoordinatesOption::None; + const bool bAutomaticTextureCoordinates = ON_SubDFromMeshParameters::TextureCoordinatesOption::Automatic == texture_coordinates_option; + + + // At most one of bCopyMeshMappingTag, bCopyMeshTextureCoordinates, or bPackedTextureCoordinates is true. + const bool bCopyMeshMappingTag + = (bAutomaticTextureCoordinates || ON_SubDFromMeshParameters::TextureCoordinatesOption::CopyMapping == texture_coordinates_option) + && mesh->m_Ttag.IsSet() + ; + const bool bCopyMeshTextureCoordinates + = false == bCopyMeshMappingTag + && (bAutomaticTextureCoordinates || ON_SubDFromMeshParameters::TextureCoordinatesOption::CopyCoordinates == texture_coordinates_option) + && mesh->HasTextureCoordinates() + ; + const bool bPackedTextureCoordinates + = false == bCopyMeshMappingTag + && false == bCopyMeshTextureCoordinates + && (bAutomaticTextureCoordinates || ON_SubDFromMeshParameters::TextureCoordinatesOption::Packed == texture_coordinates_option) + && mesh->HasTextureCoordinates() + ; + ON_SimpleArray< ON_SubDEdgePtr > EP(max_subd_face_edge_count); - unsigned int mesh_edge_index = 0; - for ( subd_face_index = 0; subd_face_index < subd_face_count; subd_face_index++ ) + ON_SimpleArray< ON_3dPoint > face_texture_points(bCopyMeshTextureCoordinates ? max_subd_face_edge_count : 0); + for (unsigned i = 0; i < mesh_edges.UnsignedCount(); /*empty iterator*/) { - while (mesh_edge_index < mesh_edges.UnsignedCount() && mesh_edges[mesh_edge_index].ngon_index < subd_face_index) - mesh_edge_index++; - - if (mesh_edges[mesh_edge_index].ngon_index != subd_face_index) - continue; - EP.SetCount(0); - while (mesh_edge_index < mesh_edges.UnsignedCount() && mesh_edges[mesh_edge_index].ngon_index == subd_face_index) + face_texture_points.SetCount(0); + const unsigned candidate_sud_face_id = mesh_edges[i].m_sud_face_id; + unsigned j = i; + for (/*empty init*/; j < mesh_edges.UnsignedCount(); ++j) { - mesh_edge = mesh_edges[mesh_edge_index]; - EP.Append(ON_SubDEdgePtr::Create(mesh_edge.e, mesh_edge.i <= mesh_edge.j ? 0 : 1)); - mesh_edge_index++; + const ON_MeshNGonEdge& mesh_edge = mesh_edges[j]; + if (candidate_sud_face_id != mesh_edge.m_sud_face_id) + break; + const ON_SubDEdgePtr eptr = mesh_edge.EdgePtr(); + if (eptr.IsNotNull()) + { + EP.Append(eptr); + const ON_SubDVertex* debug_eptr_v[2] = { eptr.RelativeVertex(0), eptr.RelativeVertex(1) }; + const ON_SubDVertex* debug_mesh_v[2] = { subd_V[mesh_edge.m_mesh_Vi], subd_V[mesh_edge.m_mesh_Vj] }; + const bool bOK = debug_eptr_v[0] == debug_mesh_v[0] && debug_eptr_v[1] == debug_mesh_v[1]; + if (false == bOK) ON_SUBD_ERROR("XXX"); + if (bCopyMeshTextureCoordinates) + face_texture_points.Append(ON_3dPoint(mesh->m_T[mesh_edge.m_mesh_Vi])); + } } - - if (EP.UnsignedCount() >= 3) - new_subd->AddFace(EP.Array(), EP.UnsignedCount()); + const unsigned edge_count = EP.UnsignedCount(); + ON_SubDFace* f + = (edge_count >= 3 && j - i == edge_count) + ? new_subd->AddFace(EP.Array(), EP.UnsignedCount()) + : nullptr; + if (nullptr != f) + { + if (bCopyMeshTextureCoordinates) + new_subd->AddFaceTexturePoints(f, face_texture_points.Array(), face_texture_points.UnsignedCount() ); + } + const unsigned actual_subd_face_id = (nullptr != f) ? f->m_id : 0; + for ( /*empty init*/; i < j; ++i) + mesh_edges[i].m_sud_face_id = actual_subd_face_id; } - // Apply "ON_SubD::EdgeTag::Crease" tag to boundary and non-manifold edges and their vertices. + // Apply "ON_SubDEdgeTag::Crease" tag to boundary and non-manifold edges and their vertices. unsigned int interior_crease_count = 0; for (const ON_SubDEdge* edge = new_subd->FirstEdge(); nullptr != edge; edge = edge->m_next_edge) { - if (2 == edge->m_face_count && ON_SubD::EdgeTag::Smooth == edge->m_edge_tag) + // Note: edges are created before faces and we set the edge tag when the edges are created + // assuming that face creation will go as expected. If the mesh is damaged, the face may not + // be created. So, we need to check both edge->m_face_count and edge->m_edge_tag. + if (2 == edge->m_face_count && ON_SubDEdgeTag::Smooth == edge->m_edge_tag) continue; + const_cast(edge)->m_edge_tag = ON_SubDEdgeTag::Crease; + bHasTaggedVertices = true; + const ON_SubDVertexTag vtag + = (1 == edge->m_face_count || 2 == edge->m_face_count) + ? ON_SubDVertexTag::Crease + : ON_SubDVertexTag::Corner; // wire edge or non-manifold edge - const ON_SubD::VertexTag vtag - = (edge->m_face_count > 2) - ? ON_SubD::VertexTag::Corner - : ON_SubD::VertexTag::Crease; - const_cast(edge)->m_edge_tag = ON_SubD::EdgeTag::Crease; + // Depending on the number of creased edges, a vertex on an interior crease here that + // is tagged as ON_SubDVertexTag::Crease here may get changed + // to ON_SubDVertexTag::Dart or ON_SubDVertexTag::Corner below. + for (unsigned int j = 0; j < 2; j++) { const ON_SubDVertex* vertex = edge->m_vertex[j]; - if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag) + if (ON_SubDVertexTag::Smooth == vertex->m_vertex_tag) { const_cast(vertex)->m_vertex_tag = vtag; - if (ON_SubD::VertexTag::Corner == vtag && edge->m_face_count > 2) + if (ON_SubDVertexTag::Corner == vtag && edge->m_face_count > 2) bHasNonmanifoldCornerVertices = true; } } @@ -847,51 +978,39 @@ ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( if (interior_crease_count > 0) { - // Any interior vertex that has exactly one creased edges must be tagged as a dart. - unsigned int k = 0; - for (const ON_SubDEdge* edge = new_subd->FirstEdge(); nullptr != edge; edge = edge->m_next_edge) + // Any interior vertex that has exactly one creased edge must be tagged as a dart. + // Any interior vertex that has more than two creased edges must be tagged as a corner. + unsigned int k = 0; // k = number of interior creases we've tested + for (const ON_SubDEdge* edge = new_subd->FirstEdge(); nullptr != edge && k < interior_crease_count; edge = edge->m_next_edge) { - if (2 != edge->m_face_count || ON_SubD::EdgeTag::Crease != edge->m_edge_tag) + if (2 != edge->m_face_count || ON_SubDEdgeTag::Crease != edge->m_edge_tag) continue; - if ( ON_SubD::VertexTag::Crease != edge->m_vertex[0]->m_vertex_tag - && ON_SubD::VertexTag::Crease != edge->m_vertex[1]->m_vertex_tag) + k++; // processing another interior crease. + + if ( ON_SubDVertexTag::Crease != edge->m_vertex[0]->m_vertex_tag + && ON_SubDVertexTag::Crease != edge->m_vertex[1]->m_vertex_tag) continue; - unsigned int dart_index = 0; - unsigned int dart_count = 0; for (unsigned int j = 0; j < 2; j++) { const ON_SubDVertex* vertex = edge->m_vertex[j]; - if (ON_SubD::VertexTag::Crease != vertex->m_vertex_tag) - continue; - const ON_SubDVertexEdgeProperties ep = vertex->EdgeProperties(); + if (ON_SubDVertexTag::Crease != vertex->m_vertex_tag) + continue; // this vertex has already been processed. + const ON_SubDVertexEdgeProperties ep = vertex->EdgeProperties(); if ( 0 == ep.m_null_edge_count && 0 == ep.m_unset_edge_count ) { - if (1 == ep.m_crease_edge_count && ep.m_smooth_edge_count >= 1 && 2 == ep.m_min_edge_face_count && 2 == ep.m_max_edge_face_count) + if (ep.m_crease_edge_count >= 3) { - dart_index = j; - ++dart_count; + const_cast(edge->m_vertex[j])->m_vertex_tag = ON_SubDVertexTag::Corner; + } + else if (1 == ep.m_crease_edge_count && ep.m_smooth_edge_count >= 1 && 2 == ep.m_min_edge_face_count && 2 == ep.m_max_edge_face_count) + { + const_cast(edge->m_vertex[j])->m_vertex_tag = ON_SubDVertexTag::Dart; } } } - - if (dart_count == 1) - { - const_cast(edge->m_vertex[dart_index])->m_vertex_tag = ON_SubD::VertexTag::Dart; - k++; - if (k == interior_crease_count) - break; - } - else if (dart_count == 2) - { - const_cast(edge->m_vertex[0])->m_vertex_tag = ON_SubD::VertexTag::Dart; - const_cast(edge->m_vertex[1])->m_vertex_tag = ON_SubD::VertexTag::Dart; - k++; - if (k == interior_crease_count) - break; - } } } @@ -899,7 +1018,7 @@ ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( { for (const ON_SubDEdge* edge = new_subd->FirstEdge(); nullptr != edge; edge = edge->m_next_edge) { - if (ON_SubD::EdgeTag::Smooth != edge->m_edge_tag) + if (ON_SubDEdgeTag::Smooth != edge->m_edge_tag) continue; const unsigned int tagged_end_index = edge->TaggedEndIndex(); if (tagged_end_index < 2) @@ -913,34 +1032,34 @@ ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( if (2 == edge->m_face_count) { // first subdivision will convert edge to smooth - const_cast(edge)->m_edge_tag = ON_SubD::EdgeTag::SmoothX; + const_cast(edge)->m_edge_tag = ON_SubDEdgeTag::SmoothX; // sector weights will be calculated when facet type is set const_cast(edge)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; const_cast(edge)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; } else { - const_cast(edge)->m_edge_tag = ON_SubD::EdgeTag::Crease; + const_cast(edge)->m_edge_tag = ON_SubDEdgeTag::Crease; } } } for (const ON_SubDVertex* vertex = new_subd->FirstVertex(); nullptr != vertex; vertex = vertex->m_next_vertex) { - if (ON_SubD::VertexTag::Crease != vertex->m_vertex_tag) + if (ON_SubDVertexTag::Crease != vertex->m_vertex_tag) continue; unsigned int vertex_creased_edge_count = 0; const unsigned int vertex_edge_count = vertex->m_edge_count; for (unsigned int j = 0; j < vertex_edge_count; j++) { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[j].m_ptr); - if (ON_SubD::EdgeTag::Crease == edge->m_edge_tag) + if (ON_SubDEdgeTag::Crease == edge->m_edge_tag) { if (vertex_creased_edge_count >= 2) { // Three or more creased edges end at this vertex. // It must be subdivided as a corner vertex. - const_cast(vertex)->m_vertex_tag = ON_SubD::VertexTag::Corner; + const_cast(vertex)->m_vertex_tag = ON_SubDVertexTag::Corner; break; } vertex_creased_edge_count++; @@ -966,88 +1085,115 @@ ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons( if ( false == new_subd->IsOriented() ) new_subd->Orient(); - if (ON_SubDFromMeshParameters::ConvexCornerOption::AtMeshCorner == from_mesh_options->ConvexCornerTest()) + const double max_convex_angle_radians = from_mesh_options->MaximumConvexCornerAngleRadians(); + const bool bLookForConvexCorners + = ON_SubDFromMeshParameters::ConvexCornerOption::AtMeshCorner == from_mesh_options->GetConvexCornerOption() + && max_convex_angle_radians > 0.0 + && max_convex_angle_radians < ON_PI + ; + const double min_concave_angle_radians = from_mesh_options->MinimumConcaveCornerAngleRadians(); + const bool bLookForConcaveCorners + = ON_SubDFromMeshParameters::ConcaveCornerOption::AtMeshCorner == from_mesh_options->GetConcaveCornerOption() + && min_concave_angle_radians > ON_PI + && min_concave_angle_radians < ON_2PI + ; + if (bLookForConvexCorners || bLookForConcaveCorners) { // Add corners ON_SubDVertexIterator vit(*new_subd); - ON_SubDEdge* e[2]; - const ON_SubDFace* f; - const ON_SubDVertex* v[4]; - ON_3dPoint P[4]; - ON_3dVector T[4]; - ON_3dVector N[4]; - double NoN[4]; - const double a = from_mesh_options->MaximumConvexCornerAngleRadians(); - if (a > 0.0 && a < ON_PI) + for (ON_SubDVertex* vertex = const_cast(vit.FirstVertex()); nullptr != vertex; vertex = const_cast(vit.NextVertex())) { - const double NoNtol = 0.2588190451; // sin(15 degrees) - const double min_cos_corner_angle = cos(a); - for (ON_SubDVertex* vertex = const_cast(vit.FirstVertex()); nullptr != vertex; vertex = const_cast(vit.NextVertex())) + if (ON_SubDVertexTag::Crease != vertex->m_vertex_tag) + continue; + if ( 1 + vertex->m_face_count != vertex->m_edge_count ) + continue; + + ON_SubDComponentPtrPair boundary_pair = boundary_pair = vertex->BoundaryEdgePair(); + if (false == boundary_pair.BothAreNotNull()) + continue; + + bool bConvexCorner + = bLookForConvexCorners + && vertex->m_edge_count <= from_mesh_options->MaximumConvexCornerEdgeCount() + ; + bool bConcaveCorner + = bLookForConcaveCorners + && vertex->m_edge_count >= from_mesh_options->MinimumConcaveCornerEdgeCount() + ; + + if (false == bConvexCorner && false == bConcaveCorner) + continue; + + // add up angles of faces at this vertex + // If the faces are not coplanar, this sum can exceed 2pi. + double vertex_angle_radians = 0.0; + for (unsigned short vei = 0; vei < vertex->m_face_count; ++vei) { - if (ON_SubD::VertexTag::Crease != vertex->m_vertex_tag) - continue; - if (2 != vertex->m_edge_count) - continue; - e[0] = ON_SUBD_EDGE_POINTER(vertex->m_edges[0].m_ptr); - e[1] = ON_SUBD_EDGE_POINTER(vertex->m_edges[1].m_ptr); - if (nullptr == e[0] || 1 != e[0]->m_face_count || ON_SubD::EdgeTag::Crease != e[0]->m_edge_tag) - continue; - if (nullptr == e[1] || 1 != e[1]->m_face_count || ON_SubD::EdgeTag::Crease != e[1]->m_edge_tag) - continue; - f = ON_SUBD_FACE_POINTER(e[0]->m_face2[0].m_ptr); - if (nullptr == f) - continue; - if (f != ON_SUBD_FACE_POINTER(e[1]->m_face2[0].m_ptr)) - continue; - - const unsigned int vi = f->VertexIndex(vertex); - if (vi >= 4) - continue; - ON_SubDEdgePtr eptr[2]; - if (e[0] == f->Edge(vi)) + const double a = Internal_FaceCornerAngleRadians(vertex, vertex->m_faces[vei]); + if (false == (a > 0.0 && a < ON_PI) ) { - eptr[0] = ON_SubDEdgePtr::Create(e[0], vertex == e[0]->m_vertex[1] ? 1 : 0); - eptr[1] = ON_SubDEdgePtr::Create(e[1], vertex == e[1]->m_vertex[1] ? 1 : 0); + vertex_angle_radians = ON_DBL_QNAN; + break; } - else if (e[1] == f->Edge(vi)) - { - eptr[1] = ON_SubDEdgePtr::Create(e[0], vertex == e[0]->m_vertex[1] ? 1 : 0); - eptr[0] = ON_SubDEdgePtr::Create(e[1], vertex == e[1]->m_vertex[1] ? 1 : 0); - } - const double corner_angle_radians = ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(eptr[0], eptr[1]); - if (!(corner_angle_radians > 0.0 && corner_angle_radians < ON_PI)) - continue; - - // ocnvex quad restriction - for now - if (4 != f->m_edge_count) - continue; - v[0] = vertex; - v[1] = f->Vertex((vi + 1) % 4); - v[2] = f->Vertex((vi + 2) % 4); - v[3] = f->Vertex((vi + 3) % 4); - if (nullptr == v[0] || nullptr == v[1] || nullptr == v[2] || nullptr == v[3]) - continue; - for (int i = 0; i < 4; i++) - P[i] = ON_3dPoint(v[i]->m_P); - for (int i = 0; i < 4; i++) - T[i] = P[(i + 1) % 4] - P[i]; - for (int i = 0; i < 4; i++) - N[i] = -ON_CrossProduct(T[i], T[(i + 3) % 4]).UnitVector(); - for (int i = 0; i < 4; i++) - NoN[i] = N[i] * N[(i + 1) % 4]; - if (false == (NoN[0] >= NoNtol && NoN[1] >= NoNtol && NoN[2] >= NoNtol && NoN[3] >= NoNtol)) - continue; - const double cos_corner_angle = ON_CrossProduct(T[0], T[3]).Length(); - if (false == (cos_corner_angle >= min_cos_corner_angle)) - continue; - vertex->m_vertex_tag = ON_SubD::VertexTag::Corner; + vertex_angle_radians += a; } + if (false == (vertex_angle_radians > 0.0)) + continue; + + bConvexCorner = bConvexCorner && vertex_angle_radians <= max_convex_angle_radians; + bConcaveCorner = bConcaveCorner && vertex_angle_radians >= min_concave_angle_radians; + if (bConvexCorner ? 0 : 1 == bConcaveCorner ? 0 : 1) + continue; + + // Finally, test the angle between the boundary edges. That angle must also pass the min/max tests. + // This test cannot be done earlier, because we need to know if we are making a concave or convex corner + // to do this test correctly. + const double boundary_angle_radians = ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(boundary_pair.First().EdgePtr(), boundary_pair.Second().EdgePtr()); + if (false == boundary_angle_radians > 0.0 && boundary_angle_radians < ON_PI) + continue; // the angle is always acute because it does not look at the active side + if (bConvexCorner) + { + if (false == (boundary_angle_radians <= max_convex_angle_radians)) + continue; + } + else if (bConcaveCorner) + { + if (false == ((ON_2PI - boundary_angle_radians) >= min_concave_angle_radians)) + continue; + } + else + continue; // should never get here + + + vertex->m_vertex_tag = ON_SubDVertexTag::Corner; } } new_subd->UpdateEdgeSectorCoefficients(false); + + if (bCopyMeshMappingTag) + { + new_subd->SetTextureMappingTag(mesh->m_Ttag); + new_subd->SetTextureCoordinateType(ON_SubDTextureCoordinateType::FromMapping); + } + else if (bCopyMeshTextureCoordinates) + { + new_subd->SetTextureMappingTag(ON_MappingTag::Unset); + new_subd->SetTextureCoordinateType(ON_SubDTextureCoordinateType::FromFaceTexturePoints); + } + else if (bPackedTextureCoordinates) + { + new_subd->SetTextureMappingTag(ON_MappingTag::Unset); + new_subd->SetTextureCoordinateType(ON_SubDTextureCoordinateType::Packed); + } + else + { + new_subd->SetTextureMappingTag(ON_MappingTag::Unset); + new_subd->SetTextureCoordinateType(ON_SubDTextureCoordinateType::Unset); + } + return new_subd; } @@ -1069,14 +1215,14 @@ static ON_SubDVertex* IndexVertex( ON_SubD* ON_SubD::CreateSubDBox( const ON_3dPoint corners[8], - ON_SubD::EdgeTag edge_tag, + ON_SubDEdgeTag edge_tag, unsigned int facecount_x, unsigned int facecount_y, unsigned int facecount_z, ON_SubD* subd) { - if (ON_SubD::EdgeTag::Crease != edge_tag) - edge_tag = ON_SubD::EdgeTag::Smooth; + if (ON_SubDEdgeTag::Crease != edge_tag) + edge_tag = ON_SubDEdgeTag::Smooth; if (nullptr == subd) subd = new ON_SubD; @@ -1129,13 +1275,13 @@ ON_SubD* ON_SubD::CreateSubDBox( ccnt++; if (ccnt > 0) // On some face { - ON_SubD::VertexTag vtag = ON_SubD::VertexTag::Smooth; - if (edge_tag == ON_SubD::EdgeTag::Crease) + ON_SubDVertexTag vtag = ON_SubDVertexTag::Smooth; + if (edge_tag == ON_SubDEdgeTag::Crease) { if(ccnt == 2) // On some edge - vtag = ON_SubD::VertexTag::Crease; + vtag = ON_SubDVertexTag::Crease; else if(ccnt == 3) // On some corner - vtag = ON_SubD::VertexTag::Corner; + vtag = ON_SubDVertexTag::Corner; } ON_3dPoint P(corners[0] + (xdir * (dx * ix)) + (ydir * (dy * iy)) + (zdir * (dz * iz))); vert_index[ix][iy][iz] = vertex.Count(); @@ -1213,7 +1359,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); for (unsigned int ix = 0; ix < facecount_x; ix++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, 0), IndexVertex(vertex, vert_index, ix + 1, iy, 0)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, 0), IndexVertex(vertex, vert_index, ix + 1, iy, 0)); row.Append(ON_SubDEdgePtr::Create(e, 0)); } } @@ -1230,7 +1376,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); for (unsigned int iy = 0; iy < facecount_y; iy++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, 0), IndexVertex(vertex, vert_index, ix, iy + 1, 0)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, 0), IndexVertex(vertex, vert_index, ix, iy + 1, 0)); col.Append(ON_SubDEdgePtr::Create(e, 0)); } } @@ -1268,7 +1414,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); for (unsigned int ix = 0; ix < facecount_x; ix++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, facecount_z), IndexVertex(vertex, vert_index, ix + 1, iy, facecount_z)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, facecount_z), IndexVertex(vertex, vert_index, ix + 1, iy, facecount_z)); row.Append(ON_SubDEdgePtr::Create(e, 0)); } } @@ -1285,7 +1431,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); for (unsigned int iy = 0; iy < facecount_y; iy++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, facecount_z), IndexVertex(vertex, vert_index, ix, iy + 1, facecount_z)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, facecount_z), IndexVertex(vertex, vert_index, ix, iy + 1, facecount_z)); col.Append(ON_SubDEdgePtr::Create(e, 0)); } } @@ -1323,7 +1469,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); for (unsigned int ix = 0; ix < facecount_x; ix++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, 0, iz), IndexVertex(vertex, vert_index, ix + 1, 0, iz)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, 0, iz), IndexVertex(vertex, vert_index, ix + 1, 0, iz)); row.Append(ON_SubDEdgePtr::Create(e, 0)); } } @@ -1340,7 +1486,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); for (unsigned int iz = 0; iz < facecount_z; iz++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, 0, iz), IndexVertex(vertex, vert_index, ix, 0, iz + 1)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, 0, iz), IndexVertex(vertex, vert_index, ix, 0, iz + 1)); col.Append(ON_SubDEdgePtr::Create(e, 0)); } } @@ -1378,7 +1524,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); for (unsigned int ix = 0; ix < facecount_x; ix++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, facecount_y, iz), IndexVertex(vertex, vert_index, ix + 1, facecount_y, iz)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, facecount_y, iz), IndexVertex(vertex, vert_index, ix + 1, facecount_y, iz)); row.Append(ON_SubDEdgePtr::Create(e, 0)); } } @@ -1395,7 +1541,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); for (unsigned int iz = 0; iz < facecount_z; iz++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, facecount_y, iz), IndexVertex(vertex, vert_index, ix, facecount_y, iz + 1)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, facecount_y, iz), IndexVertex(vertex, vert_index, ix, facecount_y, iz + 1)); col.Append(ON_SubDEdgePtr::Create(e, 0)); } } @@ -1433,7 +1579,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); for (unsigned int iy = 0; iy < facecount_y; iy++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, 0, iy, iz), IndexVertex(vertex, vert_index, 0, iy + 1, iz)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, 0, iy, iz), IndexVertex(vertex, vert_index, 0, iy + 1, iz)); row.Append(ON_SubDEdgePtr::Create(e, 0)); // mac compile warning // ON_3dPoint p0 = row.Last()->RelativeVertex(0)->ControlNetPoint(); @@ -1454,7 +1600,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); for (unsigned int iz = 0; iz < facecount_z; iz++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, 0, iy, iz), IndexVertex(vertex, vert_index, 0, iy, iz + 1)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, 0, iy, iz), IndexVertex(vertex, vert_index, 0, iy, iz + 1)); col.Append(ON_SubDEdgePtr::Create(e, 0)); // mac compile warning // ON_3dPoint p0 = col.Last()->RelativeVertex(0)->ControlNetPoint(); @@ -1509,7 +1655,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); for (unsigned int iy = 0; iy < facecount_y; iy++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, facecount_x, iy, iz), IndexVertex(vertex, vert_index, facecount_x, iy + 1, iz)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, facecount_x, iy, iz), IndexVertex(vertex, vert_index, facecount_x, iy + 1, iz)); row.Append(ON_SubDEdgePtr::Create(e, 0)); } } @@ -1526,7 +1672,7 @@ ON_SubD* ON_SubD::CreateSubDBox( ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); for (unsigned int iz = 0; iz < facecount_z; iz++) { - e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, facecount_x, iy, iz), IndexVertex(vertex, vert_index, facecount_x, iy, iz + 1)); + e = subd->AddEdge(ON_SubDEdgeTag::Smooth, IndexVertex(vertex, vert_index, facecount_x, iy, iz), IndexVertex(vertex, vert_index, facecount_x, iy, iz + 1)); col.Append(ON_SubDEdgePtr::Create(e, 0)); } } diff --git a/opennurbs_subd_heap.cpp b/opennurbs_subd_heap.cpp index 30a153ce..2b083686 100644 --- a/opennurbs_subd_heap.cpp +++ b/opennurbs_subd_heap.cpp @@ -533,10 +533,10 @@ ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( v1->SetSubdivisionLevel( edge0->SubdivisionLevel() + 1 ); - if (ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag || ON_SubD::EdgeTag::SmoothX == edge0->m_edge_tag) - v1->m_vertex_tag = ON_SubD::VertexTag::Smooth; - else if (ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) - v1->m_vertex_tag = ON_SubD::VertexTag::Crease; + if (ON_SubDEdgeTag::Smooth == edge0->m_edge_tag || ON_SubDEdgeTag::SmoothX == edge0->m_edge_tag) + v1->m_vertex_tag = ON_SubDVertexTag::Smooth; + else if (ON_SubDEdgeTag::Crease == edge0->m_edge_tag) + v1->m_vertex_tag = ON_SubDVertexTag::Crease; return v1; } @@ -570,7 +570,7 @@ ON_SubDVertex * ON_SubD_FixedSizeHeap::FindOrAllocateVertex(const ON_SubDFace * return ON_SUBD_RETURN_ERROR(nullptr); v1->SetSubdivisionLevel( face0->SubdivisionLevel() + 1 ); - v1->m_vertex_tag = ON_SubD::VertexTag::Smooth; + v1->m_vertex_tag = ON_SubDVertexTag::Smooth; Internal_HashAddPair(hash, component0, v1); @@ -591,7 +591,7 @@ ON_SubDVertex * ON_SubD_FixedSizeHeap::AllocateSectorFaceVertex(const ON_SubDFac return ON_SUBD_RETURN_ERROR(nullptr); v1->SetSubdivisionLevel( sector_face0->SubdivisionLevel() + 1 ); - v1->m_vertex_tag = ON_SubD::VertexTag::Smooth; + v1->m_vertex_tag = ON_SubDVertexTag::Smooth; return v1; } @@ -647,14 +647,14 @@ const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge( { if (nullptr == v0->m_edges || v0->m_edge_count >= v0->m_edge_capacity) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); - if (ON_SubD::VertexTag::Smooth == v0->m_vertex_tag) + if (ON_SubDVertexTag::Smooth == v0->m_vertex_tag) { bTaggedVertex[0] = false; v0_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient; } else { - bTaggedVertex[0] = (ON_SubD::VertexTag::Unset != v0->m_vertex_tag); + bTaggedVertex[0] = (ON_SubDVertexTag::Unset != v0->m_vertex_tag); } } else @@ -664,14 +664,14 @@ const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge( { if (nullptr == v1->m_edges || v1->m_edge_count >= v1->m_edge_capacity) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); - if (ON_SubD::VertexTag::Smooth == v1->m_vertex_tag) + if (ON_SubDVertexTag::Smooth == v1->m_vertex_tag) { bTaggedVertex[1] = false; v1_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient; } else { - bTaggedVertex[1] = (ON_SubD::VertexTag::Unset != v0->m_vertex_tag); + bTaggedVertex[1] = (ON_SubDVertexTag::Unset != v0->m_vertex_tag); if (bTaggedVertex[0] && bTaggedVertex[1]) { // crease edge - no weights @@ -719,7 +719,7 @@ const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge( e->m_sector_coefficient[0] = v0_sector_weight; e->m_sector_coefficient[1] = v1_sector_weight; - e->m_edge_tag = (bTaggedVertex[0] && bTaggedVertex[1]) ? ON_SubD::EdgeTag::Crease : ON_SubD::EdgeTag::Smooth; + e->m_edge_tag = (bTaggedVertex[0] && bTaggedVertex[1]) ? ON_SubDEdgeTag::Crease : ON_SubDEdgeTag::Smooth; return ON_SubDEdgePtr::Create(e,0); } @@ -1088,6 +1088,12 @@ void ON_SubDHeap::ReturnFace(class ON_SubDFace* f) { if (nullptr != f) { + if (f->m_texture_points) + { + this->Return3dPointArray(f->m_texture_points); + f->m_texture_points = nullptr; + f->m_texture_status_bits = 0; + } ReturnArray(f->m_edgex_capacity,(ON__UINT_PTR*)f->m_edgex); (&f->m_id)[1] = ON_UNSET_UINT_INDEX; // m_archive_id == ON_UNSET_UINT_INDEX marks the fixed size pool element as unused f->m_status = ON_ComponentStatus::Deleted; @@ -1449,6 +1455,10 @@ bool ON_SubDHeap::GrowFaceEdgeArray( { if ( nullptr == f) return ON_SUBD_RETURN_ERROR(false); + + // The capacity of f->m_texture_points[] must always be 4 + f->m_edgex_capacity + const size_t texture_point_capacity0 = f->TexturePointsCapacity(); + if ( 0 == capacity ) capacity = f->m_edge_count+1; if ( capacity <= (size_t)(4 + f->m_edgex_capacity)) @@ -1460,13 +1470,87 @@ bool ON_SubDHeap::GrowFaceEdgeArray( f->m_edge_count = 0; f->m_edgex_capacity = 0; f->m_edgex = nullptr; + // also remove texture points + f->m_texture_status_bits &= ON_SubDFace::TextureStatusBits::NotTexturePointsBitsMask; + f->m_texture_points = nullptr; return ON_SUBD_RETURN_ERROR(false); } f->m_edgex = (ON_SubDEdgePtr*)a; f->m_edgex_capacity = (unsigned short)xcapacity; + if (texture_point_capacity0 > 0) + { + const size_t texture_point_capacity1 = 4 + xcapacity; + if (texture_point_capacity0 < texture_point_capacity1) + { + ON_3dPoint* texture_points0 = f->m_texture_points; + ON_3dPoint* texture_points1 = this->Allocate3dPointArray(texture_point_capacity1); + for (size_t i = 0; i < texture_point_capacity0; ++i) + texture_points1[i] = texture_points0[i]; + for (size_t i = texture_point_capacity0; i < texture_point_capacity1; ++i) + texture_points1[i] = ON_3dPoint::NanPoint; + f->m_texture_points = texture_points1; + this->Return3dPointArray(texture_points0); + } + } return true; } +unsigned int ON_SubDHeap::Managed3dPointArrayCapacity(class ON_3dPoint* point_array) +{ + const unsigned int point_capacity = + (nullptr != point_array) + ? *((unsigned int*)(((const double*)point_array)-1)) + : 0; + return (point_capacity >= 3 && point_capacity <= ON_SubDFace::MaximumEdgeCount) ? point_capacity : 0; +} + +ON_3dPoint* ON_SubDHeap::Allocate3dPointArray(size_t point_capacity) +{ + if (point_capacity <= 0 || point_capacity > ON_SubDFace::MaximumEdgeCount) + return nullptr; +#if defined(ON_64BIT_RUNTIME) + if (point_capacity < 5) + point_capacity = 5; // maximize use of m_fsp17 chunks. +#endif + const size_t a_capacity = 3 * point_capacity + 1; + double* a = +#if defined(ON_64BIT_RUNTIME) + // when sizeof(void*) = sizeof(double) we can use the fast fixed size pool for faces with 5 or fewer edges. + ( a_capacity*sizeof(a[0]) <= m_fsp17.SizeofElement() ) + ? (double*)this->m_fsp17.AllocateDirtyElement() + : +#endif + a = (double*)onmalloc(a_capacity * sizeof(a[0])); + + if (nullptr != a) + { + *((unsigned int*)a) = ((unsigned int)point_capacity); + ++a; + } + return ((ON_3dPoint*)a); +} + +void ON_SubDHeap::Return3dPointArray(class ON_3dPoint* point_array) +{ + const size_t point_capacity = ON_SubDHeap::Managed3dPointArrayCapacity(point_array); + if (0 == point_capacity) + { + ON_SUBD_ERROR("point_array is not valid"); + return; + } + double* a = ((double*)point_array) - 1; +#if defined(ON_64BIT_RUNTIME) + // when sizeof(void*) = sizeof(double) we can use the fast fixed size pool for faces with 5 or fewer edges. + const size_t a_capacity = 3 * point_capacity + 1; + if ( a_capacity * sizeof(a[0]) <= m_fsp17.SizeofElement() ) + this->m_fsp17.ReturnElement(a); + else +#endif + onfree(a); + + return; +} + bool ON_SubDHeap::GrowVertexEdgeArrayByOne( ON_SubDVertex* v ) @@ -1712,8 +1796,9 @@ bool ON_SubDHeap::Internal_InitializeLimitBlockPool() { if (0 == m_limit_block_pool.SizeofElement()) { - m_sizeof_full_fragment = ON_SubDMeshFragment::SizeofFragment(ON_SubDDisplayParameters::DefaultDensity); - m_sizeof_half_fragment = ON_SubDMeshFragment::SizeofFragment(ON_SubDDisplayParameters::DefaultDensity-1); + const bool bCurvatureArray = false; // change to true when principal and sectional curvatures are needed. + m_sizeof_full_fragment = ON_SubDMeshFragment::SizeofFragment(ON_SubDDisplayParameters::DefaultDensity, bCurvatureArray); + m_sizeof_half_fragment = ON_SubDMeshFragment::SizeofFragment(ON_SubDDisplayParameters::DefaultDensity-1, bCurvatureArray); m_sizeof_limit_curve = sizeof(ON_SubDEdgeSurfaceCurve); size_t sz = m_sizeof_full_fragment; if (sz < 4 * m_sizeof_half_fragment) @@ -1814,15 +1899,10 @@ ON_SubDMeshFragment* ON_SubDHeap::AllocateMeshFragment( *fragment = src_fragment; fragment->m_prev_fragment = nullptr; fragment->m_next_fragment = nullptr; - double* a = (double*)(fragment + 1); - fragment->SetUnmanagedVertexCapacity(vertex_capacity); - fragment->SetVertexCount(0); - fragment->m_P = a; - fragment->m_P_stride = 3; - fragment->m_N = a + (vertex_capacity * 3); - fragment->m_N_stride = 3; - fragment->m_T = a + (vertex_capacity * 6); - fragment->m_T_stride = 3; + + // NOTE WELL: + // fragment and fragment array memory are from a single fixed size pool allocation. + fragment->Internal_LayoutArrays(false, (double*)(fragment + 1), vertex_capacity); if (src_fragment.VertexCount() > 0) fragment->CopyFrom(src_fragment,density); @@ -1830,6 +1910,30 @@ ON_SubDMeshFragment* ON_SubDHeap::AllocateMeshFragment( return fragment; } + +ON_SubDMeshFragment* ON_SubDHeap::CopyMeshFragments(const ON_SubDFace* source_face, const ON_SubDFace* destination_face) +{ + if (nullptr == source_face || nullptr == destination_face || nullptr != destination_face->m_mesh_fragments) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDMeshFragment* prev_dst_fragment = nullptr; + for (const ON_SubDMeshFragment* src_fragment = source_face->MeshFragments(); nullptr != src_fragment; src_fragment = src_fragment->m_next_fragment) + { + ON_SubDMeshFragment* dst_fragment = this->AllocateMeshFragment(*src_fragment); + dst_fragment->m_face = destination_face; + if (prev_dst_fragment) + prev_dst_fragment->m_next_fragment = dst_fragment; + else + { + destination_face->m_mesh_fragments = dst_fragment; + destination_face->Internal_SetSavedSurfacePointFlag(true); + } + prev_dst_fragment = dst_fragment; + } + return destination_face->m_mesh_fragments; +} + + bool ON_SubDHeap::ReturnMeshFragment(ON_SubDMeshFragment * fragment) { if (nullptr == fragment) @@ -1932,6 +2036,69 @@ class ON_SubDEdgeSurfaceCurve* ON_SubDHeap::AllocateEdgeSurfaceCurve( return limit_curve; } +ON_SubDEdgeSurfaceCurve* ON_SubDHeap::CopyEdgeSurfaceCurve(const ON_SubDEdge* source_edge, const ON_SubDEdge* desination_edge) +{ + if ( nullptr == desination_edge || source_edge == desination_edge) + return ON_SUBD_RETURN_ERROR(nullptr); + + desination_edge->Internal_ClearSurfacePointFlag(); + if (source_edge->m_limit_curve == desination_edge->m_limit_curve) + desination_edge->m_limit_curve = nullptr; + else if (nullptr != desination_edge->m_limit_curve) + desination_edge->m_limit_curve->m_cv_count = 0; + + if (nullptr == source_edge) + { + ReturnEdgeSurfaceCurve(desination_edge); + return ON_SUBD_RETURN_ERROR(nullptr); + } + + const ON_SubDEdgeSurfaceCurve* source_curve = source_edge->Internal_SurfacePointFlag() ? source_edge->m_limit_curve : nullptr; + const unsigned char cv_count = (nullptr != source_curve) ? source_curve->m_cv_count : 0; + if (0 == cv_count) + { + source_edge->Internal_ClearSurfacePointFlag(); + ReturnEdgeSurfaceCurve(desination_edge); + return nullptr; + } + + if (cv_count < 2 || cv_count > ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity || (cv_count > ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity && nullptr == source_curve->m_cvx)) + { + source_edge->Internal_ClearSurfacePointFlag(); + ReturnEdgeSurfaceCurve(desination_edge); + return ON_SUBD_RETURN_ERROR(nullptr); + } + + ON_SubDEdgeSurfaceCurve* desination_curve = desination_edge->m_limit_curve; + if (nullptr != desination_curve && desination_curve->m_cv_capacity < cv_count) + { + ReturnEdgeSurfaceCurve(desination_edge); + desination_curve = nullptr; + } + if (nullptr == desination_curve) + { + desination_curve = this->AllocateEdgeSurfaceCurve(cv_count); + if (nullptr == desination_curve) + return ON_SUBD_RETURN_ERROR(nullptr); + if (desination_curve->m_cv_capacity < cv_count) + { + ReturnEdgeSurfaceCurve(desination_curve); + return ON_SUBD_RETURN_ERROR(nullptr); + } + } + const size_t sz5 = sizeof(desination_curve->m_cv5); + memcpy(desination_curve->m_cv5, source_curve->m_cv5, sz5); + if (cv_count > ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity && nullptr != desination_curve->m_cvx && nullptr != source_curve->m_cvx) + { + const size_t szx = (cv_count - ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity) * 24; + memcpy(desination_curve->m_cvx, source_curve->m_cvx, szx); + } + desination_curve->m_cv_count = cv_count; + desination_edge->m_limit_curve = desination_curve; + desination_edge->Internal_SetSavedSurfacePointFlag(true); + return desination_curve; +} + bool ON_SubDHeap::ReturnEdgeSurfaceCurve( class ON_SubDEdgeSurfaceCurve* limit_curve ) diff --git a/opennurbs_subd_iter.cpp b/opennurbs_subd_iter.cpp index 44437cdc..888d0da6 100644 --- a/opennurbs_subd_iter.cpp +++ b/opennurbs_subd_iter.cpp @@ -1072,6 +1072,254 @@ unsigned int ON_SubDFaceEdgeIterator::CurrentEdgeIndex() const return m_edge_index; } +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDSectorId +// +const ON_SubDSectorId ON_SubDSectorId::Create( + const ON_SubDVertex* vertex, + const ON_SubDFace* face +) +{ + ON_SubDSectorId sid; + + if (nullptr == face && nullptr != vertex && vertex->IsSingleSectorVertex()) + face = vertex->Face(0); + + if (nullptr == vertex || nullptr == face) + { + // Creation of ON_SubDSectorId::Invalid in opennurbs_statics.cpp or + // just another invalid input. + sid.m_vertex_id = 0; + sid.m_minimum_face_id = 0; + sid.m_sector_face_count = 0xFFFFFFFFU; + return sid; + } + + for (;;) + { + if (vertex->m_id <= 0 || vertex->m_id >= ON_UNSET_UINT_INDEX) + break; + if (face->m_id <= 0 || face->m_id >= ON_UNSET_UINT_INDEX) + break; + if (face->m_edge_count < 3) + break; + if (vertex->m_edge_count < 2) + break; + if (vertex->m_face_count < 1) + break; + const unsigned face_vi = face->VertexIndex(vertex); + if (face_vi >= face->m_edge_count) + break; + + sid.m_vertex_id = vertex->m_id; + sid.m_minimum_face_id = face->m_id; + const unsigned short n = vertex->m_face_count; + unsigned short crease_face_count = 0; + unsigned int expected_crease_count = 0; + if (vertex->IsSmoothOrDart()) + { + if (n < 2 || n != vertex->m_edge_count) + break; + const bool bIsDart = vertex->IsDart(); + crease_face_count = bIsDart ? 2 : 0; + expected_crease_count = bIsDart ? 1 : 0; + } + else if ( vertex->IsCreaseOrCorner()) + { + const ON_SubDComponentPtrPair pair + = (n + 1 == vertex->m_edge_count && 2==vertex->CreasedEdgeCount(true,true,true,false)) + ? vertex->BoundaryEdgePair() + : ON_SubDComponentPtrPair::Null; + if (pair.BothAreNotNull()) + { + // we can avoid using a sector iterator + const ON_SubDEdge* e[2] = { pair.First().Edge(),pair.Second().Edge() }; + if (nullptr == e[0] || nullptr == e[1]) + break; + if (1 != e[0]->m_face_count || 1 != e[1]->m_face_count) + break; + crease_face_count = 1; + expected_crease_count = 2; + } + else + { + if (vertex->IsCrease()) + { + if (n != vertex->m_edge_count) + return ON_SubDSectorId::Invalid; + } + + // complicated case needs to use more time consuming sector iterator + ON_SubDSectorIterator sit0; + if ( vertex != sit0.Initialize(face, 0, face_vi)) + return ON_SubDSectorId::Invalid; + ON_SubDSectorIterator sit1(sit0); + // advance sit0 until we hit a crease; + sid.m_sector_face_count = 1; + for (const ON_SubDFace* f = sit0.NextFace(ON_SubDSectorIterator::StopAt::AnyCrease); nullptr != f && sid.m_sector_face_count <= vertex->m_face_count; f = sit0.NextFace(ON_SubDSectorIterator::StopAt::AnyCrease)) + { + ++sid.m_sector_face_count; + if (f->m_id < sid.m_minimum_face_id) + sid.m_minimum_face_id = f->m_id; + } + // advance sit1 until we hit a crease; + for (const ON_SubDFace* f = sit1.PrevFace(ON_SubDSectorIterator::StopAt::AnyCrease); nullptr != f && sid.m_sector_face_count <= vertex->m_face_count; f = sit1.PrevFace(ON_SubDSectorIterator::StopAt::AnyCrease)) + { + ++sid.m_sector_face_count; + if (f->m_id < sid.m_minimum_face_id) + sid.m_minimum_face_id = f->m_id; + } + break; + } + } + else + { + break; + } + + if (0 == sid.m_sector_face_count) + { + // we can avoid expensive sector iterator. + unsigned int crease_count = 0; + const ON_SubDFace* f; + for (unsigned short vei = 0; vei < vertex->m_edge_count; ++vei) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); + if (nullptr == e || 0 == e->m_face_count) + return ON_SubDSectorId::Invalid; + f = ON_SUBD_FACE_POINTER(e->m_face2[0].m_ptr); + if (nullptr == f) + return ON_SubDSectorId::Invalid; + if (f->m_id < sid.m_minimum_face_id) + sid.m_minimum_face_id = f->m_id; + if (ON_SubDEdgeTag::Crease == e->m_edge_tag) + { + ++crease_count; + if (crease_count > expected_crease_count) + return ON_SubDSectorId::Invalid; + if (crease_face_count != e->m_face_count) + return ON_SubDSectorId::Invalid; + if (1 == crease_face_count) + continue; + } + if (2 != e->m_face_count) + return ON_SubDSectorId::Invalid; + f = ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr); + if (nullptr == f) + return ON_SubDSectorId::Invalid; + if (f->m_id < sid.m_minimum_face_id) + sid.m_minimum_face_id = f->m_id; + } + if (crease_count == expected_crease_count) + sid.m_sector_face_count = n; + } + break; + } + + return (sid.IsSet() && sid.m_sector_face_count <= vertex->m_face_count) ? sid : ON_SubDSectorId::Invalid; +} + +const ON_SubDSectorId ON_SubDSectorId::CreateFromIds( + unsigned int vertex_id, + unsigned int minimum_face_id +) +{ + ON_SubDSectorId sid; + sid.m_vertex_id = vertex_id; + sid.m_minimum_face_id = minimum_face_id; + sid.m_sector_face_count = 0; + return sid; +} + +int ON_SubDSectorId::Compare(ON_SubDSectorId lhs, ON_SubDSectorId rhs) +{ + if (lhs.m_vertex_id < rhs.m_vertex_id) + return -1; + if (lhs.m_vertex_id > rhs.m_vertex_id) + return 1; + if (lhs.m_minimum_face_id < rhs.m_minimum_face_id) + return -1; + if (lhs.m_minimum_face_id > rhs.m_minimum_face_id) + return 1; + if (lhs.m_sector_face_count < rhs.m_sector_face_count) + return -1; + if (lhs.m_sector_face_count > rhs.m_sector_face_count) + return 1; + return 0; +} + +int ON_SubDSectorId::CompareFromPointers(const ON_SubDSectorId* lhs, const ON_SubDSectorId* rhs) +{ + if (nullptr == lhs || nullptr == rhs) + { + // sort nullptr to end + if (nullptr != lhs) + return -1; + if (nullptr != rhs) + return 1; + return 0; + } + return ON_SubDSectorId::Compare(*lhs, *rhs); +} + +const unsigned int ON_SubDSectorId::VertexId() const +{ + return m_vertex_id; +} + +const unsigned int ON_SubDSectorId::MinimumFaceId() const +{ + return m_minimum_face_id; +} + +const unsigned int ON_SubDSectorId::SectorFaceCount() const +{ + return m_sector_face_count <= 0x10000U ? m_sector_face_count : 0U; +} + +bool ON_SubDSectorId::IsZero() const +{ + return 0 == m_vertex_id && 0 == m_minimum_face_id && 0 == m_sector_face_count; +} + +bool ON_SubDSectorId::IsSet() const +{ + // the maximum valid subd vertex face count is 0xFFFF. + return m_vertex_id > 0 && m_minimum_face_id > 0 && m_sector_face_count > 0 && m_vertex_id < ON_UNSET_UINT_INDEX&& m_minimum_face_id < ON_UNSET_UINT_INDEX&& m_sector_face_count < 0x10000U; +} + + +bool operator==(ON_SubDSectorId lhs, ON_SubDSectorId rhs) +{ + return 0 == ON_SubDSectorId::Compare(lhs, rhs); +} + +bool operator!=(ON_SubDSectorId lhs, ON_SubDSectorId rhs) +{ + return 0 != ON_SubDSectorId::Compare(lhs, rhs); +} + +bool operator>(ON_SubDSectorId lhs, ON_SubDSectorId rhs) +{ + return ON_SubDSectorId::Compare(lhs, rhs) < 0; +} + +bool operator<(ON_SubDSectorId lhs, ON_SubDSectorId rhs) +{ + return ON_SubDSectorId::Compare(lhs, rhs) > 0; +} + +bool operator>=(ON_SubDSectorId lhs, ON_SubDSectorId rhs) +{ + return ON_SubDSectorId::Compare(lhs, rhs) >= 0; +} + +bool operator<=(ON_SubDSectorId lhs, ON_SubDSectorId rhs) +{ + return ON_SubDSectorId::Compare(lhs, rhs) <= 0; +} + ////////////////////////////////////////////////////////////////////////// // @@ -1423,7 +1671,7 @@ const ON_SubDFace* ON_SubDSectorIterator::IncrementFace( if (ON_SubDSectorIterator::StopAt::Boundary != stop_at) { - if (ON_SubD::EdgeTag::Crease == edge->m_edge_tag) + if (ON_SubDEdgeTag::Crease == edge->m_edge_tag) { if (ON_SubDSectorIterator::StopAt::AnyCrease == stop_at) break; @@ -1569,7 +1817,7 @@ const ON_SubDFace* ON_SubDSectorIterator::IncrementToCrease( if (nullptr == edge) return ON_SUBD_RETURN_ERROR(nullptr); - if (edge->m_face_count != 2 || ON_SubD::EdgeTag::Crease == edge->m_edge_tag) + if (edge->m_face_count != 2 || ON_SubDEdgeTag::Crease == edge->m_edge_tag) { *this = sit; return CurrentFace(); diff --git a/opennurbs_subd_limit.cpp b/opennurbs_subd_limit.cpp index b2907b15..513904c7 100644 --- a/opennurbs_subd_limit.cpp +++ b/opennurbs_subd_limit.cpp @@ -160,7 +160,7 @@ bool ON_SubDQuadNeighborhood::IsValid() const if (m_bBoundaryCrease[fi]) { - // A tag of ON_SubD::EdgeTag::SmoothX is an error here + // A tag of ON_SubDEdgeTag::SmoothX is an error here if ( false == quad_edge[fi]->IsCrease() ) return ON_SUBD_RETURN_ERROR(false); @@ -183,7 +183,7 @@ bool ON_SubDQuadNeighborhood::IsValid() const if (nullptr == side_face[fi]) return ON_SUBD_RETURN_ERROR(false); - // A tag of ON_SubD::EdgeTag::SmoothX is permitted here + // A tag of ON_SubDEdgeTag::SmoothX is permitted here if ( 2 != quad_edge[fi]->m_face_count) return ON_SUBD_RETURN_ERROR(false); @@ -191,14 +191,14 @@ bool ON_SubDQuadNeighborhood::IsValid() const { unsigned int dart_count = 0; unsigned int crease_count = 0; - if ( ON_SubD::VertexTag::Dart == quad_edge[fi]->m_vertex[0]->m_vertex_tag) + if ( ON_SubDVertexTag::Dart == quad_edge[fi]->m_vertex[0]->m_vertex_tag) dart_count++; else if ( quad_edge[fi]->m_vertex[0]->IsCreaseOrCorner()) crease_count++; else return ON_SUBD_RETURN_ERROR(false); - if ( ON_SubD::VertexTag::Dart == quad_edge[fi]->m_vertex[1]->m_vertex_tag) + if ( ON_SubDVertexTag::Dart == quad_edge[fi]->m_vertex[1]->m_vertex_tag) dart_count++; else if ( quad_edge[fi]->m_vertex[1]->IsCreaseOrCorner()) crease_count++; @@ -274,7 +274,7 @@ bool ON_SubDQuadNeighborhood::IsValid() const { if (e->IsSmooth()) continue; - if (e->IsCrease() && ON_SubD::VertexTag::Dart == quad_vertex[fi]->m_vertex_tag) + if (e->IsCrease() && ON_SubDVertexTag::Dart == quad_vertex[fi]->m_vertex_tag) continue; } if (false == e->IsCrease()) @@ -387,16 +387,6 @@ void ON_SubDQuadNeighborhood::Internal_Destroy(bool bReinitialize) m_bBoundaryCrease[2] = false; m_bBoundaryCrease[3] = false; - m_bCenterEdgeLimitPoint[0] = false; - m_bCenterEdgeLimitPoint[1] = false; - m_bCenterEdgeLimitPoint[2] = false; - m_bCenterEdgeLimitPoint[3] = false; - - m_center_edge_limit_point[0] = ON_SubDSectorSurfacePoint::Nan; - m_center_edge_limit_point[1] = ON_SubDSectorSurfacePoint::Nan; - m_center_edge_limit_point[2] = ON_SubDSectorSurfacePoint::Nan; - m_center_edge_limit_point[3] = ON_SubDSectorSurfacePoint::Nan; - for (unsigned int i = 0; i < 4; i++) { m_vertex_grid[i][0] = nullptr; @@ -618,6 +608,8 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( ) { m_bIsCubicPatch = false; + + // When the original SubD face is an n-gon, it is subdivided into quads and m_initial_subdivision_level = 1. const unsigned int delta_subdivision_level = (m_current_subdivision_level > m_initial_subdivision_level) ? ((unsigned int)(m_current_subdivision_level - m_initial_subdivision_level)) @@ -670,7 +662,7 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( { if ( nullptr != m_center_edges[i] - && ON_SubD::EdgeTag::SmoothX != m_center_edges[i]->m_edge_tag + && ON_SubDEdgeTag::SmoothX != m_center_edges[i]->m_edge_tag && (bCenterEdgeIsSmooth[i] != bCenterEdgeIsCrease[i]) ) { @@ -704,7 +696,7 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( m_vertex_grid[1][2] }; - const bool bQuadVertexIsSmoothOrCrease[4] = + const bool bQuadVertexIsSmoothOrCrease[4] = { quad_vertex[0]->IsSmoothOrCrease(), quad_vertex[1]->IsSmoothOrCrease(), @@ -717,8 +709,9 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( if (false == bExtraordinaryCornerVertex[corner_index]) continue; - if (false == bQuadVertexIsSmoothOrCrease[corner_index] && false == quad_vertex[corner_index]->IsCorner()) + if (quad_vertex[corner_index]->IsDart()) continue; + if (false == bQuadVertexIsSmoothOrCrease[(corner_index+1)%4]) continue; if (false == bQuadVertexIsSmoothOrCrease[(corner_index+3)%4]) @@ -799,9 +792,7 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( { m_bExtraordinaryCornerVertex[corner_index] = bExtraordinaryCornerVertex[corner_index]; if (bExtraordinaryCornerVertex[corner_index]) - { m_extraordinary_corner_vertex_count++; - } } m_exact_quadrant_patch_count = 0; @@ -1035,8 +1026,8 @@ bool ON_SubDQuadNeighborhood::Set( m_vertex_grid[0][2] = outer_vertex[10]; m_vertex_grid[0][1] = outer_vertex[11]; - m_initial_subdivision_level = 0; - m_current_subdivision_level = 0; + m_initial_subdivision_level = (unsigned char)(center_quad_face->m_level_zero_face_id > 0 ? center_quad_face->SubdivisionLevel() : 0U); + m_current_subdivision_level = m_initial_subdivision_level; SetPatchStatus(0); @@ -1189,7 +1180,7 @@ bool ON_SubDQuadNeighborhood::GetLimitSurfaceCV( const ON_SubDVertex* v[2] = { m_vertex_grid[i + di][j + dj], m_vertex_grid[i + 2 * di][j + 2 * dj] }; if (nullptr == v[0] || nullptr == v[1]) return ON_SUBD_RETURN_ERROR(false); - if (ON_SubD::VertexTag::Crease != v[0]->m_vertex_tag) + if (ON_SubDVertexTag::Crease != v[0]->m_vertex_tag) return ON_SUBD_RETURN_ERROR(false); //dstP = srf_cv[i][j]; dstP = srf_cv + 3*(i*srf_cv_grid_size+j); @@ -1279,25 +1270,53 @@ bool ON_SubDQuadNeighborhood::GetSubdivisionPoint( } unsigned int ON_SubDQuadNeighborhood::SetLimitSubSurfaceExactCVs( + bool bEnableApproximatePatch, unsigned int quadrant_index ) { - if (nullptr == m_face_grid[1][1] || quadrant_index > 4) + // When subdivision_count >= 2, there should be 3 or 4 or more exact quadrants. + // When the original SubD face is an n-gon, it is subdivided into quads and m_initial_subdivision_level = 1. + const unsigned char subdivision_count + = (m_current_subdivision_level > m_initial_subdivision_level) + ? m_current_subdivision_level + : 0; if (bEnableApproximatePatch && (subdivision_count < 2 || m_extraordinary_corner_vertex_count > 1)) + { + ON_SUBD_ERROR("bEnableApproximatePatch should be false at this stage."); + bEnableApproximatePatch = false; + } + + if (nullptr == m_face_grid[1][1] || 4 != m_face_grid[1][1]->m_edge_count || quadrant_index > 4) return ON_SUBD_RETURN_ERROR(0); + // faces_vertices[] face, in counterclockwise order. + // m_center_edges[] are the 4 edges of face in counterclockwise order with + // m_center_edges[0] connecting face_vertices[0] and face_vertices[1]. + // However, it may be that face->Vertex(0) != face_vertices[0]. + const ON_SubDVertex* center_vertices[4] = { m_vertex_grid[1][1], m_vertex_grid[2][1], m_vertex_grid[2][2], m_vertex_grid[1][2] }; + for (unsigned int i = 0; i < 4; i++) + { + if (nullptr == center_vertices[i]) + return ON_SUBD_RETURN_ERROR(0); + if (nullptr == m_center_edges[i]) + return ON_SUBD_RETURN_ERROR(0); + } + ON_2dex dex; ON_2dex deltadex; const ON_SubDFace* face; unsigned int i; double Q[3][3]; - double* P1; + double* P1[3]; if (!ON_IsValid(m_srf_cv1[2][2][0])) { - // all sub surfaces require inner 3x3 grid of subdivision points + // All sub surfaces require inner 3x3 grid of subdivision points + // In all cases these are the 9 subdivision points of the central quad + // 9 points + // = the quad face subdivision point at m_srf_cv1[2][2] + // + 4 edge subdivision pointsat m_srf_cv1[2][1], m_srf_cv1[3][2], m_srf_cv1[2][3], m_srf_cv1[1][2] + // + 4 vertex subdivision points at m_srf_cv1[1][1], m_srf_cv1[3][1], m_srf_cv1[3][3], m_srf_cv1[1][3] face = m_face_grid[1][1]; - if (4 != face->m_edge_count) - return ON_SUBD_RETURN_ERROR(0); // The value of m_srf_cv1[2][2][0] is used to determine when the inner 3x3 grid is set, // so its value must be set after the 8 cvs around it are successfully set. @@ -1306,184 +1325,264 @@ unsigned int ON_SubDQuadNeighborhood::SetLimitSubSurfaceExactCVs( // is calculated here because if face->GetSubdivisionPoint() fails, // nothing below should succeed. The value of Q[2] is assigned to m_srf_cv1[2][2] // after the 8 ring points are successfully calculated. - if (!face->GetSubdivisionPoint( Q[2])) + if (!face->GetSubdivisionPoint(Q[2])) + { return ON_SUBD_RETURN_ERROR(0); + } - // faces_vertices[] face, in counterclockwise order. - // m_center_edges[] are the 4 edges of face in counterclockwise order with - // m_center_edges[0] connecting face_vertices[0] and face_vertices[1]. - // However, it may be that face->Vertex(0) != face_vertices[0]. - const ON_SubDVertex* faces_vertices[4] = { m_vertex_grid[1][1], m_vertex_grid[2][1], m_vertex_grid[2][2], m_vertex_grid[1][2] }; const ON_2dex srf_cv_dex[8] = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 3, 2 }, { 3, 3 }, { 2, 3 }, { 1, 3 }, { 1, 2 } }; for (unsigned int fei = 0; fei < 4; fei++) { - if (nullptr == faces_vertices[fei]) - return ON_SUBD_RETURN_ERROR(0); - if (!faces_vertices[fei]->GetSubdivisionPoint( Q[0])) - return ON_SUBD_RETURN_ERROR(0); - if (nullptr == m_center_edges[fei]) + if (!center_vertices[fei]->GetSubdivisionPoint( Q[0])) return ON_SUBD_RETURN_ERROR(0); if (!m_center_edges[fei]->GetSubdivisionPoint( Q[1])) return ON_SUBD_RETURN_ERROR(0); dex = srf_cv_dex[2 * fei]; - P1 = m_srf_cv1[dex.i][dex.j]; - P1[0] = Q[0][0]; P1[1] = Q[0][1]; P1[2] = Q[0][2]; + P1[0] = m_srf_cv1[dex.i][dex.j]; + P1[0][0] = Q[0][0]; P1[0][1] = Q[0][1]; P1[0][2] = Q[0][2]; dex = srf_cv_dex[2 * fei + 1]; - P1 = m_srf_cv1[dex.i][dex.j]; - P1[0] = Q[1][0]; P1[1] = Q[1][1]; P1[2] = Q[1][2]; + P1[1] = m_srf_cv1[dex.i][dex.j]; + P1[1][0] = Q[1][0]; P1[1][1] = Q[1][1]; P1[1][2] = Q[1][2]; } // m_srf_cv1[2][2][0] is used to determine when the inner 3x3 grid is set, // so its value must be set after the 8 cvs around it are successfully set. - P1 = m_srf_cv1[2][2]; - P1[0] = Q[2][0]; P1[1] = Q[2][1]; P1[2] = Q[2][2]; + P1[2] = m_srf_cv1[2][2]; + P1[2][0] = Q[2][0]; P1[2][1] = Q[2][1]; P1[2][2] = Q[2][2]; } const unsigned int fvi_min = (4 == quadrant_index) ? 0 : quadrant_index; const unsigned int fvi_max = (4 == quadrant_index) ? 4 : fvi_min+1; - unsigned int quadrant_count = 0; + unsigned int set_quadrant_count = 0; + unsigned int exact_quadrant_count = 0; for (unsigned int fvi = fvi_min; fvi < fvi_max; fvi++) { - if (false == m_bExactQuadrantPatch[fvi]) + if (m_bExactQuadrantPatch[fvi]) + ++exact_quadrant_count; + else if (false == bEnableApproximatePatch) continue; - quadrant_count++; + + unsigned int set_cv_count = 9; // 3x3 grid is set + const unsigned int fvi3 = (fvi + 3) % 4; + for (unsigned int side_pass = 0; side_pass < 2; side_pass++) { const unsigned int side_fvi = (0 == side_pass) ? fvi : fvi3; dex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 0); deltadex = ON_SubDQuadNeighborhood::DeltaDex(side_fvi, 1, 0); - - P1 = &m_srf_cv1[dex.i][dex.j][0]; - if ( !ON_IsValid(P1[0]) ) + P1[0] = &m_srf_cv1[dex.i][dex.j][0]; + dex.i += deltadex.i; + dex.j += deltadex.j; + P1[1] = &m_srf_cv1[dex.i][dex.j][0]; + dex.i += deltadex.i; + dex.j += deltadex.j; + P1[2] = &m_srf_cv1[dex.i][dex.j][0]; + if (ON_IsValid(P1[0][0]) && ON_IsValid(P1[1][0]) && ON_IsValid(P1[2][0])) { - bool bEvaluateCrease = m_bBoundaryCrease[side_fvi] || m_center_edges[side_fvi]->IsCrease(); - if (bEvaluateCrease) - { - ON_2dex Adex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 1); - ON_2dex Bdex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 2); - for (i = 0; i < 3; i++) - { - const double* A = m_srf_cv1[Adex.i][Adex.j]; - const double* B = m_srf_cv1[Bdex.i][Bdex.j]; - Q[i][0] = 2.0*A[0] - B[0]; - Q[i][1] = 2.0*A[1] - B[1]; - Q[i][2] = 2.0*A[2] - B[2]; - Adex.i += deltadex.i; - Adex.j += deltadex.j; - Bdex.i += deltadex.i; - Bdex.j += deltadex.j; - } - } - else if ( m_center_edges[side_fvi]->IsSmoothNotX() ) - { - const ON_SubDEdge* edge = m_edge_grid[side_fvi][0]; - if (nullptr == edge) - return ON_SUBD_RETURN_ERROR(0); - if (!edge->GetSubdivisionPoint(Q[0])) - return ON_SUBD_RETURN_ERROR(0); + set_cv_count += 3; + continue; // Already set + } - const ON_2dex fdex = ON_SubDQuadNeighborhood::GridDex(3, side_fvi, 1, 0); - face = m_face_grid[fdex.i][fdex.j]; - if (nullptr == face) - return ON_SUBD_RETURN_ERROR(0); - if (!face->GetSubdivisionPoint(Q[1])) - return ON_SUBD_RETURN_ERROR(0); - - edge = m_edge_grid[side_fvi][1]; - if (nullptr == edge) - return ON_SUBD_RETURN_ERROR(0); - if (!edge->GetSubdivisionPoint(Q[2])) - return ON_SUBD_RETURN_ERROR(0); - } + // m_bBoundaryCrease[] is true if m_center_edges[fvi]->IsCrease() is true AND neither end is a dart vertex. + // Using bCenterEdgeCrease0 is required to get correct results when bEnableApproximatePatch is true + const bool bCenterEdgeCrease = bEnableApproximatePatch ? m_center_edges[side_fvi]->IsCrease() : m_bBoundaryCrease[side_fvi]; + if ( bCenterEdgeCrease ) + { + // When approximation is being applied (m_bExactQuadrantPatch[fvi] is false and bEnableApproximatePatch is true), + // This technique insures the approximations on either side of the crease + // create a continuous edge. + ON_2dex Adex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 1); + ON_2dex Bdex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 2); for (i = 0; i < 3; i++) { - P1 = m_srf_cv1[dex.i][dex.j]; - P1[0] = Q[i][0]; P1[1] = Q[i][1]; P1[2] = Q[i][2]; - dex.i += deltadex.i; - dex.j += deltadex.j; + const double* A = m_srf_cv1[Adex.i][Adex.j]; + const double* B = m_srf_cv1[Bdex.i][Bdex.j]; + Q[i][0] = 2.0*A[0] - B[0]; + Q[i][1] = 2.0*A[1] - B[1]; + Q[i][2] = 2.0*A[2] - B[2]; + Adex.i += deltadex.i; + Adex.j += deltadex.j; + Bdex.i += deltadex.i; + Bdex.j += deltadex.j; } } - } - - dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 0); - P1 = &m_srf_cv1[dex.i][dex.j][0]; - if (!ON_IsValid(P1[0])) - { - if (m_bBoundaryCrease[fvi] && m_bBoundaryCrease[fvi3]) + else if ( (m_bExactQuadrantPatch[fvi] || subdivision_count >= 2) && m_center_edges[side_fvi]->IsSmoothNotX() ) { - dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 1); - Q[0][0] = m_srf_cv1[dex.i][dex.j][0]; - Q[0][1] = m_srf_cv1[dex.i][dex.j][1]; - Q[0][2] = m_srf_cv1[dex.i][dex.j][2]; - dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 2); - Q[1][0] = m_srf_cv1[dex.i][dex.j][0]; - Q[1][1] = m_srf_cv1[dex.i][dex.j][1]; - Q[1][2] = m_srf_cv1[dex.i][dex.j][2]; - dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 2); - Q[2][0] = m_srf_cv1[dex.i][dex.j][0]; - Q[2][1] = m_srf_cv1[dex.i][dex.j][1]; - Q[2][2] = m_srf_cv1[dex.i][dex.j][2]; + const ON_SubDEdge* edge = m_edge_grid[side_fvi][0]; + if (nullptr == edge) + { + if (m_bExactQuadrantPatch[fvi]) + return ON_SUBD_RETURN_ERROR(0); + Q[0][0] = ON_UNSET_VALUE; + } + else if (!edge->GetSubdivisionPoint(Q[0])) + return ON_SUBD_RETURN_ERROR(0); - dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 1); - //const double c = 4.0; - //const double b = -2.0; - const double c = -8.0; - const double b = 4.0; - P1[0] = c*m_srf_cv1[dex.i][dex.j][0] + b*(Q[0][0] + Q[1][0]) + Q[2][0]; - P1[1] = c*m_srf_cv1[dex.i][dex.j][1] + b*(Q[0][1] + Q[1][1]) + Q[2][1]; - P1[2] = c*m_srf_cv1[dex.i][dex.j][2] + b*(Q[0][2] + Q[1][2]) + Q[2][2]; - } - else if (m_bBoundaryCrease[fvi]) - { - dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 1); - Q[0][0] = m_srf_cv1[dex.i][dex.j][0]; - Q[0][1] = m_srf_cv1[dex.i][dex.j][1]; - Q[0][2] = m_srf_cv1[dex.i][dex.j][2]; - dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 2); - Q[1][0] = m_srf_cv1[dex.i][dex.j][0]; - Q[1][1] = m_srf_cv1[dex.i][dex.j][1]; - Q[1][2] = m_srf_cv1[dex.i][dex.j][2]; + const ON_2dex fdex = ON_SubDQuadNeighborhood::GridDex(3, side_fvi, 1, 0); + face = m_face_grid[fdex.i][fdex.j]; + if (nullptr == face) + { + if (m_bExactQuadrantPatch[fvi]) + return ON_SUBD_RETURN_ERROR(0); + Q[1][0] = ON_UNSET_VALUE; + } + else if (!face->GetSubdivisionPoint(Q[1])) + return ON_SUBD_RETURN_ERROR(0); - P1[0] = 2.0*Q[0][0] - Q[1][0]; - P1[1] = 2.0*Q[0][1] - Q[1][1]; - P1[2] = 2.0*Q[0][2] - Q[1][2]; - } - else if (m_bBoundaryCrease[fvi3]) - { - dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 0); - Q[0][0] = m_srf_cv1[dex.i][dex.j][0]; - Q[0][1] = m_srf_cv1[dex.i][dex.j][1]; - Q[0][2] = m_srf_cv1[dex.i][dex.j][2]; - dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 0); - Q[1][0] = m_srf_cv1[dex.i][dex.j][0]; - Q[1][1] = m_srf_cv1[dex.i][dex.j][1]; - Q[1][2] = m_srf_cv1[dex.i][dex.j][2]; - - P1[0] = 2.0*Q[0][0] - Q[1][0]; - P1[1] = 2.0*Q[0][1] - Q[1][1]; - P1[2] = 2.0*Q[0][2] - Q[1][2]; + edge = m_edge_grid[side_fvi][1]; + if (nullptr == edge) + { + if (m_bExactQuadrantPatch[fvi]) + return ON_SUBD_RETURN_ERROR(0); + Q[2][0] = ON_UNSET_VALUE; + } + else if (!edge->GetSubdivisionPoint(Q[2])) + return ON_SUBD_RETURN_ERROR(0); } else { - dex = ON_SubDQuadNeighborhood::GridDex(3, fvi, 0, 0); - const ON_SubDFace* face_ij = m_face_grid[dex.i][dex.j]; - if (nullptr == face_ij) - return ON_SUBD_RETURN_ERROR(0); - if (!face_ij->GetSubdivisionPoint(Q[0])) - return ON_SUBD_RETURN_ERROR(0); - P1[0] = Q[0][0]; P1[1] = Q[0][1]; P1[2] = Q[0][2]; + // Need more subdivisions before these can be set. + continue; } + + for (i = 0; i < 3; i++) + { + if (ON_IsValid(P1[i][0])) + { + ++set_cv_count; + continue; + } + if (ON_IsValid(Q[i][0])) + { + ++set_cv_count; + P1[i][0] = Q[i][0]; + P1[i][1] = Q[i][1]; + P1[i][2] = Q[i][2]; + } + } + } + + if ( + m_bExactQuadrantPatch[fvi] + || + (false == m_bExtraordinaryCornerVertex[fvi] && bEnableApproximatePatch)) + { + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 0); + P1[0] = &m_srf_cv1[dex.i][dex.j][0]; + if (ON_IsValid(P1[0][0])) + { + ++set_cv_count; + } + else + { + if ( center_vertices[fvi]->IsSmooth() ) + { + if (4 == center_vertices[fvi]->m_edge_count && 4 == center_vertices[fvi]->m_face_count) + { + dex = ON_SubDQuadNeighborhood::GridDex(3, fvi, 0, 0); + const ON_SubDFace* face_ij = m_face_grid[dex.i][dex.j]; + if (nullptr == face_ij) + return ON_SUBD_RETURN_ERROR(0); + if (!face_ij->GetSubdivisionPoint(Q[0])) + return ON_SUBD_RETURN_ERROR(0); + ++set_cv_count; + P1[0][0] = Q[0][0]; P1[0][1] = Q[0][1]; P1[0][2] = Q[0][2]; + } + } + else if (center_vertices[fvi]->IsCrease()) + { + if (m_bBoundaryCrease[fvi] && m_bBoundaryCrease[fvi3]) + { + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 1); + Q[0][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[0][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[0][2] = m_srf_cv1[dex.i][dex.j][2]; + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 2); + Q[1][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[1][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[1][2] = m_srf_cv1[dex.i][dex.j][2]; + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 2); + Q[2][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[2][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[2][2] = m_srf_cv1[dex.i][dex.j][2]; + + if (ON_IsValid(Q[0][0]) && ON_IsValid(Q[1][0]) && ON_IsValid(Q[2][0])) + { + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 1); + if (ON_IsValid(m_srf_cv1[dex.i][dex.j][0])) + { + const double c = -8.0; + const double b = 4.0; + ++set_cv_count; + P1[0][0] = c * m_srf_cv1[dex.i][dex.j][0] + b * (Q[0][0] + Q[1][0]) + Q[2][0]; + P1[0][1] = c * m_srf_cv1[dex.i][dex.j][1] + b * (Q[0][1] + Q[1][1]) + Q[2][1]; + P1[0][2] = c * m_srf_cv1[dex.i][dex.j][2] + b * (Q[0][2] + Q[1][2]) + Q[2][2]; + } + } + } + else if (m_bBoundaryCrease[fvi] && false == m_bBoundaryCrease[fvi3]) + { + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 1); + Q[0][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[0][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[0][2] = m_srf_cv1[dex.i][dex.j][2]; + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 2); + Q[1][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[1][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[1][2] = m_srf_cv1[dex.i][dex.j][2]; + + if (ON_IsValid(Q[0][0]) && ON_IsValid(Q[1][0])) + { + ++set_cv_count; + P1[0][0] = 2.0 * Q[0][0] - Q[1][0]; + P1[0][1] = 2.0 * Q[0][1] - Q[1][1]; + P1[0][2] = 2.0 * Q[0][2] - Q[1][2]; + } + } + else if (false == m_bBoundaryCrease[fvi] && m_bBoundaryCrease[fvi3]) + { + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 0); + Q[0][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[0][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[0][2] = m_srf_cv1[dex.i][dex.j][2]; + dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 0); + Q[1][0] = m_srf_cv1[dex.i][dex.j][0]; + Q[1][1] = m_srf_cv1[dex.i][dex.j][1]; + Q[1][2] = m_srf_cv1[dex.i][dex.j][2]; + + if (ON_IsValid(Q[0][0]) && ON_IsValid(Q[1][0])) + { + ++set_cv_count; + P1[0][0] = 2.0 * Q[0][0] - Q[1][0]; + P1[0][1] = 2.0 * Q[0][1] - Q[1][1]; + P1[0][2] = 2.0 * Q[0][2] - Q[1][2]; + } + } + else if (m_bExactQuadrantPatch[fvi]) + { + ON_SUBD_ERROR("Why is this happening?"); + } + } + } + } + if (16 == set_cv_count) + { + // quadrant with index fvi is set; + ++set_quadrant_count; } } - return quadrant_count; + if (exact_quadrant_count > set_quadrant_count) + return ON_SUBD_RETURN_ERROR(set_quadrant_count); + + return set_quadrant_count; } bool ON_SubDQuadNeighborhood::GetLimitSubSurfaceSinglePatchCV( @@ -1497,8 +1596,8 @@ bool ON_SubDQuadNeighborhood::GetLimitSubSurfaceSinglePatchCV( if (false == m_bExactQuadrantPatch[fvi]) return ON_SUBD_RETURN_ERROR(false); - unsigned int quadrant_count = SetLimitSubSurfaceExactCVs(fvi); - if ( 1 != quadrant_count ) + const unsigned int set_quadrant_count = SetLimitSubSurfaceExactCVs(false,fvi); + if ( 1 != set_quadrant_count ) return ON_SUBD_RETURN_ERROR(false); ON_2dex dex; @@ -1528,7 +1627,7 @@ bool ON_SubDQuadNeighborhood::GetLimitSubSurfaceSinglePatchCV( unsigned int ON_SubDQuadNeighborhood::ExtraordinaryCenterVertexIndex( - ON_SubD::VertexTag vertex_tag_filter, + ON_SubDVertexTag vertex_tag_filter, unsigned int minimum_edge_count_filter ) const { @@ -1551,12 +1650,12 @@ unsigned int ON_SubDQuadNeighborhood::ExtraordinaryCenterVertexIndex( break; if (nullptr == m_vertex_grid[dex.i][dex.j]) break; - if (ON_SubD::VertexTag::Corner != m_vertex_grid[dex.i][dex.j]->m_vertex_tag) + if (ON_SubDVertexTag::Corner != m_vertex_grid[dex.i][dex.j]->m_vertex_tag) { if (((unsigned int)(m_vertex_grid[dex.i][dex.j]->m_edge_count)) < minimum_edge_count_filter) break; if ( - ON_SubD::VertexTag::Unset != vertex_tag_filter + ON_SubDVertexTag::Unset != vertex_tag_filter && m_vertex_grid[dex.i][dex.j]->m_vertex_tag != vertex_tag_filter ) { @@ -1584,99 +1683,124 @@ bool ON_SubDQuadNeighborhood::Internal_GetApproximateCV( const ON_SubDFace* f = nullptr; if (0 == j) { - switch (i) + if (nullptr != m_center_edges[0] && m_center_edges[0]->IsSmooth()) { - case 0: - if ( false == m_bExtraordinaryCornerVertex[0] ) - f = m_face_grid[0][0]; - break; - case 1: - e = m_edge_grid[0][0]; - break; - case 2: - f = m_face_grid[1][0]; - break; - case 3: - e = m_edge_grid[0][1]; - break; - case 4: - if ( false == m_bExtraordinaryCornerVertex[1] ) - f = m_face_grid[2][0]; - break; + switch (i) + { + case 0: + if (false == m_bExtraordinaryCornerVertex[0]) + { + const ON_SubDVertex* v = this->CenterVertex(0); + if (nullptr != v && v->IsSmooth()) + f = m_face_grid[0][0]; + } + break; + case 1: + e = m_edge_grid[0][0]; + break; + case 2: + f = m_face_grid[1][0]; + break; + case 3: + e = m_edge_grid[0][1]; + break; + case 4: + if (false == m_bExtraordinaryCornerVertex[1]) + { + const ON_SubDVertex* v = this->CenterVertex(1); + if (nullptr != v && v->IsSmooth()) + f = m_face_grid[2][0]; + } + break; + } } } else if (4 == i) { - switch (j) + if (nullptr != m_center_edges[1] && m_center_edges[1]->IsSmooth()) { - // case 0: // i = 0; j = 0 handled above - case 1: - e = m_edge_grid[1][0]; - break; - case 2: - f = m_face_grid[2][1]; - break; - case 3: - e = m_edge_grid[1][1]; - break; - case 4: - if ( false == m_bExtraordinaryCornerVertex[2] ) - f = m_face_grid[2][2]; - break; + switch (j) + { + // case 0: // i = 0; j = 0 handled above + case 1: + e = m_edge_grid[1][0]; + break; + case 2: + f = m_face_grid[2][1]; + break; + case 3: + e = m_edge_grid[1][1]; + break; + case 4: + if (false == m_bExtraordinaryCornerVertex[2]) + { + const ON_SubDVertex* v = this->CenterVertex(2); + if (nullptr != v && v->IsSmooth()) + f = m_face_grid[2][2]; + } + break; + } } } else if (4 == j) { - switch (i) + if (nullptr != m_center_edges[2] && m_center_edges[2]->IsSmooth()) { - case 0: - if ( false == m_bExtraordinaryCornerVertex[3] ) - f = m_face_grid[0][2]; - break; - case 1: - e = m_edge_grid[2][1]; - break; - case 2: - f = m_face_grid[1][2]; - break; - case 3: - e = m_edge_grid[2][0]; - break; - // case 4: // i = 4; j = 4 handled above + switch (i) + { + case 0: + if (false == m_bExtraordinaryCornerVertex[3]) + { + const ON_SubDVertex* v = this->CenterVertex(3); + if (nullptr != v && v->IsSmooth()) + f = m_face_grid[0][2]; + } + break; + case 1: + e = m_edge_grid[2][1]; + break; + case 2: + f = m_face_grid[1][2]; + break; + case 3: + e = m_edge_grid[2][0]; + break; + // case 4: // i = 4; j = 4 handled above + } } } else if (0 == i) { - switch (j) + if (nullptr != m_center_edges[3] && m_center_edges[3]->IsSmooth()) { - // case 0: // i = 0; j = 0 handled above - case 1: - e = m_edge_grid[3][1]; - break; - case 2: - f = m_face_grid[0][1]; - break; - case 3: - e = m_edge_grid[3][0]; - break; - // case 4: // i = 0; j = 4 handled above + switch (j) + { + // case 0: // i = 0; j = 0 handled above + case 1: + e = m_edge_grid[3][1]; + break; + case 2: + f = m_face_grid[0][1]; + break; + case 3: + e = m_edge_grid[3][0]; + break; + // case 4: // i = 0; j = 4 handled above + } } } bool bHaveApproximateCV; if (nullptr != e) { - const int extraordinary_vertex_index = ExtraordinaryCenterVertexIndex(ON_SubD::VertexTag::Crease, 4); + const int extraordinary_vertex_index = ExtraordinaryCenterVertexIndex(ON_SubDVertexTag::Crease, 4); const ON_SubDVertex* extraordinary_vertex = (extraordinary_vertex_index >= 0 && extraordinary_vertex_index < 4) ? CenterVertex(extraordinary_vertex_index) : nullptr; - if ( - (ON_SubD::EdgeTag::Smooth == e->m_edge_tag || ON_SubD::EdgeTag::Crease == e->m_edge_tag) - && (e->m_vertex[0] == extraordinary_vertex || e->m_vertex[1] == extraordinary_vertex) - ) + if ( e->m_vertex[0] == extraordinary_vertex || e->m_vertex[1] == extraordinary_vertex ) { - // extraordinary crease vertices + // an extraordinary crease vertex is on this edge bHaveApproximateCV = false; } else @@ -1704,7 +1828,7 @@ static double ON_SubDQuadFaceTopology_CopySectorWeight( if (nullptr == e0 || nullptr == e0v) return ON_SUBD_RETURN_ERROR(false); - if (ON_SubD::EdgeTag::Smooth != e0->m_edge_tag && ON_SubD::EdgeTag::SmoothX != e0->m_edge_tag ) + if (ON_SubDEdgeTag::Smooth != e0->m_edge_tag && ON_SubDEdgeTag::SmoothX != e0->m_edge_tag ) return ON_SubDSectorType::IgnoredSectorCoefficient; if (e0v == e0->m_vertex[0]) @@ -1737,7 +1861,7 @@ static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge( // qv1 is the subdivision point of qv0. if ( qv1->m_vertex_tag != qv0->m_vertex_tag ) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); - if ( ON_SubD::VertexTag::Smooth == qv0->m_vertex_tag) + if ( ON_SubDVertexTag::Smooth == qv0->m_vertex_tag) v0_weight = ON_SubDSectorType::IgnoredSectorCoefficient; else { @@ -1758,9 +1882,9 @@ static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge( if (e1->m_edge_tag != e0->m_edge_tag) { // On the first subdivision step, - // e0 with tag ON_SubD::EdgeTag::SmoothX turns into - // e1 with tag ON_SubD::EdgeTag::Smooth. - if ( ON_SubD::EdgeTag::Smooth != e1->m_edge_tag || ON_SubD::EdgeTag::SmoothX != e0->m_edge_tag) + // e0 with tag ON_SubDEdgeTag::SmoothX turns into + // e1 with tag ON_SubDEdgeTag::Smooth. + if ( ON_SubDEdgeTag::Smooth != e1->m_edge_tag || ON_SubDEdgeTag::SmoothX != e0->m_edge_tag) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); } @@ -1797,9 +1921,9 @@ static ON_SubDFace* ON_SubDQuadFaceTopology_SubdivideFace( if ( nullptr == v[2]) return ON_SUBD_RETURN_ERROR(nullptr); - const double v1_weight = (ON_SubD::VertexTag::Crease == v[1]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorCoefficient; + const double v1_weight = (ON_SubDVertexTag::Crease == v[1]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorCoefficient; const double v2_weight = ON_SubDSectorType::IgnoredSectorCoefficient; - const double v3_weight = (ON_SubD::VertexTag::Crease == v[3]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorCoefficient; + const double v3_weight = (ON_SubDVertexTag::Crease == v[3]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorCoefficient; ON_SubDEdgePtr e12 = fsh.AllocateEdge(v[1],v1_weight,v[2],v2_weight); if ( nullptr == e12.Edge()) @@ -1877,7 +2001,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( if (nullptr == qv0 || qv0->m_face_count > qv0->m_edge_count) { - if (ON_SubD::VertexTag::Corner == qv0->m_vertex_tag) + if (ON_SubDVertexTag::Corner == qv0->m_vertex_tag) { // nonmanifold case can have face_count > edge_count if ( qv0->m_edge_count < 3 ) @@ -1900,7 +2024,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( if ( nullptr == sit.Initialize(qf0,0,qv0) ) return ON_SUBD_RETURN_ERROR(false); - const bool bIsDartSector = (ON_SubD::VertexTag::Dart == qv0->m_vertex_tag); + const bool bIsDartSector = (ON_SubDVertexTag::Dart == qv0->m_vertex_tag); const bool bIsCreaseOrCornerSector = qv0->IsCreaseOrCorner(); const bool bBoundaryCrease1[4] = { @@ -2001,7 +2125,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( if (bAtBoundaryCrease) { - e1[1].Edge()->m_edge_tag = ON_SubD::EdgeTag::Crease; + e1[1].Edge()->m_edge_tag = ON_SubDEdgeTag::Crease; break; } @@ -2083,9 +2207,9 @@ bool ON_SubDQuadNeighborhood::Subdivide( face_grid1_10 = f1; f0 = sit.PrevFace(stop_at); - if (nullptr == f0 || ON_SubD::EdgeTag::Crease == e0[1]->m_edge_tag) + if (nullptr == f0 || ON_SubDEdgeTag::Crease == e0[1]->m_edge_tag) { - bFinished = (nullptr == f0 && ON_SubD::EdgeTag::Crease == e0[0]->m_edge_tag && qv1->m_face_count+1 == qv1->m_edge_count); + bFinished = (nullptr == f0 && ON_SubDEdgeTag::Crease == e0[0]->m_edge_tag && qv1->m_face_count+1 == qv1->m_edge_count); if (false == bFinished) return ON_SUBD_RETURN_ERROR(false); break; diff --git a/opennurbs_subd_matrix.cpp b/opennurbs_subd_matrix.cpp index 9677bc3e..91e2864d 100644 --- a/opennurbs_subd_matrix.cpp +++ b/opennurbs_subd_matrix.cpp @@ -562,13 +562,13 @@ unsigned int ON_SubDSectorType::GetAllEigenvalues( if ( 0 == R || (nullptr != eigenvalues && eigenvalues_capacity < R)) return ON_SUBD_RETURN_ERROR(0); - const ON_SubD::VertexTag vertex_tag = VertexTag(); + const ON_SubDVertexTag vertex_tag = VertexTag(); const unsigned int N = EdgeCount(); if (false == ON_SubD::IsValidSectorEdgeCount(vertex_tag, N)) return ON_SUBD_RETURN_ERROR(0); - if (ON_SubD::VertexTag::Smooth == vertex_tag) + if (ON_SubDVertexTag::Smooth == vertex_tag) { if (nullptr == eigenvalues) { @@ -603,7 +603,7 @@ unsigned int ON_SubDSectorType::GetAllEigenvalues( // return sorted in decreasing order ON_SortDoubleArrayDecreasing(eigenvalues + 1, R - 1); } - else if (ON_SubD::VertexTag::Crease == vertex_tag) + else if (ON_SubDVertexTag::Crease == vertex_tag) { if (N <= 20) { @@ -1125,10 +1125,10 @@ double ON_SubDSectorType::SubdominantEigenvalue() const switch (VertexTag()) { - case ON_SubD::VertexTag::Unset: + case ON_SubDVertexTag::Unset: break; - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: if (1 == (R % 2)) { // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth @@ -1150,7 +1150,7 @@ double ON_SubDSectorType::SubdominantEigenvalue() const } break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() // for more details on how this case is handled. @@ -1166,8 +1166,8 @@ double ON_SubDSectorType::SubdominantEigenvalue() const break; - case ON_SubD::VertexTag::Corner: - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Corner: + case ON_SubDVertexTag::Crease: if (0 == (R % 2)) return 0.5; break; @@ -1183,7 +1183,7 @@ unsigned int ON_SubDSectorType::SubdominantEigenvalueMulitiplicity() const return 0; // Catmull-Clark quad subdivision special cases - if (ON_SubD::VertexTag::Crease == m_vertex_tag) + if (ON_SubDVertexTag::Crease == m_vertex_tag) { if (0 == m_sector_face_count) { @@ -1205,7 +1205,7 @@ unsigned int ON_SubDSectorType::SubdominantEigenvalueMulitiplicity() const else if (2 == m_sector_face_count) { // valance 2 special cases - if (ON_SubD::VertexTag::Smooth == m_vertex_tag) + if (ON_SubDVertexTag::Smooth == m_vertex_tag) { // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() @@ -1218,7 +1218,7 @@ unsigned int ON_SubDSectorType::SubdominantEigenvalueMulitiplicity() const return 2; // for the 1/4, 1/4 pair } - if (ON_SubD::VertexTag::Dart == m_vertex_tag) + if (ON_SubDVertexTag::Dart == m_vertex_tag) { // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() @@ -1286,10 +1286,10 @@ double ON_SubDSectorType::GetSubdominantEigenvectors( switch (VertexTag()) { - case ON_SubD::VertexTag::Unset: + case ON_SubDVertexTag::Unset: break; - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: if (1 == (R % 2)) { if (nullptr != E1) @@ -1329,7 +1329,7 @@ double ON_SubDSectorType::GetSubdominantEigenvectors( } break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: if (1 == (R % 2)) { if (nullptr != E1) @@ -1367,7 +1367,7 @@ double ON_SubDSectorType::GetSubdominantEigenvectors( } break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: if (0 == (R % 2)) { const unsigned int sector_angle_index = CornerSectorAngleIndex(); @@ -1403,7 +1403,7 @@ double ON_SubDSectorType::GetSubdominantEigenvectors( } break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: if (0 == (R % 2)) { if (1 == F) @@ -1496,7 +1496,7 @@ double ON_SubDSectorType::SurfaceNormalSign() const return ON_SUBD_RETURN_ERROR(rc_error); const unsigned int R = PointRingCount(); - const ON_SubD::VertexTag vertex_tag = VertexTag(); + const ON_SubDVertexTag vertex_tag = VertexTag(); ON_SimpleArray buffer; double* LP = buffer.Reserve(3*R); @@ -1512,14 +1512,14 @@ double ON_SubDSectorType::SurfaceNormalSign() const switch (vertex_tag) { - case ON_SubD::VertexTag::Smooth: - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Smooth: + case ON_SubDVertexTag::Dart: sector_angle = 2.0*ON_PI; break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: sector_angle = 0.5*ON_PI; break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: sector_angle = CornerSectorAngleRadians(); break; default: @@ -1536,7 +1536,7 @@ bool ON_SubDSectorType::SurfaceEvaluationCoefficientsAvailable() const if (IsValid()) { // Available as of March 23, 2015 - //if (ON_SubD::VertexTag::Dart == m_vertex_tag && FaceCount() > 5) + //if (ON_SubDVertexTag::Dart == m_vertex_tag && FaceCount() > 5) //{ // // temporary limit // return false; @@ -1606,10 +1606,10 @@ unsigned int ON_SubDSectorType::GetSurfaceEvaluationCoefficients( switch (VertexTag()) { - case ON_SubD::VertexTag::Unset: + case ON_SubDVertexTag::Unset: break; - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: if (R >= 5 && 1 == (R % 2)) { if (nullptr != LP) @@ -1695,7 +1695,7 @@ unsigned int ON_SubDSectorType::GetSurfaceEvaluationCoefficients( } break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: if (1 == (R % 2)) { ON_Matrix Sbuffer; @@ -1993,7 +1993,7 @@ unsigned int ON_SubDSectorType::GetSurfaceEvaluationCoefficients( break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: if (0 == (R % 2)) { const unsigned int angle_index = CornerSectorAngleIndex(); @@ -2040,7 +2040,7 @@ unsigned int ON_SubDSectorType::GetSurfaceEvaluationCoefficients( if ( false == b180degreeCorner) break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: // NOTE: In the case when there are 2 crease edes and a single face, // The Catmull-Clark subdivision matrix is singular. if (0 == (R % 2)) @@ -2199,7 +2199,7 @@ static bool Internal_GetAlterateTangent( if (2 == Ldex) { - if ( 4 == subd_matrix.m_R && ON_SubD::VertexTag::Crease == subd_matrix.m_sector_type.VertexTag() ) + if ( 4 == subd_matrix.m_R && ON_SubDVertexTag::Crease == subd_matrix.m_sector_type.VertexTag() ) { // valence 2 crease case when crease edges are colinear // F = face point, C = crease vertex point. @@ -2234,7 +2234,7 @@ static bool Internal_GetAlterateNormal( if (point_ring_count < 4 || point_ring_stride < 3 || nullptr == point_ring) return false; - if ( 4 == subd_matrix.m_R && ON_SubD::VertexTag::Crease == subd_matrix.m_sector_type.VertexTag() ) + if ( 4 == subd_matrix.m_R && ON_SubDVertexTag::Crease == subd_matrix.m_sector_type.VertexTag() ) { // valence 2 crease case when crease edges are colinear // F = face point, C = crease vertex point. @@ -2911,11 +2911,11 @@ double ON_SubDMatrix::TestEvaluation( ON_TextLog* text_log ) { - ON_SubD::VertexTag vertex_tags[] = { - ON_SubD::VertexTag::Smooth - ,ON_SubD::VertexTag::Crease - ,ON_SubD::VertexTag::Corner - ,ON_SubD::VertexTag::Dart + ON_SubDVertexTag vertex_tags[] = { + ON_SubDVertexTag::Smooth + ,ON_SubDVertexTag::Crease + ,ON_SubDVertexTag::Corner + ,ON_SubDVertexTag::Dart }; const char* vertex_tag_names[sizeof(vertex_tags) / sizeof(vertex_tags[0])] = { "smooth" @@ -2927,12 +2927,12 @@ double ON_SubDMatrix::TestEvaluation( unsigned int corner_sector_angle_index0 = ON_UNSET_UINT_INDEX-1; unsigned int corner_sector_angle_index1 = ON_UNSET_UINT_INDEX; const double corner_sector_angle_radians - = (ON_SubD::VertexTag::Corner == sector_type.VertexTag()) + = (ON_SubDVertexTag::Corner == sector_type.VertexTag()) ? sector_type.CornerSectorAngleRadians() : ON_SubDSectorType::UnsetCornerSectorAngle; - ON_SubD::VertexTag vertex_tag = sector_type.VertexTag(); + ON_SubDVertexTag vertex_tag = sector_type.VertexTag(); size_t subd_type_count = 1; size_t subd_type_index0 = 0; @@ -2943,7 +2943,7 @@ double ON_SubDMatrix::TestEvaluation( size_t vertex_tag_count = sizeof(vertex_tags) / sizeof(vertex_tags[0]); size_t vertex_tag_index0 = 0; - if (ON_SubD::VertexTag::Unset != vertex_tag) + if (ON_SubDVertexTag::Unset != vertex_tag) { for (size_t vertex_tag_index = vertex_tag_index0; vertex_tag_index < vertex_tag_count; vertex_tag_index++) { @@ -2951,7 +2951,7 @@ double ON_SubDMatrix::TestEvaluation( { vertex_tag_index0 = vertex_tag_index; vertex_tag_count = vertex_tag_index + 1; - if (ON_SubD::VertexTag::Corner == vertex_tag && ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians) ) + if (ON_SubDVertexTag::Corner == vertex_tag && ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians) ) { unsigned int angle_dex = sector_type.CornerSectorAngleIndex(); if (angle_dex <= ON_SubDSectorType::MaximumCornerAngleIndex) @@ -2984,7 +2984,7 @@ double ON_SubDMatrix::TestEvaluation( for (size_t vertex_tag_index = vertex_tag_index0; vertex_tag_index < vertex_tag_count; vertex_tag_index++) { - const ON_SubD::VertexTag vertex_tag_for_scope = vertex_tags[vertex_tag_index]; + const ON_SubDVertexTag vertex_tag_for_scope = vertex_tags[vertex_tag_index]; const char* sVertexTagName = vertex_tag_names[vertex_tag_index]; unsigned int Fmin = ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag_for_scope); @@ -2993,7 +2993,7 @@ double ON_SubDMatrix::TestEvaluation( unsigned int angle_i0 = corner_sector_angle_index0; unsigned int angle_i1 = corner_sector_angle_index1; - if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope && ON_SubDSectorType::UnsetCornerSectorAngle == corner_sector_angle_radians) + if (ON_SubDVertexTag::Corner == vertex_tag_for_scope && ON_SubDSectorType::UnsetCornerSectorAngle == corner_sector_angle_radians) { angle_i0 = 2; angle_i1 = ON_SubDSectorType::MaximumCornerAngleIndex/2 - 1; @@ -3004,7 +3004,7 @@ double ON_SubDMatrix::TestEvaluation( for (unsigned int corner_sector_angle_index = angle_i0; corner_sector_angle_index < angle_i1; corner_sector_angle_index++) { double angle_radians = corner_sector_angle_radians; - if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope && ON_SubDSectorType::UnsetCornerSectorAngle == angle_radians) + if (ON_SubDVertexTag::Corner == vertex_tag_for_scope && ON_SubDSectorType::UnsetCornerSectorAngle == angle_radians) angle_radians = ON_SubDSectorType::AngleRadiansFromCornerAngleIndex(corner_sector_angle_index); ON_SubDSectorType test_sector_type = ON_SubDSectorType::Create( vertex_tag_for_scope, F, angle_radians); @@ -3026,7 +3026,7 @@ double ON_SubDMatrix::TestEvaluation( if (nullptr != text_log) { ON_String test_description; - if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope) + if (ON_SubDVertexTag::Corner == vertex_tag_for_scope) test_description.Format("%s, %s, %u faces, %u edges, angle = %u/%u 2pi", sSubDTypeName, sVertexTagName, F, N, corner_sector_angle_index, ON_SubDSectorType::MaximumCornerAngleIndex); else test_description.Format("%s, %s, %u faces, %u edges", sSubDTypeName, sVertexTagName, F, N); @@ -3036,7 +3036,7 @@ double ON_SubDMatrix::TestEvaluation( else text_log->Print("Test( %s ) failed\n", (const char*)test_description); } - if (ON_SubD::VertexTag::Corner != vertex_tag_for_scope) + if (ON_SubDVertexTag::Corner != vertex_tag_for_scope) break; if (fail_count >= maximum_fail_count) break; @@ -3097,7 +3097,7 @@ double ON_SubDMatrix::TestComponentRing( if (m_R != R) return ON_SUBD_RETURN_ERROR(rc_error); - const ON_SubD::VertexTag vertex_tag = m_sector_type.VertexTag(); + const ON_SubDVertexTag vertex_tag = m_sector_type.VertexTag(); const unsigned int face_edge_count = m_sector_type.FacetEdgeCount(); const bool bSubdivideFaces = (R == F+N+1 && 4 == face_edge_count); diff --git a/opennurbs_subd_mesh.cpp b/opennurbs_subd_mesh.cpp index 0f2b2adf..3b238353 100644 --- a/opennurbs_subd_mesh.cpp +++ b/opennurbs_subd_mesh.cpp @@ -33,13 +33,13 @@ bool ON_SubDFaceRegionBreakpoint( { #if defined(ON_DEBUG) if ( - 11 != level0_face_id + 8 != level0_face_id ) { return false; } - const unsigned short region_pattern[] = { 3, 3 }; + const unsigned short region_pattern[] = { 2, 2 }; // { 2, 2, 1 }; const unsigned short region_pattern_count = (unsigned short)(sizeof(region_pattern) / sizeof(region_pattern[0])); if (region_index.m_subdivision_count < region_pattern_count) @@ -392,6 +392,33 @@ unsigned short ON_SubDComponentRegionIndex::Index( : 0xFFFF; } +const ON_SubDFace* ON_SubDFaceRegion::Level0Face() const +{ + return this->m_face_region.m_level0_component.Face(); +} + +unsigned int ON_SubDFaceRegion::CornerIndexFromVertexId( + unsigned int vertex_id +) const +{ + unsigned corner_index = ON_UNSET_UINT_INDEX; + if (vertex_id > 0 && vertex_id < ON_UNSET_UINT_INDEX) + { + for (unsigned i = 0; i < 4; ++i) + { + if (vertex_id == this->m_vertex_id[i]) + { + if (ON_UNSET_UINT_INDEX == corner_index) + corner_index = i; + else + return ON_UNSET_UINT_INDEX; + } + } + } + return corner_index; +} + + void ON_SubDFaceRegion::Push(unsigned int quadrant_index) { m_face_region.PushAbsolute(quadrant_index); @@ -411,6 +438,9 @@ void ON_SubDFaceRegion::Push(unsigned int quadrant_index) m_vertex_id[(surviving_vi+1)%4] = 0; m_vertex_id[(surviving_vi+2)%4] = 0; m_vertex_id[(surviving_vi+3)%4] = 0; + m_sector_id[(surviving_vi + 1) % 4] = ON_SubDSectorId::Zero; + m_sector_id[(surviving_vi + 2) % 4] = ON_SubDSectorId::Zero; + m_sector_id[(surviving_vi + 3) % 4] = ON_SubDSectorId::Zero; } bool ON_SubDComponentRegion::IsEmptyRegion() const @@ -445,6 +475,12 @@ bool ON_SubDFaceRegion::IsValid( ON_SUBD_ERROR("m_face_region is empty and m_vertex_id[] is not zero."); return false; } + if (false == m_sector_id[vi].IsZero()) + { + if (false == bSilentError) + ON_SUBD_ERROR("m_face_region is empty and m_sector_id[] is not zero."); + return false; + } } return true; } @@ -632,6 +668,32 @@ bool ON_SubDFaceRegion::IsValid( return false; } } + + const unsigned sector_vertex_id = m_sector_id[vi].VertexId(); + if (0 != sector_vertex_id) + { + if (sector_vertex_id != m_vertex_id[vi]) + { + if (false == bSilentError) + ON_SUBD_ERROR("m_sector_id[].VertexId() is incorrect."); + return false; + } + if (false == m_sector_id[vi].IsSet()) + { + if (false == bSilentError) + ON_SUBD_ERROR("m_sector_id[] is missing face information."); + return false; + } + } + else + { + if (false == m_sector_id[vi].IsZero()) + { + if (false == bSilentError) + ON_SUBD_ERROR("m_sector_id[] is missing vertex information."); + return false; + } + } } else if ( 0 != m_vertex_id[vi] ) { @@ -641,6 +703,12 @@ bool ON_SubDFaceRegion::IsValid( ON_SUBD_ERROR("m_vertex_id[] missing a transient vertex id."); return false; } + if (false == m_sector_id[vi].IsZero()) + { + if (false == bSilentError) + ON_SUBD_ERROR("Transient vertex has an nonzero m_sector_id[]."); + return false; + } } } @@ -930,6 +998,64 @@ const ON_wString ON_SubDComponentRegion::ToString() const return ON_wString::EmptyString; } +const ON_wString ON_SubDSectorId::ToString(bool bVerbose) const +{ + if (IsZero()) + return ON_wString(bVerbose ? L"ON_SubDSectorId::Zero" : L"Zero"); + if (m_sector_face_count > 0xFFFFU) + return ON_wString(bVerbose ? L"ON_SubDSectorId::Invalid" : L"Invalid"); + wchar_t s_buffer[64]; + if (nullptr != ToString(s_buffer, sizeof(s_buffer) / sizeof(s_buffer[0]))) + { + return bVerbose ? ON_wString::FormatToString(L"ON_SubDSectorId %ls", s_buffer) : ON_wString(s_buffer); + } + return ON_wString::EmptyString; +} + +wchar_t* ON_SubDSectorId::ToString( + wchar_t* s, + size_t s_capacity +) const +{ + if (s_capacity <= 0 || nullptr == s) + return nullptr; + + *s = 0; + wchar_t* s1 = s + (s_capacity - 1); + *s1 = 0; + if (s < s1) + { + if (IsZero()) + *s++ = '0'; + else if (m_sector_face_count > 0xFFFFU) + *s++ = 'X'; + else if ( s+6 < s1 ) + { + *s++ = 'v'; + if (nullptr != s && s < s1) + { + s = Internal_AppendUnsigned(this->m_vertex_id, s, s1); + if (nullptr != s && s + 5 < s1) + { + *s++ = '.'; + *s++ = 'f'; + s = Internal_AppendUnsigned(this->m_minimum_face_id, s, s1); + if (nullptr != s && s + 2 < s1) + { + *s++ = 'x'; + s = Internal_AppendUnsigned(this->m_sector_face_count, s, s1); + } + } + } + } + } + + if (nullptr != s && s <= s1) + *s = 0; + + return s; +} + ON__UINT32 ON_SubDComponentRegionIndex::ToCompressedRegionIndex() const { @@ -1063,7 +1189,7 @@ wchar_t* ON_SubDFaceRegion::ToString( *s++ = ')'; } - if (nullptr != s && s+4 < s1) + if (nullptr != s && s + 4 < s1) { for (unsigned int i = 0; i < 4 && nullptr != s && s + 4 < s1; i++) { @@ -1083,6 +1209,29 @@ wchar_t* ON_SubDFaceRegion::ToString( *s++ = ')'; } + if ( + false == m_sector_id[0].IsZero() + || + false == m_sector_id[1].IsZero() + || + false == m_sector_id[2].IsZero() + || + false == m_sector_id[3].IsZero() + ) + { + if (nullptr != s && s + 4 < s1) + { + for (unsigned int i = 0; i < 4 && nullptr != s && s + 4 < s1; i++) + { + *s++ = ON_wString::Space; + *s++ = (0 == i) ? '(' : ','; + s = m_sector_id[i].ToString(s, s1 - s); + } + if (nullptr != s && s < s1) + *s++ = ')'; + } + } + if (nullptr != s && s <= s1) *s = 0; return s; @@ -1221,17 +1370,20 @@ bool ON_SubDMeshFragment::SealAdjacentSides( class VertexToDuplicate { public: + // ON_SubD information const ON_SubDVertex* m_vertex = nullptr; const ON_SubDFace* m_face = nullptr; + unsigned int m_sector_id = 0; // all the dups for a specific vertex that are in the same sector get a unique nonzero sector id + + // ON_Mesh information unsigned int m_mesh_V_index = 0; unsigned int m_mesh_F_index = 0; static int CompareVertexId(const class VertexToDuplicate* a, const class VertexToDuplicate*); - static int CompareVertexAndFaceIds(const class VertexToDuplicate* a, const class VertexToDuplicate*); + static int CompareVertexIdAndFaceId(const class VertexToDuplicate* a, const class VertexToDuplicate*); + static int CompareSectorIdAndFaceId(const class VertexToDuplicate* a, const class VertexToDuplicate*); - static bool NeedsDuplicated( - const ON_SubDVertex* vertex - ); + static bool NeedsDuplicated(const ON_SubDVertex* vertex); }; int VertexToDuplicate::CompareVertexId(const class VertexToDuplicate* a, const class VertexToDuplicate* b) @@ -1252,31 +1404,65 @@ int VertexToDuplicate::CompareVertexId(const class VertexToDuplicate* a, const c return 0; } -int VertexToDuplicate::CompareVertexAndFaceIds(const class VertexToDuplicate* a, const class VertexToDuplicate* b) + +int VertexToDuplicate::CompareVertexIdAndFaceId(const class VertexToDuplicate* a, const class VertexToDuplicate* b) { if ( a == b ) return 0; - int rc = VertexToDuplicate::CompareVertexId(a,b); - if (0 != rc) - return rc; if (nullptr == a) return -1; if (nullptr == b) return 1; - unsigned int a_id = a->m_face ? a->m_face->m_id : 0; - unsigned int b_id = b->m_face ? b->m_face->m_id : 0; + + unsigned int a_id = a->m_vertex ? a->m_vertex->m_id : 0; + unsigned int b_id = b->m_vertex ? b->m_vertex->m_id : 0; if (a_id < b_id) return -1; if (a_id > b_id) return 1; + + a_id = a->m_face ? a->m_face->m_id : 0; + b_id = b->m_face ? b->m_face->m_id : 0; + if (a_id < b_id) + return -1; + if (a_id > b_id) + return 1; + return 0; } +int VertexToDuplicate::CompareSectorIdAndFaceId(const class VertexToDuplicate* a, const class VertexToDuplicate* b) +{ + if (a == b) + return 0; + if (nullptr == a) + return -1; + if (nullptr == b) + return 1; + + unsigned int a_id = a->m_sector_id; + unsigned int b_id = b->m_sector_id; + if (a_id < b_id) + return -1; + if (a_id > b_id) + return 1; + + a_id = a->m_face ? a->m_face->m_id : 0; + b_id = b->m_face ? b->m_face->m_id : 0; + if (a_id < b_id) + return -1; + if (a_id > b_id) + return 1; + + return 0; + +} + bool VertexToDuplicate::NeedsDuplicated( const ON_SubDVertex* vertex ) { - if ( nullptr == vertex || vertex->m_face_count <= 0 || vertex->m_edge_count < 2 || nullptr == vertex->m_edges ) + if ( nullptr == vertex || vertex->m_face_count < 2 || vertex->m_edge_count < 2 || nullptr == vertex->m_edges || nullptr == vertex->m_faces) return false; if (vertex->IsSmooth()) return false; @@ -1284,85 +1470,81 @@ bool VertexToDuplicate::NeedsDuplicated( for (unsigned int vei = 0; vei < edge_count; vei++) { const ON_SubDEdge* edge = vertex->Edge(vei); - if ( nullptr != edge && false == edge->IsSmooth() && edge->m_face_count > 1 ) + if (nullptr != edge && false == edge->IsSmooth() && edge->m_face_count > 1) return true; } return false; } -static bool ChangeMeshFaceIndex( - unsigned int mesh_V_index0, +static bool Internal_UpdateMeshFaceVertexIndex( + ON_Mesh& mesh, + unsigned mesh_F_index, unsigned int mesh_F_count, - ON_Mesh* mesh, - VertexToDuplicate& dup, - ON_SimpleArray& dups_sub_array + unsigned mesh_V_index0, + unsigned mesh_V_index1 ) { - int k = dups_sub_array.BinarySearch(&dup,VertexToDuplicate::CompareVertexAndFaceIds); - if (k < 0) + if (mesh_F_index < mesh_F_count && mesh_V_index0 < mesh_V_index1 ) { - // error. terminate creation of dups. - ON_SubDIncrementErrorCount(); - return false; + unsigned int* fvi = (unsigned int*)(mesh.m_F[mesh_F_index].vi); + if (fvi[0] == mesh_V_index0) + fvi[0] = mesh_V_index1; + if (fvi[1] == mesh_V_index0) + fvi[1] = mesh_V_index1; + if (fvi[2] == mesh_V_index0) + fvi[2] = mesh_V_index1; + if (fvi[3] == mesh_V_index0) + fvi[3] = mesh_V_index1; + return true; } - - VertexToDuplicate* dupk = dups_sub_array.Array() + k; - - if (mesh_V_index0 != dup.m_mesh_V_index) - { - if (mesh_V_index0 == dupk->m_mesh_V_index && dupk->m_mesh_F_index < mesh_F_count) - { - unsigned int* fvi = (unsigned int*)(mesh->m_F[dupk->m_mesh_F_index].vi); - if (fvi[0] == mesh_V_index0) - fvi[0] = dup.m_mesh_V_index; - if (fvi[1] == mesh_V_index0) - fvi[1] = dup.m_mesh_V_index; - if (fvi[2] == mesh_V_index0) - fvi[2] = dup.m_mesh_V_index; - if (fvi[3] == mesh_V_index0) - fvi[3] = dup.m_mesh_V_index; - } - } - dupk->m_mesh_V_index = ON_UNSET_UINT_INDEX; - dupk->m_mesh_F_index = ON_UNSET_UINT_INDEX; - return true; + ON_SubDIncrementErrorCount(); + return false; } -static bool DuplicateVerticesAtCreases( - ON_Mesh* mesh, + +static VertexToDuplicate* Internal_FindMatchingVertexIdAndFaceId(const VertexToDuplicate* key, VertexToDuplicate* vertex_dups, unsigned int vertex_dups_count ) +{ + return (VertexToDuplicate*)bsearch(key, vertex_dups, vertex_dups_count, sizeof(vertex_dups[0]), (int(*)(const void*, const void*))VertexToDuplicate::CompareVertexIdAndFaceId); +} + +static bool Internal_DuplicateVertices( + ON_Mesh& mesh, ON_3dPointArray& D, ON_SimpleArray& dups_array ) { - const unsigned int mesh_F_count = mesh->m_F.UnsignedCount(); + const unsigned int mesh_F_count = mesh.m_F.UnsignedCount(); const unsigned int mesh_D_count0 = D.UnsignedCount(); const unsigned int dups_count = dups_array.UnsignedCount(); if (dups_count <= 1) return true; - dups_array.QuickSort(VertexToDuplicate::CompareVertexAndFaceIds); - ON_SimpleArray dups_sub_array; // for searching + unsigned int sector_id = 0; + + dups_array.QuickSortAndRemoveDuplicates(VertexToDuplicate::CompareVertexIdAndFaceId); + + ON_SubDSectorIterator sit; VertexToDuplicate* dups = dups_array; - VertexToDuplicate dup; + VertexToDuplicate key; unsigned int i1 = 0; for (unsigned int i0 = i1; i0 < dups_count; i0 = i1) { - dup = dups[i0]; - if (nullptr == dup.m_vertex) + key = dups[i0]; + if (nullptr == key.m_vertex) { ON_SubDIncrementErrorCount(); return false; } for (i1 = i0 + 1; i1 < dups_count; i1++) { - int rc = VertexToDuplicate::CompareVertexId(&dup,dups+i1); + int rc = VertexToDuplicate::CompareVertexId(&key,dups+i1); if (rc < 0) break; if ( 0 != rc - || dup.m_vertex != dups[i1].m_vertex - || dup.m_mesh_V_index != dups[i1].m_mesh_V_index - || dup.m_mesh_V_index >= mesh_D_count0 + || key.m_vertex != dups[i1].m_vertex + || key.m_mesh_V_index != dups[i1].m_mesh_V_index + || key.m_mesh_V_index >= mesh_D_count0 ) { ON_SubDIncrementErrorCount(); @@ -1373,119 +1555,249 @@ static bool DuplicateVerticesAtCreases( if ( i1 == i0+1) continue; - const unsigned int mesh_V_index0 = dup.m_mesh_V_index; + const unsigned int mesh_V_index0 = key.m_mesh_V_index; const ON_3dPoint P = D[mesh_V_index0]; - dups_sub_array.SetArray(dups+i0,i1-i0,0); - ON_SubDSectorIterator sit; + VertexToDuplicate* vertex_dups = dups+i0; + const unsigned vertex_dups_count = i1 - i0; unsigned int sector_count = 0; - bool bDupError = false; - for (unsigned int i = i0; i < i1 && false == bDupError; i++) + // Set the sector id + for (unsigned int k = 0; k < vertex_dups_count; ++k) { - if (dups[i].m_mesh_V_index >= mesh_D_count0 || dups[i].m_mesh_F_index >= mesh_F_count) + if (vertex_dups[k].m_sector_id > 0 ) + continue; // this was in a previously found sector. + + ++sector_id; + ++sector_count; + + // When the priority is encoding creases, we need to divide the faces around this vertex + // into sets that are in the same sector. Since a dart vertex has only 1 sector + // but the crease edge at a dart needs to have duplicate vertices at both ends, + // darts get special case handling. + + if (nullptr == sit.Initialize(vertex_dups[k].m_face, 0, key.m_vertex)) { - if (sector_count > 0 - && ON_UNSET_UINT_INDEX == dups[i].m_mesh_V_index - && ON_UNSET_UINT_INDEX == dups[i].m_mesh_F_index - ) - { - // this dup[i] was part of a previously processed sector. - continue; - } - // error. terminate creation of dups. + // not fatal, but don't bother with the vertices in vertex_dups[]. ON_SubDIncrementErrorCount(); - bDupError = true; + sector_count = 0; + break; + } + if (nullptr == sit.IncrementToCrease(-1)) + { + // not fatal, but don't bother with the vertices in vertex_dups[]. + ON_SubDIncrementErrorCount(); + sector_count = 0; break; } - if (nullptr == sit.Initialize(dups[i].m_face, 0, dup.m_vertex)) - { - // error. terminate creation of dups. - ON_SubDIncrementErrorCount(); - bDupError = true; - break; - } - if ( nullptr == sit.IncrementToCrease(-1) ) - { - // error. terminate creation of dups. - ON_SubDIncrementErrorCount(); - bDupError = true; - break; - } - - if (dup.m_vertex->IsDart()) + if (0 == k && key.m_vertex->IsDart()) { + // darts have a single sector, but the dart vertex needs to be duplicated across the creased edge. const ON_SubDEdge* edge = sit.CurrentEdge(0); if (nullptr == edge || false == edge->IsCrease() || 2 != edge->m_face_count) { + // not fatal, but don't bother with the vertices in vertex_dups[]. ON_SubDIncrementErrorCount(); - bDupError = true; + sector_count = 0; break; } + + // we found the creased edge - duplicate the ON_Mesh vertices at this edge. for (unsigned int efi = 0; efi < 2; efi++) { - dup.m_face = edge->Face(efi); - dup.m_mesh_V_index = D.UnsignedCount(); - D.Append(P); - if (false == ChangeMeshFaceIndex(mesh_V_index0, mesh_F_count, mesh, dup, dups_sub_array)) + key.m_face = edge->Face(efi); + VertexToDuplicate* vertex_dup = Internal_FindMatchingVertexIdAndFaceId(&key, vertex_dups, vertex_dups_count); + if (nullptr == vertex_dup) { + // not fatal, but don't bother with the vertices in vertex_dups[]. ON_SubDIncrementErrorCount(); - bDupError = true; + sector_count = 0; + break; + } + // add a new vertex on either side of the dart's edge + vertex_dup->m_mesh_V_index = D.UnsignedCount(); + D.Append(P); + if ( false == Internal_UpdateMeshFaceVertexIndex(mesh, vertex_dup->m_mesh_F_index, mesh_F_count, mesh_V_index0, vertex_dup->m_mesh_V_index) ) + { + // not fatal, but don't bother with the vertices in vertex_dups[]. + ON_SubDIncrementErrorCount(); + sector_count = 0; break; } } - - sit.NextFace(ON_SubDSectorIterator::StopAt::AnyCrease); - } - - sector_count++; - if (sector_count > 1) - { - dup.m_mesh_V_index = D.UnsignedCount(); - D.Append(P); - } - else - { - dup.m_mesh_V_index = mesh_V_index0; - } - - for (dup.m_face = sit.CurrentFace(); nullptr != dup.m_face && false == bDupError; dup.m_face = sit.NextFace(ON_SubDSectorIterator::StopAt::AnyCrease)) - { - if (false == ChangeMeshFaceIndex(mesh_V_index0, mesh_F_count, mesh, dup, dups_sub_array)) + for (k = 0; k < vertex_dups_count; ++k) { + vertex_dups[k].m_sector_id = sector_id; + } + break; + } + + // assign the sector id + for (key.m_face = sit.CurrentFace(); nullptr != key.m_face; key.m_face = sit.NextFace(ON_SubDSectorIterator::StopAt::AnyCrease)) + { + VertexToDuplicate* vertex_dup = Internal_FindMatchingVertexIdAndFaceId(&key, vertex_dups, vertex_dups_count); + if (nullptr == vertex_dup) + { + // not fatal, but don't bother with the vertices in vertex_dups[]. ON_SubDIncrementErrorCount(); - bDupError = true; + sector_count = 0; break; } + vertex_dup->m_sector_id = sector_id; } - if (bDupError) + if (0 == sector_count) break; - } - dups_sub_array.SetCapacity(0); - if (bDupError) - return false; + + if (0 == sector_count) + continue; + + if (sector_count > 1) + { + // sort vertex_dups[] by sector id + ON_qsort(vertex_dups, vertex_dups_count, sizeof(vertex_dups[0]), (int(*)(const void*, const void*))VertexToDuplicate::CompareSectorIdAndFaceId); + } + + unsigned int k1 = 0; + for (unsigned int k0 = 0; k0 < vertex_dups_count; k0 = k1) + { + key = vertex_dups[k0]; + for (k1 = k0 + 1; k1 < vertex_dups_count; ++k1) + { + if (key.m_sector_id != vertex_dups[k1].m_sector_id) + break; + } + if (k0 > 0) + { + // make a new vertex for this sector; + key.m_mesh_V_index = D.UnsignedCount(); + D.Append(P); + + // update ON_Mesh faces in this sector to use new vertex + for (unsigned k = k0; k < k1; ++k) + Internal_UpdateMeshFaceVertexIndex(mesh, vertex_dups[k].m_mesh_F_index, mesh_F_count, mesh_V_index0, key.m_mesh_V_index); + } + } } + return true; } +//static const ON_2dPoint Internal_NgonFakeSurfaceParameter( +// const ON_SubDFace* face, +// const ON_2dPoint face_pack_rect_corners[4], +// const ON_2dPoint& center, +// unsigned fvi +//) +//{ +// for (;;) +// { +// if (nullptr == face || face->m_edge_count < 5) +// break; +// const ON_2dVector diag = face_pack_rect_corners[0] - center; +// const double r = 0.5 * ((fabs(diag.y) < fabs(diag.x)) ? fabs(diag.y) : fabs(diag.x)); +// const double a = (((double)fvi) / ((double)face->m_edge_count))*ON_2PI; +// return ON_2dPoint( center.x + r * cos(a), center.y + r*sin(a) ); +// } +// return ON_2dPoint::NanPoint; +//} + ON_Mesh* ON_SubD::GetControlNetMesh( - ON_Mesh* destination_mesh - ) const + ON_Mesh* destination_mesh, + ON_SubDGetControlNetMeshPriority priority +) const { if (destination_mesh) destination_mesh->Destroy(); + const ON_SubDimple* subdimple = SubDimple(); + if (nullptr == subdimple) + return nullptr; // SubD is empty - not an error + const ON_SubDLevel& level = ActiveLevel(); if (level.IsEmpty()) return ON_SUBD_RETURN_ERROR(nullptr); + if (level.m_vertex_count < 3) + return ON_SUBD_RETURN_ERROR(nullptr); + if (level.m_edge_count < 3) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( level.m_face_count < 1) + return ON_SUBD_RETURN_ERROR(nullptr); + + std::unique_ptr< ON_Mesh > up; + ON_Mesh* mesh = nullptr; + if (nullptr != destination_mesh) + { + mesh = destination_mesh; + *mesh = ON_Mesh::Empty; + } + else + { + mesh = new ON_Mesh(); + } + + bool bSuccess = false; + switch (priority) + { + case ON_SubDGetControlNetMeshPriority::Geometry: + { + unsigned int archive_id_partition[4] = {}; + bool bLevelLinkedListIncreasingId[3] = {}; + level.SetArchiveId(*subdimple, archive_id_partition, bLevelLinkedListIncreasingId); + if (archive_id_partition[1] - archive_id_partition[0] == level.m_vertex_count) + { + // Have to use idit because subd editing (deleting and then adding) can leave the level's linked lists + // with components in an order that is not increasing in id and it is critical that the next three for + // loops iterate the level's components in order of increasing id. + + // must iterate vertices in order of increasing id + ON_SubDLevelComponentIdIterator vit_by_id; + vit_by_id.Initialize(bLevelLinkedListIncreasingId[0], ON_SubDComponentPtr::Type::Vertex, *subdimple, level); + + // must iterate vertices in order of increasing id + ON_SubDLevelComponentIdIterator fit_by_id; + fit_by_id.Initialize(bLevelLinkedListIncreasingId[2], ON_SubDComponentPtr::Type::Face, *subdimple, level); + bSuccess = Internal_GetGeometryControlNetMesh(level, vit_by_id, fit_by_id, *mesh); + } + } + break; + + case ON_SubDGetControlNetMeshPriority::TextureCoordinates: + bSuccess = Internal_GetTextureCoordinatesGeometryControlNetMesh(level, *mesh); + break; + } + + if (false == bSuccess) + { + if (mesh != destination_mesh) + delete mesh; + mesh = nullptr; + } + else + { + mesh->UpdateSinglePrecisionVertices(); + if (ON_SubDGetControlNetMeshPriority::TextureCoordinates != priority) + { + mesh->ComputeFaceNormals(); + mesh->ComputeVertexNormals(); + } + mesh->BoundingBox(); + } + + return mesh; +} + + +bool ON_SubD::Internal_GetGeometryControlNetMesh( + const ON_SubDLevel& level, + ON_SubDLevelComponentIdIterator& vit_by_id, + ON_SubDLevelComponentIdIterator& fit_by_id, + ON_Mesh& mesh +) const +{ VertexToDuplicate dup; ON_SimpleArray dups_array; - const ON_SubDimple* subdimple = SubDimple(); - if ( nullptr == subdimple) - return nullptr; - const unsigned int subd_vertex_count = level.m_vertex_count; unsigned int mesh_ngon_count = 0; @@ -1506,47 +1818,25 @@ ON_Mesh* ON_SubD::GetControlNetMesh( max_ngon_Vcount = face->m_edge_count; } - if (subd_vertex_count < 4 || mesh_face_count < 1 ) - return ON_SUBD_RETURN_ERROR(nullptr); + if (subd_vertex_count < 3 || mesh_face_count < 1 ) + return ON_SUBD_RETURN_ERROR(false); - std::unique_ptr< ON_Mesh > up; - ON_Mesh* mesh = nullptr; - if (nullptr != destination_mesh) - mesh = destination_mesh; - else - { - up = std::make_unique< ON_Mesh >(); - mesh = up.get(); - } - - ON_3dPointArray& D = mesh->DoublePrecisionVertices(); - D.Reserve(subd_vertex_count+mesh_ngon_count); + const size_t D_initial_capacity = subd_vertex_count + mesh_ngon_count; + ON_3dPointArray& D = mesh.DoublePrecisionVertices(); + D.Reserve(D_initial_capacity); D.SetCount(0); + ON_SimpleArray mesh_VertexNeedsDuplicated(D_initial_capacity); - mesh->m_F.Reserve(mesh_face_count); - mesh->m_F.SetCount(0); + mesh.m_F.Reserve(mesh_face_count); + mesh.m_F.SetCount(0); ON_SimpleArray< ON_2udex > ngon_spans(mesh_ngon_count); bool rc = false; for (;;) { - - unsigned int archive_id_partition[4] = {}; - bool bLevelLinkedListIncreasingId[3] = {}; - level.SetArchiveId(*subdimple,archive_id_partition,bLevelLinkedListIncreasingId); - - if (archive_id_partition[1] - archive_id_partition[0] != subd_vertex_count) - break; - - // Have to use idit because subd editing (deleting and then adding) can leave the level's linked lists - // with components in an order that is not increasing in id and it is critical that the next three for - // loops iterate the level's components in order of increasing id. - ON_SubDLevelComponentIdIterator idit; - // must iterate vertices in order of increasing id - idit.Initialize(bLevelLinkedListIncreasingId[0], ON_SubDComponentPtr::Type::Vertex, *subdimple, level); - for (const ON_SubDVertex* vertex = idit.FirstVertex(); nullptr != vertex; vertex = idit.NextVertex()) + for (const ON_SubDVertex* vertex = vit_by_id.FirstVertex(); nullptr != vertex; vertex = vit_by_id.NextVertex()) { unsigned int vi = vertex->ArchiveId(); if (vi < 1 || vi > subd_vertex_count) @@ -1554,6 +1844,7 @@ ON_Mesh* ON_SubD::GetControlNetMesh( if (D.UnsignedCount()+1 != vi) break; D.AppendNew() = vertex->m_P; + mesh_VertexNeedsDuplicated.AppendNew() = VertexToDuplicate::NeedsDuplicated(vertex); } if (D.UnsignedCount() != subd_vertex_count) @@ -1563,16 +1854,18 @@ ON_Mesh* ON_SubD::GetControlNetMesh( unsigned int max_ngon_face_count = 0; mesh_face_count = 0; // must iterate faces in order of increasing id - idit.Initialize(bLevelLinkedListIncreasingId[2], ON_SubDComponentPtr::Type::Face, *subdimple, level); - for (const ON_SubDFace* face = idit.FirstFace(); nullptr != face; face = idit.NextFace()) + for (const ON_SubDFace* face = fit_by_id.FirstFace(); nullptr != face; face = fit_by_id.NextFace()) { ON_MeshFace meshf = {}; - + if (face->m_edge_count <= 4) { + // SubD quad face or 3-gon face gets a single ON_Mesh face if (face->m_edge_count < 3) continue; + const bool bQuad = 4 == face->m_edge_count; + for (unsigned short fvi = 0; fvi < face->m_edge_count; fvi++) { const ON_SubDVertex* vertex = face->Vertex(fvi); @@ -1583,11 +1876,11 @@ ON_Mesh* ON_SubD::GetControlNetMesh( break; } meshf.vi[fvi]--; - if (VertexToDuplicate::NeedsDuplicated(vertex)) + if (mesh_VertexNeedsDuplicated[meshf.vi[fvi]]) { dup.m_vertex = vertex; dup.m_face = face; - dup.m_mesh_F_index = mesh->m_F.UnsignedCount(); + dup.m_mesh_F_index = mesh.m_F.UnsignedCount(); dup.m_mesh_V_index = meshf.vi[fvi]; dups_array.Append(dup); } @@ -1596,21 +1889,22 @@ ON_Mesh* ON_SubD::GetControlNetMesh( continue; if ( 3 == face->m_edge_count) meshf.vi[3] = meshf.vi[2]; - mesh->m_F.Append(meshf); + mesh.m_F.Append(meshf); continue; } - else + else // face->m_edge_count >= 5 { + // SubD n-gon face with n >= 5 gets n ON_Mesh triangles grouped into ON_3dPoint center_point; if (false == face->GetSubdivisionPoint( center_point)) continue; - ON_2udex ngon_span = { mesh->m_F.UnsignedCount(), 0 }; + ON_2udex ngon_span = { mesh.m_F.UnsignedCount(), 0 }; const unsigned int dup_count0 = dups_array.UnsignedCount(); const unsigned int Dcount0 = D.UnsignedCount(); - const unsigned int Fcount0 = mesh->m_F.UnsignedCount(); + const unsigned int Fcount0 = mesh.m_F.UnsignedCount(); meshf.vi[2] = (int)Dcount0; meshf.vi[3] = meshf.vi[2]; @@ -1620,15 +1914,16 @@ ON_Mesh* ON_SubD::GetControlNetMesh( continue; meshf.vi[1]--; - if (VertexToDuplicate::NeedsDuplicated(vertex)) + if (mesh_VertexNeedsDuplicated[meshf.vi[1]]) { dup.m_vertex = vertex; dup.m_face = face; - dup.m_mesh_F_index = mesh->m_F.UnsignedCount(); + dup.m_mesh_F_index = mesh.m_F.UnsignedCount(); dup.m_mesh_V_index = meshf.vi[1]; dups_array.Append(dup); } + mesh_VertexNeedsDuplicated.Append(false); D.Append(center_point); for (unsigned short fvi = 1; fvi <= face->m_edge_count; fvi++) @@ -1643,28 +1938,31 @@ ON_Mesh* ON_SubD::GetControlNetMesh( } meshf.vi[1]--; - if (VertexToDuplicate::NeedsDuplicated(vertex)) + if (fvi < face->m_edge_count) { - dup.m_vertex = vertex; - dup.m_face = face; - dup.m_mesh_F_index = mesh->m_F.UnsignedCount(); - dup.m_mesh_V_index = meshf.vi[1]; - dups_array.Append(dup); + if (mesh_VertexNeedsDuplicated[meshf.vi[1]]) + { + dup.m_vertex = vertex; + dup.m_face = face; + dup.m_mesh_F_index = mesh.m_F.UnsignedCount(); + dup.m_mesh_V_index = meshf.vi[1]; + dups_array.Append(dup); + } } - mesh->m_F.Append(meshf); + mesh.m_F.Append(meshf); } - ngon_span.j = mesh->m_F.UnsignedCount(); + ngon_span.j = mesh.m_F.UnsignedCount(); unsigned int ngon_face_count = ngon_span.j - ngon_span.i; - if (-1 == meshf.vi[0] || ngon_face_count < 3) + if ( -1 == meshf.vi[0] || ngon_face_count != face->EdgeCount() ) { D.SetCount(Dcount0); - mesh->m_F.SetCount(Fcount0); + mesh.m_F.SetCount(Fcount0); dups_array.SetCount(dup_count0); continue; } - ngon_span.j = mesh->m_F.UnsignedCount(); + ngon_span.j = mesh.m_F.UnsignedCount(); if (ngon_face_count >= 3) { ngon_spans.Append(ngon_span); @@ -1674,7 +1972,7 @@ ON_Mesh* ON_SubD::GetControlNetMesh( } } - if (mesh->m_F.UnsignedCount() <= 0) + if (mesh.m_F.UnsignedCount() <= 0) break; rc = true; @@ -1683,16 +1981,12 @@ ON_Mesh* ON_SubD::GetControlNetMesh( level.ClearArchiveId(); if (false == rc ) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(false); - if (D.UnsignedCount() < 3 || mesh->m_F.UnsignedCount() < 1) - return ON_SUBD_RETURN_ERROR(nullptr); + if (D.UnsignedCount() < 3 || mesh.m_F.UnsignedCount() < 1) + return ON_SUBD_RETURN_ERROR(false); - DuplicateVerticesAtCreases(mesh,D,dups_array); - mesh->UpdateSinglePrecisionVertices(); - mesh->ComputeFaceNormals(); - mesh->ComputeVertexNormals(); - mesh->BoundingBox(); + Internal_DuplicateVertices( mesh, D, dups_array); // group all mesh faces that came from the same level zero subd face into an ngon. if (ngon_spans.UnsignedCount() > 0 && max_ngon_Vcount >= 3) @@ -1708,44 +2002,349 @@ ON_Mesh* ON_SubD::GetControlNetMesh( continue; ngon_fi[0] = ngon_span.i; - ngon_fi[0] = (unsigned int)mesh->m_F[ngon_fi[0]].vi[0]; + ngon_fi[0] = (unsigned int)mesh.m_F[ngon_fi[0]].vi[0]; unsigned int ngon_Vcount = 0; for (unsigned int i = ngon_span.i; i < ngon_span.j; i++) { ngon_fi[ngon_Vcount] = i; - ngon_vi[ngon_Vcount] = (unsigned int)(mesh->m_F[i].vi[0]); + ngon_vi[ngon_Vcount] = (unsigned int)(mesh.m_F[i].vi[0]); ngon_Vcount++; } - mesh->AddNgon(ngon_Vcount, ngon_vi, ngon_Vcount, ngon_fi ); + mesh.AddNgon(ngon_Vcount, ngon_vi, ngon_Vcount, ngon_fi ); } } - up.release(); - return mesh; + return true; } +bool ON_SubD::Internal_GetTextureCoordinatesGeometryControlNetMesh( + const ON_SubDLevel& level, + ON_Mesh& mesh +) const +{ + const bool bSetMeshT = 2 * this->TexturePointsAreSet() > this->FaceCount(); // more than half the faces have texture points. + + bool bSubdivide = false; // required if any SubD faces are not quads. + unsigned mesh_4gon_count = 0; + unsigned mesh_quad_count = 0; + for (const ON_SubDFace* f = level.m_face[0]; nullptr != f; f = f->m_next_face) + { + const unsigned n = f->EdgeCount(); + if (n < 3) + continue; + + if (false == bSubdivide) + { + if (4 == n) + { + // subdivision is not required and we have another quad face + ++mesh_quad_count; + continue; + } + // first non-quad SubD face - switch to subdivided case + bSubdivide = true; + + // each of the previously counted quad faces will generate 9 mesh vertices, 4 mesh faces, and a single ON_MeshNgon. + mesh_4gon_count = mesh_quad_count; + mesh_quad_count *= 4; + } + + // In the subdivided case, each SubD face is represented by n quads in the ON_Mesh. + if ( 4 == n) + ++mesh_4gon_count; + mesh_quad_count += n; + } + + if (mesh_quad_count < 1) + return ON_SUBD_RETURN_ERROR(false); + + mesh.m_F.Reserve(mesh_quad_count); + mesh.m_F.SetCount(0); + mesh.m_FN.Reserve(mesh_quad_count); + mesh.m_FN.SetCount(0); + + const unsigned mesh_vertex_count = 4 * (mesh_quad_count - 4* mesh_4gon_count) + 9 * mesh_4gon_count; + ON_3dPointArray& D = mesh.DoublePrecisionVertices(); + D.Reserve(mesh_vertex_count); + D.SetCount(0); + mesh.m_N.Reserve(mesh_vertex_count); + mesh.m_N.SetCount(0); + mesh.m_S.Reserve(mesh_vertex_count); + mesh.m_S.SetCount(0); + if (bSetMeshT) + mesh.m_T.Reserve(mesh_vertex_count); + mesh.m_T.SetCount(0); + + mesh.m_F.Reserve(mesh_quad_count); + mesh.m_F.SetCount(0); + mesh.m_FN.Reserve(mesh_quad_count); + mesh.m_FN.SetCount(0); + + ON_MeshFace mesh_f; + ON_2dPoint face_pack_rect_corners[4]; + ON_3dPoint quadP[4]; + if (bSubdivide) + { + ON_3dPoint centerT = ON_3dPoint::NanPoint; + + ON_2dVector ngon_sub_pack_rect_size = ON_2dVector::NanVector; + ON_2dVector ngon_sub_pack_rect_delta = ON_2dVector::NanVector; + ON_2dPoint quadS[4] = { ON_2dPoint::NanPoint, ON_2dPoint::NanPoint, ON_2dPoint::NanPoint, ON_2dPoint::NanPoint }; + ON_3dPoint quadT[4] = { ON_3dPoint::NanPoint, ON_3dPoint::NanPoint, ON_3dPoint::NanPoint, ON_3dPoint::NanPoint }; + ON_3dPoint faceT[2] = { ON_3dPoint ::NanPoint, ON_3dPoint::NanPoint }; + + + ON_SimpleArray P(64); + for (const ON_SubDFace* f = level.m_face[0]; nullptr != f; f = f->m_next_face) + { + const unsigned n = f->m_edge_count; + if (n < 3) + continue; + + P.SetCount(0); + for (unsigned i = 0; i < n; ++i) + { + const ON_SubDVertex* v = f->Vertex(i); + if (nullptr == v) + break; + const ON_3dPoint C = v->ControlNetPoint(); + if (false == C.IsValid()) + break; + P.Append(v->ControlNetPoint()); + } + if (n != P.UnsignedCount()) + continue; + + quadP[0] = f->ControlNetCenterPoint(); + if (false == quadP[0].IsValid()) + continue; + + const ON_3fVector N(f->ControlNetCenterNormal()); + + f->GetFacePackRectCorners(false, face_pack_rect_corners); + const ON_2dVector face_pack_rect_size = f->PackRectSize(); + + if (bSetMeshT) + { + faceT[0] = f->TexturePoint(n - 1); + faceT[1] = f->TexturePoint(0); + quadT[0] = f->TextureCenterPoint(); + } + + + if (4 == n) + { + // An ON_SubDFace quad becomes an ON_Mesh ngon made from 4 ON_Mesh quads. + + // center point + mesh_f.vi[0] = D.UnsignedCount(); + D.Append(quadP[0]); + mesh.m_N.Append(N); + mesh.m_S.Append(f->PackRectOrigin() + 0.5 * f->PackRectSize()); + if (bSetMeshT) + mesh.m_T.Append(ON_2fPoint(quadT[0])); + + ON_MeshNgon* four_gon = mesh.AllocateNgon(8,4); + four_gon->m_vi[0] = D.UnsignedCount(); + four_gon->m_vi[1] = four_gon->m_vi[0] + 1; + four_gon->m_vi[2] = four_gon->m_vi[0] + 2; + four_gon->m_vi[3] = four_gon->m_vi[0] + 3; + four_gon->m_vi[4] = four_gon->m_vi[0] + 4; + four_gon->m_vi[5] = four_gon->m_vi[0] + 5; + four_gon->m_vi[6] = four_gon->m_vi[0] + 6; + four_gon->m_vi[7] = four_gon->m_vi[0] + 7; + + four_gon->m_fi[0] = mesh.m_F.UnsignedCount(); + four_gon->m_fi[1] = four_gon->m_fi[0] + 1; + four_gon->m_fi[2] = four_gon->m_fi[0] + 2; + four_gon->m_fi[3] = four_gon->m_fi[0] + 3; + + // add 8 boundary vertices + for (unsigned i = 0; i < 4; ++i) + { + D.Append(P[i]); + D.Append(ON_3dPoint::Midpoint(P[i], P[(i + 1) % 4])); + mesh.m_N.Append(N); + mesh.m_N.Append(N); + mesh.m_S.Append(face_pack_rect_corners[i]); + mesh.m_S.Append(ON_2dPoint::Midpoint(face_pack_rect_corners[i], face_pack_rect_corners[(i+1)%4])); + if (bSetMeshT) + { + faceT[0] = faceT[1]; + faceT[1] = f->TexturePoint((i + 1) % 4); + mesh.m_T.Append(ON_2fPoint(faceT[0])); + mesh.m_T.Append(ON_2fPoint(ON_3dPoint::Midpoint(faceT[0], faceT[1]))); + } + } + + // add 4 ON_Mesh quads that make up the ON_SubDFace quad + mesh_f.vi[3] = four_gon->m_vi[7]; + for (unsigned i = 0; i < 4; ++i) + { + mesh_f.vi[1] = mesh_f.vi[3]; + mesh_f.vi[2] = four_gon->m_vi[2 * i]; + mesh_f.vi[3] = mesh_f.vi[2] + 1; + mesh.m_F.Append(mesh_f); + mesh.m_FN.Append(N); + } + + // add an ON_MeshNgon that represents the ON_SubDFace quad + mesh.AddNgon(four_gon); + } + else + { + quadP[3] = ON_3dPoint::Midpoint(P[n - 1], P[0]); + if (bSetMeshT) + quadT[3] = ON_3dPoint::Midpoint(faceT[0], faceT[1]); + + + const ON_2udex ngon_grid_size + = (n >= 5) + ? ON_SubDFace::GetNgonSubPackRectSizeAndDelta(n, face_pack_rect_size, ngon_sub_pack_rect_size, ngon_sub_pack_rect_delta) + : ON_2udex::Zero; + + // an ON_Mesh n-gon is not possible because the fake packed surface parameters are not continuous across the ON_SubD face. + for (unsigned i = 0; i < n; ++i) + { + if (3 == n) + { + ON_SubDMeshFragment::Get3gonFaceFragmentPackRectCorners(false, face_pack_rect_corners, i, false, quadS); + } + else + { + ON_SubDMeshFragment::GetNgonFaceFragmentPackRectCorners( + n, + i, + false, + face_pack_rect_corners, + face_pack_rect_size, + ngon_grid_size, + ngon_sub_pack_rect_size, + ngon_sub_pack_rect_delta, + quadS + ); + } + + quadP[1] = quadP[3]; + quadP[2] = P[i]; + quadP[3] = ON_3dPoint::Midpoint(P[i], P[(i + 1) % n]); + if (bSetMeshT) + { + faceT[0] = faceT[1]; + faceT[1] = f->TexturePoint((i + 1) % n); + quadT[1] = quadT[3]; + quadT[2] = faceT[0]; + quadT[3] = ON_3dPoint::Midpoint(faceT[0], faceT[1]); + } + + mesh_f.vi[0] = D.UnsignedCount(); + mesh_f.vi[1] = mesh_f.vi[0] + 1; + mesh_f.vi[2] = mesh_f.vi[1] + 1; + mesh_f.vi[3] = mesh_f.vi[2] + 1; + + for (unsigned j = 0; j < 4U; ++j) + { + D.Append(quadP[j]); + mesh.m_N.Append(N); + mesh.m_S.Append(quadS[j]); + if (bSetMeshT) + mesh.m_T.Append(ON_2fPoint(quadT[j])); + } + mesh.m_F.Append(mesh_f); + mesh.m_FN.Append(N); + } + } + } + } + else + { + // All SubD faces are quads + for (const ON_SubDFace* f = level.m_face[0]; nullptr != f; f = f->m_next_face) + { + if (4 != f->m_edge_count) + continue; + quadP[3].x = ON_DBL_QNAN; + for (unsigned i = 0; i < 4; ++i) + { + const ON_SubDVertex* v = f->Vertex(i); + if (nullptr == v) + break; + quadP[i] = v->ControlNetPoint(); + if (false == quadP[i].IsValid()) + break; + } + if (false == quadP[3].IsValid()) + continue; + const ON_3fVector N(f->ControlNetCenterNormal()); + mesh_f.vi[0] = D.UnsignedCount(); + mesh_f.vi[1] = mesh_f.vi[0] + 1; + mesh_f.vi[2] = mesh_f.vi[1] + 1; + mesh_f.vi[3] = mesh_f.vi[2] + 1; + mesh.m_F.Append(mesh_f); + mesh.m_FN.Append(N); + for (unsigned i = 0; i < 4U; ++i) + { + D.Append(quadP[i]); + mesh.m_N.Append(N); + mesh.m_S.Append(f->PackRectCorner(false, i)); + if (bSetMeshT) + mesh.m_T.Append(ON_2fPoint(f->TexturePoint(i))); + } + } + } + + + ON_MappingTag mapping_tag = this->TextureMappingTag(false); + + if (mesh.m_S.UnsignedCount() != D.UnsignedCount()) + { + mesh.m_S.Destroy(); + if (ON_TextureMapping::TYPE::srfp_mapping == mapping_tag.m_mapping_type) + mapping_tag = ON_MappingTag::Unset; + } + else + { + // set fake surface mapping information + mesh.m_srf_domain[0] = ON_Interval::ZeroToOne; + mesh.m_srf_domain[1] = ON_Interval::ZeroToOne; + mesh.m_srf_scale[0] = 0.0; + mesh.m_srf_scale[1] = 0.0; + mesh.m_packed_tex_domain[0] = ON_Interval::ZeroToOne; + mesh.m_packed_tex_domain[1] = ON_Interval::ZeroToOne; + mesh.m_packed_tex_rotate = false; + + if ( + false == bSetMeshT + && (false == mapping_tag.IsSet() || mapping_tag.IsDefaultSurfaceParameterMapping()) + ) + { + const int count = mesh.m_S.Count(); + mesh.m_T.Reserve(count); + mesh.m_T.SetCount(0); + for (int i = 0; i < count; ++i) + mesh.m_T.Append(ON_2fPoint(mesh.m_S[i])); + } + } + + if (bSetMeshT) + { + if (mesh.m_T.UnsignedCount() != D.UnsignedCount()) + mesh.m_T.Destroy(); + } + mesh.m_Ttag = mapping_tag; + + return true; +} + + void ON_SubD::ClearEvaluationCache() const { const ON_SubDLevel* level = ActiveLevelConstPointer(); if (nullptr != level) { - const_cast(this)->ChangeContentSerialNumberForExperts(false); + const_cast(this)->ChangeGeometryContentSerialNumberForExperts(false); level->ClearEvaluationCache(); } } - -void ON_SubD::ClearNeighborhoodEvaluationCache(const ON_SubDVertex * vertex, bool bTagChanged) const -{ - const ON_SubDLevel* level = ActiveLevelConstPointer(); - - if (nullptr != level) - { - const_cast(this)->ChangeContentSerialNumberForExperts(false); - level->ClearNeighborhoodEvaluationCache(vertex, bTagChanged); - } -} - -//////////////////////////////////////////////////////////////////////////// - diff --git a/opennurbs_subd_ref.cpp b/opennurbs_subd_ref.cpp index eee2eb5f..260a10c2 100644 --- a/opennurbs_subd_ref.cpp +++ b/opennurbs_subd_ref.cpp @@ -589,19 +589,19 @@ bool ON_SubDComponentRefList::Internal_UpdateCount(const ON_SubDComponentRef& r, break; switch (v->m_vertex_tag) { - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: m_subd_vertex_smooth_count += i; rc = true; break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: m_subd_vertex_crease_count += i; rc = true; break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: m_subd_vertex_corner_count += i; rc = true; break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: m_subd_vertex_dart_count += i; rc = true; break; @@ -616,12 +616,12 @@ bool ON_SubDComponentRefList::Internal_UpdateCount(const ON_SubDComponentRef& r, break; switch (e->m_edge_tag) { - case ON_SubD::EdgeTag::Smooth: - case ON_SubD::EdgeTag::SmoothX: + case ON_SubDEdgeTag::Smooth: + case ON_SubDEdgeTag::SmoothX: m_subd_edge_smooth_count += i; rc = true; break; - case ON_SubD::EdgeTag::Crease: + case ON_SubDEdgeTag::Crease: m_subd_edge_crease_count += i; rc = true; break; @@ -839,21 +839,21 @@ int ON_SubDComponentRefList::VertexCount() const + m_subd_vertex_corner_count; } -int ON_SubDComponentRefList::VertexCount(ON_SubD::VertexTag vertex_tag) const +int ON_SubDComponentRefList::VertexCount(ON_SubDVertexTag vertex_tag) const { int c = 0; switch (vertex_tag) { - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: c = m_subd_vertex_smooth_count; break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: c = m_subd_vertex_crease_count; break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: c = m_subd_vertex_corner_count; break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: c = m_subd_vertex_dart_count; break; } @@ -866,17 +866,17 @@ int ON_SubDComponentRefList::EdgeCount() const return m_subd_edge_crease_count + m_subd_edge_smooth_count; } -int ON_SubDComponentRefList::EdgeCount(ON_SubD::EdgeTag edge_tag) const +int ON_SubDComponentRefList::EdgeCount(ON_SubDEdgeTag edge_tag) const { int c = 0; switch (edge_tag) { - case ON_SubD::EdgeTag::Unset: + case ON_SubDEdgeTag::Unset: break; - case ON_SubD::EdgeTag::Smooth: + case ON_SubDEdgeTag::Smooth: c = m_subd_edge_smooth_count; break; - case ON_SubD::EdgeTag::Crease: + case ON_SubDEdgeTag::Crease: c = m_subd_edge_crease_count; break; } diff --git a/opennurbs_subd_ring.cpp b/opennurbs_subd_ring.cpp index 92093250..1da7b5d8 100644 --- a/opennurbs_subd_ring.cpp +++ b/opennurbs_subd_ring.cpp @@ -104,10 +104,10 @@ unsigned int ON_SubD::GetQuadSectorPointRing( if (0 == pass) { - if (ON_SubD::EdgeTag::SmoothX == edge->m_edge_tag) + if (ON_SubDEdgeTag::SmoothX == edge->m_edge_tag) break; // need to use subdivision point in 2nd pass - if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag - || ON_SubD::EdgeTag::Crease == edge->m_edge_tag + if (ON_SubDVertexTag::Smooth == vertex->m_vertex_tag + || ON_SubDEdgeTag::Crease == edge->m_edge_tag || 0.5 == edge->m_sector_coefficient[eptr] ) { @@ -232,14 +232,14 @@ bool ON_SubD::ComponentRingIsValid( if ( vertex->m_face_count < F || nullptr == vertex->m_faces) return ON_SUBD_RETURN_ERROR(false); - const ON_SubD::EdgeTag edge0_tag - = (F+1 == N || (F == N && ON_SubD::VertexTag::Dart == vertex->m_vertex_tag)) - ? ON_SubD::EdgeTag::Crease - : ON_SubD::EdgeTag::Smooth; - const ON_SubD::EdgeTag edge1_tag + const ON_SubDEdgeTag edge0_tag + = (F+1 == N || (F == N && ON_SubDVertexTag::Dart == vertex->m_vertex_tag)) + ? ON_SubDEdgeTag::Crease + : ON_SubDEdgeTag::Smooth; + const ON_SubDEdgeTag edge1_tag = (F+1 == N) - ? ON_SubD::EdgeTag::Crease - : ON_SubD::EdgeTag::Smooth; + ? ON_SubDEdgeTag::Crease + : ON_SubDEdgeTag::Smooth; unsigned int component_ring_index = 1; for (unsigned int i = 0; i < N; i++, component_ring_index++) @@ -254,7 +254,7 @@ bool ON_SubD::ComponentRingIsValid( { if (edge0_tag != edge->m_edge_tag) { - if ( ON_SubD::EdgeTag::Smooth != edge0_tag || ON_SubD::EdgeTag::SmoothX != edge->m_edge_tag ) + if ( ON_SubDEdgeTag::Smooth != edge0_tag || ON_SubDEdgeTag::SmoothX != edge->m_edge_tag ) return ON_SUBD_RETURN_ERROR(false); } } @@ -262,10 +262,10 @@ bool ON_SubD::ComponentRingIsValid( { if (edge1_tag != edge->m_edge_tag) { - if ( ON_SubD::EdgeTag::Smooth != edge1_tag || ON_SubD::EdgeTag::SmoothX != edge->m_edge_tag ) + if ( ON_SubDEdgeTag::Smooth != edge1_tag || ON_SubDEdgeTag::SmoothX != edge->m_edge_tag ) return ON_SUBD_RETURN_ERROR(false); } - if ( ON_SubD::EdgeTag::Crease == edge1_tag) + if ( ON_SubDEdgeTag::Crease == edge1_tag) continue; } else @@ -466,9 +466,9 @@ static double Subdivide_CenterVertexSectorWeight( const ON_SubDVertex* vertex0 ) { - if ( ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) + if ( ON_SubDEdgeTag::Crease == edge0->m_edge_tag) return ON_SubDSectorType::IgnoredSectorCoefficient; - if (ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag || ON_SubD::EdgeTag::SmoothX == edge0->m_edge_tag) + if (ON_SubDEdgeTag::Smooth == edge0->m_edge_tag || ON_SubDEdgeTag::SmoothX == edge0->m_edge_tag) { if (vertex0 == edge0->m_vertex[0]) return edge0->m_sector_coefficient[0]; @@ -521,7 +521,7 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( return ON_SUBD_RETURN_ERROR(nullptr); } - const ON_SubD::EdgeTag edge0_tag = (F+1 == N) ? ON_SubD::EdgeTag::Crease : ON_SubD::EdgeTag::Smooth; + const ON_SubDEdgeTag edge0_tag = (F+1 == N) ? ON_SubDEdgeTag::Crease : ON_SubDEdgeTag::Smooth; //const unsigned int face_edge_count = 4; const unsigned int K = 3; @@ -531,7 +531,7 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( return ON_SUBD_RETURN_ERROR(nullptr); edges += element_stride; - if (ON_SubD::EdgeTag::Smooth == edge0_tag) + if (ON_SubDEdgeTag::Smooth == edge0_tag) { if (false == edge0->IsSmooth() ) return ON_SUBD_RETURN_ERROR(nullptr); @@ -567,9 +567,9 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( // at_crease weight is used when the cooresponding vertex is a crease. // Otherwise, fsh.AllocateEdge() ignores at_crease_weight. - ON_SubD::EdgeTag edge1_tag = (ON_SubD::EdgeTag::SmoothX == edge0_tag) ? ON_SubD::EdgeTag::Smooth : edge0_tag; + ON_SubDEdgeTag edge1_tag = (ON_SubDEdgeTag::SmoothX == edge0_tag) ? ON_SubDEdgeTag::Smooth : edge0_tag; const double at_crease_weight - = ON_SubD::EdgeTag::Crease == edge1_tag + = ON_SubDEdgeTag::Crease == edge1_tag ? ON_SubDSectorType::CreaseSectorCoefficient(5-K) : ON_SubDSectorType::IgnoredSectorCoefficient; ON_SubDEdgePtr edge1 = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0,vertex0), vertex1, ON_SubDSectorType::IgnoredSectorCoefficient ); @@ -580,7 +580,7 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( v1[1] = vertex1; e1[0] = edge1; f1epts[0] = e1[0]; - edge1_tag = ON_SubD::EdgeTag::Smooth; + edge1_tag = ON_SubDEdgeTag::Smooth; for (unsigned int i = 1; i < N; i++, edges += element_stride, faces += element_stride) { @@ -595,7 +595,7 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( edge1_tag = edge0_tag; if ( edge1_tag != edge0->m_edge_tag) return ON_SUBD_RETURN_ERROR(nullptr); - if (ON_SubD::EdgeTag::Smooth == edge1_tag) + if (ON_SubDEdgeTag::Smooth == edge1_tag) { v1[K] = vertex1; e1[K] = edge1; @@ -659,7 +659,7 @@ unsigned int ON_SubD::GetSectorComponentRing( if ( nullptr == vertex || vertex->m_edge_count < 2 || vertex->m_face_count < 1) return ON_SUBD_RETURN_ERROR(0); - const ON_SubD::VertexTag center_vertex_tag = vertex->m_vertex_tag; + const ON_SubDVertexTag center_vertex_tag = vertex->m_vertex_tag; ON_SubDSectorIterator localsit(sit); const bool bCreases = (nullptr != localsit.IncrementToCrease(-1)); @@ -677,7 +677,7 @@ unsigned int ON_SubD::GetSectorComponentRing( if ( nullptr == ring_vertex0 || vertex == ring_vertex0) return ON_SUBD_RETURN_ERROR(0); - if (bCreases && ON_SubD::EdgeTag::Crease != edge0->m_edge_tag) + if (bCreases && ON_SubDEdgeTag::Crease != edge0->m_edge_tag) return ON_SUBD_RETURN_ERROR(0); unsigned int component_ring_count = 0; @@ -702,20 +702,20 @@ unsigned int ON_SubD::GetSectorComponentRing( // back to start? if (edge == edge0 && ring_vertex == ring_vertex0) { - if (ON_SubD::VertexTag::Smooth == center_vertex_tag) + if (ON_SubDVertexTag::Smooth == center_vertex_tag) { - if (face == face0 && ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag) + if (face == face0 && ON_SubDEdgeTag::Smooth == edge0->m_edge_tag) return component_ring_count; // back to start smooth case. } - if (ON_SubD::VertexTag::Dart == center_vertex_tag) + if (ON_SubDVertexTag::Dart == center_vertex_tag) { - if (nullptr == face && ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) + if (nullptr == face && ON_SubDEdgeTag::Crease == edge0->m_edge_tag) return component_ring_count; // back to start dart case. } - if (ON_SubD::VertexTag::Corner == center_vertex_tag) + if (ON_SubDVertexTag::Corner == center_vertex_tag) { // occurs in nonmanifold cases like the one in RH-49843 - if (nullptr == face && ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) + if (nullptr == face && ON_SubDEdgeTag::Crease == edge0->m_edge_tag) return component_ring_count; // back to start corner case. } } @@ -730,7 +730,7 @@ unsigned int ON_SubD::GetSectorComponentRing( if (nullptr == face) { - if (bCreases && ON_SubD::EdgeTag::Crease == edge->m_edge_tag) + if (bCreases && ON_SubDEdgeTag::Crease == edge->m_edge_tag) return component_ring_count; return ON_SUBD_RETURN_ERROR(0); } diff --git a/opennurbs_subd_sector.cpp b/opennurbs_subd_sector.cpp index 59dbdce8..d7f190c2 100644 --- a/opennurbs_subd_sector.cpp +++ b/opennurbs_subd_sector.cpp @@ -40,7 +40,7 @@ public: static ON_SubDSectorType Create( ON_SubD::SubDType subd_type, - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_edge_count, double sector_angle_radians ); @@ -72,7 +72,7 @@ public: ); ON_SubD::SubDType m_subd_type = ON_SubD::SubDType::Unset; - ON_SubD::VertexTag m_vertex_tag = ON_SubD::VertexTag::Unset; + ON_SubDVertexTag m_vertex_tag = ON_SubDVertexTag::Unset; private: unsigned char m_reserved1 = 0; @@ -109,28 +109,28 @@ bool ON_SubDSectorType::IsValid() const switch (m_vertex_tag) { - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle)) return ON_SUBD_RETURN_ERROR(false); if (!(m_sector_coefficient == ON_SubDSectorType::IgnoredSectorCoefficient)) return ON_SUBD_RETURN_ERROR(false); break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle)) return ON_SUBD_RETURN_ERROR(false); if (!(m_sector_coefficient == ON_SubDSectorType::CreaseSectorCoefficient(m_sector_face_count))) return ON_SUBD_RETURN_ERROR(false); break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: if (!(m_corner_sector_angle_radians > 0.0 && m_corner_sector_angle_radians < ON_2PI)) return ON_SUBD_RETURN_ERROR(false); if (!(m_sector_coefficient == ON_SubDSectorType::CornerSectorCoefficient(m_sector_face_count,m_corner_sector_angle_radians))) return ON_SUBD_RETURN_ERROR(false); break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle)) return ON_SUBD_RETURN_ERROR(false); if (!(m_sector_coefficient == ON_SubDSectorType::DartSectorCoefficient(m_sector_face_count))) @@ -145,7 +145,7 @@ bool ON_SubDSectorType::IsValid() const return true; } -ON_SubD::VertexTag ON_SubDSectorType::VertexTag() const +ON_SubDVertexTag ON_SubDSectorType::VertexTag() const { return m_vertex_tag; } @@ -159,46 +159,46 @@ unsigned int ON_SubDSectorType::FacetEdgeCount() const double ON_SubDSectorType::CornerSectorAngleRadians() const { return - (ON_SubD::VertexTag::Corner == m_vertex_tag) + (ON_SubDVertexTag::Corner == m_vertex_tag) ? m_corner_sector_angle_radians : ON_SubDSectorType::ErrorCornerSectorAngle; } unsigned int ON_SubDSectorType::CornerSectorAngleIndex() const { - return (m_vertex_tag == ON_SubD::VertexTag::Corner) ? m_corner_sector_angle_index : ON_UNSET_UINT_INDEX; + return (m_vertex_tag == ON_SubDVertexTag::Corner) ? m_corner_sector_angle_index : ON_UNSET_UINT_INDEX; } bool ON_SubDSectorType::IsSmoothSector() const { - return (m_vertex_tag == ON_SubD::VertexTag::Smooth); + return (m_vertex_tag == ON_SubDVertexTag::Smooth); } bool ON_SubDSectorType::IsDartSector() const { - return (m_vertex_tag == ON_SubD::VertexTag::Dart); + return (m_vertex_tag == ON_SubDVertexTag::Dart); } bool ON_SubDSectorType::IsCreaseSector() const { - return (m_vertex_tag == ON_SubD::VertexTag::Crease); + return (m_vertex_tag == ON_SubDVertexTag::Crease); } bool ON_SubDSectorType::IsCornerSector() const { - return (m_vertex_tag == ON_SubD::VertexTag::Corner && m_corner_sector_angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex); + return (m_vertex_tag == ON_SubDVertexTag::Corner && m_corner_sector_angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex); } bool ON_SubDSectorType::IsConvexCornerSector() const { - return (m_vertex_tag == ON_SubD::VertexTag::Corner && 2*m_corner_sector_angle_index >= ON_SubDSectorType::MaximumCornerAngleIndex); + return (m_vertex_tag == ON_SubDVertexTag::Corner && 2*m_corner_sector_angle_index >= ON_SubDSectorType::MaximumCornerAngleIndex); } bool ON_SubDSectorType::IsConcaveCornerSector() const { - return (m_vertex_tag == ON_SubD::VertexTag::Corner && 2*m_corner_sector_angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex); + return (m_vertex_tag == ON_SubDVertexTag::Corner && 2*m_corner_sector_angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex); } @@ -208,19 +208,19 @@ unsigned int ON_SubDSectorType::EdgeCount() const { switch (m_vertex_tag) { - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: return m_sector_face_count; break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: return m_sector_face_count + 1; break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: return m_sector_face_count + 1; break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: return m_sector_face_count; break; @@ -367,7 +367,7 @@ double ON_SubDSectorType::CornerSectorThetaFromCornerAngle( double corner_sector_angle_radians ) { - if (sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(ON_SubD::VertexTag::Corner) + if (sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(ON_SubDVertexTag::Corner) && sector_face_count <= ON_SubDVertex::MaximumFaceCount ) { @@ -607,7 +607,7 @@ double ON_SubDSectorType::CornerSectorCoefficient( { corner_sector_angle_radians = ON_SubDSectorType::ClampCornerSectorAngleRadians(corner_sector_angle_radians); if (ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians) - && sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(ON_SubD::VertexTag::Corner) + && sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(ON_SubDVertexTag::Corner) && sector_face_count <= ON_SubDVertex::MaximumFaceCount ) { @@ -643,9 +643,9 @@ int ON_SubDSectorType::Compare(const ON_SubDSectorType* a, const ON_SubDSectorTy if (0 != rc) { // bias towards valid tags - if ( ON_SubD::VertexTag::Unset == b->m_vertex_tag) + if ( ON_SubDVertexTag::Unset == b->m_vertex_tag) rc = -1; - else if ( ON_SubD::VertexTag::Unset == a->m_vertex_tag) + else if ( ON_SubDVertexTag::Unset == a->m_vertex_tag) rc = 1; break; } @@ -659,7 +659,7 @@ int ON_SubDSectorType::Compare(const ON_SubDSectorType* a, const ON_SubDSectorTy rc = 1; break; } - if (ON_SubD::VertexTag::Corner == a->m_vertex_tag) + if (ON_SubDVertexTag::Corner == a->m_vertex_tag) { rc = CompareUnsinged(a->m_corner_sector_angle_index, b->m_corner_sector_angle_index); if (0 != rc) @@ -678,7 +678,7 @@ void ON_SubDSectorType::SetHash() unsigned int hash = 0; hash = ON_CRC32(hash,sizeof(m_vertex_tag),&m_vertex_tag); hash = ON_CRC32(hash,sizeof(m_sector_face_count),&m_sector_face_count); - if ( ON_SubD::VertexTag::Corner == m_vertex_tag) + if ( ON_SubDVertexTag::Corner == m_vertex_tag) hash = ON_CRC32(hash,sizeof(m_corner_sector_angle_index),&m_corner_sector_angle_index); if ( 0 == hash ) hash = 1; @@ -691,7 +691,7 @@ unsigned int ON_SubDSectorType::SectorTypeHash() const } static bool ON_SubDSectorType_IsValidFaceCount( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_face_count ) { @@ -699,7 +699,7 @@ static bool ON_SubDSectorType_IsValidFaceCount( } static bool ON_SubDSectorType_IsValidFaceCountForCreate( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_face_count ) { @@ -710,7 +710,7 @@ ON_SubDSectorType ON_SubDSectorType::CreateSmoothSectorType( unsigned int sector_face_count ) { - const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Smooth; + const ON_SubDVertexTag vertex_tag = ON_SubDVertexTag::Smooth; if (ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count)) { ON_SubDSectorType st; @@ -737,7 +737,7 @@ ON_SubDSectorType ON_SubDSectorType::CreateCreaseSectorType( unsigned int sector_face_count ) { - const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Crease; + const ON_SubDVertexTag vertex_tag = ON_SubDVertexTag::Crease; if (ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count)) { ON_SubDSectorType st; @@ -765,7 +765,7 @@ ON_SubDSectorType ON_SubDSectorType::CreateDartSectorType( unsigned int sector_face_count ) { - const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Dart; + const ON_SubDVertexTag vertex_tag = ON_SubDVertexTag::Dart; if ( ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count) ) { ON_SubDSectorType st; @@ -811,7 +811,7 @@ ON_SubDSectorType ON_SubDSectorType::CreateCornerSectorType( || ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians) ) { - const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Corner; + const ON_SubDVertexTag vertex_tag = ON_SubDVertexTag::Corner; if (ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count)) { unsigned int corner_sector_angle_index @@ -846,26 +846,26 @@ ON_SubDSectorType ON_SubDSectorType::CreateCornerSectorType( ON_SubDSectorType ON_SubDSectorType::Create( - ON_SubD::VertexTag vertex_tag, + ON_SubDVertexTag vertex_tag, unsigned int sector_face_count, double corner_sector_angle_radians ) { - if (ON_SubD::VertexTag::Unset == vertex_tag && 0 == sector_face_count) + if (ON_SubDVertexTag::Unset == vertex_tag && 0 == sector_face_count) return ON_SubDSectorType::Empty; switch (vertex_tag) { - case ON_SubD::VertexTag::Smooth: + case ON_SubDVertexTag::Smooth: return ON_SubDSectorType::CreateSmoothSectorType(sector_face_count); break; - case ON_SubD::VertexTag::Crease: + case ON_SubDVertexTag::Crease: return ON_SubDSectorType::CreateCreaseSectorType(sector_face_count); break; - case ON_SubD::VertexTag::Corner: + case ON_SubDVertexTag::Corner: return ON_SubDSectorType::CreateCornerSectorType(sector_face_count,corner_sector_angle_radians); break; - case ON_SubD::VertexTag::Dart: + case ON_SubDVertexTag::Dart: return ON_SubDSectorType::CreateDartSectorType(sector_face_count); break; } @@ -884,11 +884,11 @@ ON_SubDSectorType ON_SubDSectorType::Create( ON_SubDSectorIterator local_sit(sit); - const ON_SubD::VertexTag vertex_tag = center_vertex->m_vertex_tag; + const ON_SubDVertexTag vertex_tag = center_vertex->m_vertex_tag; const ON_SubDFace* face0; ON_SubDEdgePtr edge0ptr = ON_SubDEdgePtr::Null; - if (ON_SubD::VertexTag::Smooth == vertex_tag) + if (ON_SubDVertexTag::Smooth == vertex_tag) { face0 = local_sit.CurrentFace(); if (nullptr == face0 ) @@ -912,7 +912,7 @@ ON_SubDSectorType ON_SubDSectorType::Create( if (face0 == local_sit.NextFace(ON_SubDSectorIterator::StopAt::AnyCrease)) { double corner_sector_angle_radians - = (ON_SubD::VertexTag::Corner == vertex_tag) + = (ON_SubDVertexTag::Corner == vertex_tag) ? ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(edge0ptr,local_sit.CurrentEdgePtr(0)) : 0.0; return ON_SubDSectorType::Create(vertex_tag,sector_face_count,corner_sector_angle_radians); diff --git a/opennurbs_subd_texture.cpp b/opennurbs_subd_texture.cpp index 9c936fc6..c3f03633 100644 --- a/opennurbs_subd_texture.cpp +++ b/opennurbs_subd_texture.cpp @@ -26,49 +26,202 @@ //////////////////////////////////////////////////////////////// */ - - -ON_SubDTextureDomainType ON_SubD::TextureDomainTypeFromUnsigned -( - unsigned int texture_domain_type_as_unsigned -) +unsigned char ON_SubD::ObsoleteTextureDomainTypeFromTextureCoordinateType(ON_SubDTextureCoordinateType texture_coordinate_type) { - switch (texture_domain_type_as_unsigned) +/* + OBSOLETE enum class ON_SubDTextureDomainType : unsigned char { - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Unset); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::PerFace); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Packed); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Zero); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Nan); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Custom); + /// + /// Texture domains are not set. + /// + Unset = 0, + + /// + /// Each face texture domain is a unique rectangle of normlized texture space. + /// + PerFace = 1, + + /// + /// Each face texture domain is a unique rectangle of normlized texture space. + /// When possible, faces are partitioned into quad groups. Adjactent members + /// of the group are assigned adjacent rectangles in texture space. + /// + Packed = 2, + + /// + /// All face texture domain values are zero. + /// + Zero = 3, + + /// + /// All face texture domain values are nan. + /// + Nan = 4, + + /// + /// All face texture domain is (0,1)x(0,1). + /// + UnitSquare = 5, + + /// + /// Code outside of opennurbs set the values. No other information is available. + /// + Custom = 7, + }; + */ + unsigned char obsolete_texture_domain_type = 0; + switch (texture_coordinate_type) + { + case ON_SubDTextureCoordinateType::Unset: + obsolete_texture_domain_type = 0; // OBSOLETE ON_SubDTextureDomainType::Unset = 0 + break; + case ON_SubDTextureCoordinateType::Unpacked: + obsolete_texture_domain_type = 1; // OBSOLETE ON_SubDTextureDomainType::PerFace = 1 + break; + case ON_SubDTextureCoordinateType::Packed: + obsolete_texture_domain_type = 2; // OBSOLETE ON_SubDTextureDomainType::Packed = 2 + break; + case ON_SubDTextureCoordinateType::Zero: + obsolete_texture_domain_type = 3; // OBSOLETE ON_SubDTextureDomainType::Zero = 3 + break; + case ON_SubDTextureCoordinateType::Nan: + obsolete_texture_domain_type = 4; // OBSOLETE ON_SubDTextureDomainType::Nan = 4 + break; + case ON_SubDTextureCoordinateType::FromFaceTexturePoints: + obsolete_texture_domain_type = 6; // no corresponding OBSOLETE ON_SubDTextureDomainType + break; + case ON_SubDTextureCoordinateType::FromMapping: + obsolete_texture_domain_type = 7; // OBSOLETE ON_SubDTextureDomainType::Custom = 7 + break; + default: + obsolete_texture_domain_type = 0; + break; } - return ON_SUBD_RETURN_ERROR(ON_SubDTextureDomainType::Unset); + return obsolete_texture_domain_type; } -const ON_wString ON_SubD::TextureDomainTypeToString( - ON_SubDTextureDomainType texture_domain_type + +ON_SubDTextureCoordinateType ON_SubD::TextureCoordinateTypeFromObsoleteTextureDomainType( + unsigned int obsolete_texture_domain_type_as_unsigned +) +{ + /* + OBSOLETE enum class ON_SubDTextureDomainType : unsigned char + { + /// + /// Texture domains are not set. + /// + Unset = 0, + + /// + /// Each face texture domain is a unique rectangle of normlized texture space. + /// + PerFace = 1, + + /// + /// Each face texture domain is a unique rectangle of normlized texture space. + /// When possible, faces are partitioned into quad groups. Adjactent members + /// of the group are assigned adjacent rectangles in texture space. + /// + Packed = 2, + + /// + /// All face texture domain values are zero. + /// + Zero = 3, + + /// + /// All face texture domain values are nan. + /// + Nan = 4, + + /// + /// All face texture domain is (0,1)x(0,1). + /// + UnitSquare = 5, + + /// + /// Code outside of opennurbs set the values. No other information is available. + /// + Custom = 7, + }; + */ + + switch (obsolete_texture_domain_type_as_unsigned) + { + case 0: // OBSOLETE ON_SubDTextureDomainType::Unset = 0 + return ON_SubDTextureCoordinateType::Unset; + break; + case 1: // OBSOLETE ON_SubDTextureDomainType::PerFace = 1 + return ON_SubDTextureCoordinateType::Unpacked; + break; + case 2: // OBSOLETE ON_SubDTextureDomainType::Packed = 2 + return ON_SubDTextureCoordinateType::Packed; + break; + case 3: // OBSOLETE ON_SubDTextureDomainType:: Zero = 3 + return ON_SubDTextureCoordinateType::Zero; + break; + case 4: // OBSOLETE ON_SubDTextureDomainType::Nan = 4 + return ON_SubDTextureCoordinateType::Nan; + break; + case 5: // OBSOLETE ON_SubDTextureDomainType::UnitSquare = 5 + return ON_SubDTextureCoordinateType::Unset; + break; + case 6: // no corresponding OBSOLETE ON_SubDTextureDomainType + return ON_SubDTextureCoordinateType::FromFaceTexturePoints; + break; + case 7: // OBSOLETE ON_SubDTextureDomainType::Custom = 7 + return ON_SubDTextureCoordinateType::FromMapping; + break; + } + return ON_SubDTextureCoordinateType::Unset; +} + +ON_SubDTextureCoordinateType ON_SubD::TextureCoordinateTypeFromUnsigned +( + unsigned int texture_coordinate_type_as_unsigned +) +{ + switch (texture_coordinate_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::Unpacked); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::Packed); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::Zero); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::Nan); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::FromFaceTexturePoints); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::FromMapping); + } + return ON_SUBD_RETURN_ERROR(ON_SubDTextureCoordinateType::Unset); +} + +const ON_wString ON_SubD::TextureCoordinateTypeToString( + ON_SubDTextureCoordinateType texture_coordinate_type ) { const wchar_t* s; - switch (texture_domain_type) + switch (texture_coordinate_type) { - case ON_SubDTextureDomainType::Unset: + case ON_SubDTextureCoordinateType::Unset: s = L"Unset"; break; - case ON_SubDTextureDomainType::PerFace: - s = L"PerFace"; + case ON_SubDTextureCoordinateType::Unpacked: + s = L"Unpacked"; break; - case ON_SubDTextureDomainType::Packed: + case ON_SubDTextureCoordinateType::Packed: s = L"Packed"; break; - case ON_SubDTextureDomainType::Zero: + case ON_SubDTextureCoordinateType::Zero: s = L"Zero"; break; - case ON_SubDTextureDomainType::Nan: + case ON_SubDTextureCoordinateType::Nan: s = L"Nan"; break; - case ON_SubDTextureDomainType::Custom: - s = L"Custom"; + case ON_SubDTextureCoordinateType::FromFaceTexturePoints: + s = L"FromFaceTexturePoints"; + break; + case ON_SubDTextureCoordinateType::FromMapping: + s = L"FromMapping"; break; default: s = nullptr; @@ -76,7 +229,7 @@ const ON_wString ON_SubD::TextureDomainTypeToString( } return (nullptr != s && 0 != s[0]) ? ON_wString(s) - : ON_wString::FormatToString(L"ON_SubDTextureDomainType(%u)", ((unsigned)static_cast(texture_domain_type))); + : ON_wString::FormatToString(L"ON_SubDTextureCoordinateType(%u)", ((unsigned)static_cast(texture_coordinate_type))); } ////////////////////////////////////////////////////////////////////////////// @@ -88,17 +241,39 @@ const ON_wString ON_SubD::TextureDomainTypeToString( void ON_SubDimple::SetTextureMappingTag(const ON_MappingTag &mapping_tag) const { - this->m_texture_mapping_tag = mapping_tag; + if (0 != ON_MappingTag::CompareAll(this->m_texture_mapping_tag, mapping_tag)) + { + this->m_texture_mapping_tag = mapping_tag; + ChangeRenderContentSerialNumber(); + } } -const ON_MappingTag ON_SubDimple::TextureMappingTag() const +const ON_MappingTag ON_SubDimple::TextureMappingTag(bool bIgnoreTextureCoordinateType) const { - return m_texture_mapping_tag; + return + (bIgnoreTextureCoordinateType || ON_SubDTextureCoordinateType::FromMapping == TextureCoordinateType()) + ? m_texture_mapping_tag + : ON_MappingTag::Unset; } #pragma endregion #endif +const ON_MappingTag ON_SubDimple::FragmentColorsMappingTag() const +{ + return m_fragment_colors_mapping_tag; +} + +void ON_SubDimple::SetFragmentColorsMappingTag(const ON_MappingTag& mapping_tag) const +{ + if (0 != ON_MappingTag::CompareAll(this->m_fragment_colors_mapping_tag, mapping_tag)) + { + this->m_fragment_colors_mapping_tag = mapping_tag; + ChangeRenderContentSerialNumber(); + } +} + + ////////////////////////////////////////////////////////////////////////////// // // ON_SubD - texture coordinates @@ -106,60 +281,69 @@ const ON_MappingTag ON_SubDimple::TextureMappingTag() const #if 1 #pragma region ON_SubD - texture coordinates -ON_SubDTextureDomainType ON_SubD::TextureDomainType() const +ON_SubDTextureCoordinateType ON_SubD::TextureCoordinateType() const { const ON_SubDimple* subdimple = SubDimple(); - return (nullptr != subdimple) ? subdimple->TextureDomainType() : ON_SubDTextureDomainType::Unset; + return (nullptr != subdimple) ? subdimple->TextureCoordinateType() : ON_SubDTextureCoordinateType::Unset; } -ON_SubDTextureDomainType ON_SubDimple::TextureDomainType() const +ON_SubDTextureCoordinateType ON_SubDimple::TextureCoordinateType() const { - return m_texture_domain_type; + return m_texture_coordinate_type; } -void ON_SubDimple::SetTextureDomainType( - ON_SubDTextureDomainType texture_domain_type + +unsigned int ON_SubD::TexturePointsAreSet() const +{ + unsigned int count = 0; + ON_SubDFaceIterator fit(*this); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + if (f->TexturePointsAreSet()) + ++count; + } + return count; +} + +unsigned int ON_SubD::ClearTexturePoints() const +{ + const bool bUpdateFragments = ON_SubDTextureCoordinateType::FromFaceTexturePoints == this->TextureCoordinateType(); + const ON_SubDimple* subdimple = SubDimple(); + const unsigned count = (nullptr != subdimple) ? subdimple->ClearTexturePoints() : 0; + if (bUpdateFragments) + this->Internal_SetFragmentTextureCoordinatesWithoutMapping(); + return count; +} + +unsigned int ON_SubDimple::ClearTexturePoints() const +{ + bool bSetPacked = ON_SubDTextureCoordinateType::FromFaceTexturePoints == this->TextureCoordinateType(); + if ( bSetPacked) + this->SetTextureCoordinateType(ON_SubDTextureCoordinateType::Unset); + const ON_SubDLevel& level = this->ActiveLevel(); + unsigned int count = 0; + for (const ON_SubDFace* f = level.m_face[0]; nullptr != f; f = f->m_next_face) + { + if (f->TexturePointsAreSet()) + ++count; + this->ReturnFaceTexturePoints(f); + if (bSetPacked && false == f->PackRectIsSet()) + bSetPacked = false; + } + if (bSetPacked) + this->SetTextureCoordinateType(ON_SubDTextureCoordinateType::Packed); + return count; +} + +void ON_SubDimple::SetTextureCoordinateType( + ON_SubDTextureCoordinateType texture_coordinate_type ) const { - m_texture_domain_type = texture_domain_type; -} - -unsigned int ON_SubD::TextureImageSuggestedMinimumSize() const -{ - const ON_2udex grid_size = ON_SubD::TextureDomainGridSize(FaceCount(), 0.0, 0.0); - return ON_SubD::TextureImageSuggestedMinimumSize(grid_size); -} - -unsigned int ON_SubD::TextureImageSuggestedMinimumSize( - ON_2udex grid_size -) -{ - const unsigned min_pixels = 16; - const unsigned max_pixels = 4098; - unsigned pixels = grid_size.i >= grid_size.j ? grid_size.i : grid_size.j; - if (0 == pixels) - return ON_SUBD_RETURN_ERROR(1); - - if (pixels < max_pixels / min_pixels) - pixels *= min_pixels; - else - pixels = max_pixels; - - if (pixels < min_pixels) + if (m_texture_coordinate_type != texture_coordinate_type) { - pixels = min_pixels; - const unsigned smallgrid_min_pixels = 128; - if (grid_size.i > 0 && grid_size.j > 0) - { - while (grid_size.i * grid_size.j * pixels < smallgrid_min_pixels) - pixels *= 2; - } + ChangeRenderContentSerialNumber(); + m_texture_coordinate_type = texture_coordinate_type; } - - if (pixels > max_pixels) - pixels = max_pixels; - - return pixels; } const ON_2udex ON_SubD::TextureDomainGridSize( @@ -180,271 +364,370 @@ const ON_2udex ON_SubD::TextureDomainGridSize( return (image_height > image_width) ? ON_2udex(j, i) : ON_2udex(i, j); } -const ON_2udex ON_SubD::GetTextureDomainAndDelta( - unsigned quad_count, - double image_width, - double image_height, - ON_2dVector& quad_texture_domain, - ON_2dVector& quad_texture_delta +double ON_SubDFace::PackRectGapInPixels( + double pack_rect_distance_in_pixels +) +{ + // In order to produce repeatable rendering results, + // it is critical that TextureImageSuggestedMinimumSize be a + // constant and context independent value. + + // As is clearly stated in the definition of ON_SubD::TextureImageSuggestedMinimumSize, + // this code assumes ON_SubD::TextureImageSuggestedMinimumSize is a power of 2 and + // ON_SubD::TextureImageSuggestedMinimumSize >= 512. + // As a result the following code assumes min_distance_in_pixels >= 8.0 and an integer value; + const double min_distance_in_pixels = ((double)(ON_SubD::TextureImageSuggestedMinimumSize))/64.0; + + if (pack_rect_distance_in_pixels >= min_distance_in_pixels) + { + if (pack_rect_distance_in_pixels <= 2.0 * min_distance_in_pixels) + return 1.0; + if (pack_rect_distance_in_pixels <= 4.0 * min_distance_in_pixels) + return 2.0; + return 3.0; + } + return 0.0; +} + +const ON_2udex ON_SubDFace::GetNgonSubPackRectSizeAndDelta( + unsigned int ngon_edge_count, + ON_2dVector ngon_face_pack_rect_size, + ON_2dVector& ngon_sub_pack_rect_size, + ON_2dVector& ngon_sub_pack_rect_delta ) { ON_2udex grid_size(0, 0); - quad_texture_domain = ON_2dVector::ZeroVector; - quad_texture_delta = ON_2dVector::ZeroVector; + ngon_sub_pack_rect_size = ON_2dVector::ZeroVector; + ngon_sub_pack_rect_delta = ON_2dVector::ZeroVector; - const bool bImageSizeKnown - = image_width > 0.0 && image_width < ON_UNSET_POSITIVE_VALUE - && image_width > 0.0 && image_width < ON_UNSET_POSITIVE_VALUE; - if (false == bImageSizeKnown) + // This function determines how to partition ngon_face_pack_rect_size into + // ngon_edge_count sub quads that the ngon will use when it is rendered. + + // Validate input parameters + if (ngon_edge_count < 5 || ngon_edge_count > ON_SubDFace::MaximumEdgeCount) { - // unknown image size - assume it's square. - grid_size = ON_SubD::TextureDomainGridSize(quad_count, 0.0, 0.0); + ON_SUBD_ERROR("Invalid ngon_edge_count parameter. Value must be >= 5 and <= ON_SubDFace::MaximumEdgeCount."); + return grid_size; + } - // use grid size to set minimum sugggested square image size - image_width = image_height = (double)ON_SubD::TextureImageSuggestedMinimumSize(grid_size); + if (false == (ngon_face_pack_rect_size.x > 0.0 && ngon_face_pack_rect_size.x < ON_UNSET_POSITIVE_VALUE)) + { + ON_SUBD_ERROR("Invalid ngon_face_pack_rect_size.x parameter. Value must be > 0."); + return grid_size; + } + + if (false == (ngon_face_pack_rect_size.y > 0.0 && ngon_face_pack_rect_size.y < ON_UNSET_POSITIVE_VALUE)) + { + ON_SUBD_ERROR("Invalid ngon_face_pack_rect_size.y parameter. Value must be > 0."); + return grid_size; + } + + // The ngon's face pack rect will be partitioned into grid_size.i X grid_size.j sub pack rects. + const int pack_rect_aspectdex + = (ngon_face_pack_rect_size.x > ngon_face_pack_rect_size.y) + ? 1 + : ((ngon_face_pack_rect_size.x < ngon_face_pack_rect_size.y) ? -1 : 0) + ; + grid_size = ON_SubD::TextureDomainGridSize(ngon_edge_count, 0.0, 0.0); + if (grid_size.i * grid_size.j < ngon_edge_count) + { + ON_SUBD_ERROR("Failed to get a valid grid_size."); + return ON_2udex(0, 0); } else { - // use image size to help set grid size - grid_size = ON_SubD::TextureDomainGridSize(quad_count, image_width, image_height); + const int grid_aspectdex + = (grid_size.i > grid_size.j) + ? 1 + : ((grid_size.i < grid_size.j) ? -1 : 0) + ; + if (grid_aspectdex * pack_rect_aspectdex < 0) + grid_size = ON_2udex(grid_size.j, grid_size.i); } - if (0 == grid_size.i || 0 == grid_size.j) - return ON_SUBD_RETURN_ERROR(grid_size); + // Estimate image pixels assigned to this ngon's pack rect. + // In order to produce repeatable rendering results, + // it is critical that TextureImageSuggestedMinimumSize be a + // constant and context independent value. + // + // The portion of the image assgned to the ngon face pack rect + // is rect_image_width X rect_image_height pixels. + // All variables ending in "size" have pixels as units. + const double subd_image_size = ON_SubD::TextureImageSuggestedMinimumSize; + const double rect_image_size[2] = { + subd_image_size * ngon_face_pack_rect_size.x, + subd_image_size * ngon_face_pack_rect_size.y + }; + double subrect_image_size[2] = { + rect_image_size[0] / ((double)(grid_size.i)), + rect_image_size[1] / ((double)(grid_size.j)) + }; + double subrect_gap_size[2] = { + ON_SubDFace::PackRectGapInPixels(subrect_image_size[0]), + ON_SubDFace::PackRectGapInPixels(subrect_image_size[1]) + }; - const double image_size[2] = { image_width ,image_height }; + // As is clearly stated in the definition of ON_SubD::TextureImageSuggestedMinimumSize, + // this code assumes ON_SubD::TextureImageSuggestedMinimumSize is a power of 2 and + // ON_SubD::TextureImageSuggestedMinimumSize >= 512. + // As a result the following code assumes min_distance_in_pixels >= 8.0 and an integer value; + const double min_distance_in_pixels = ((double)(ON_SubD::TextureImageSuggestedMinimumSize)) / 64.0; - // quad_image_size = size of image for each quad - const ON_2dVector quad_image_size(image_size[0] / ((double)grid_size.i), image_size[1] / ((double)grid_size.j)); - - if (1U == quad_count) + bool bApplyGap[2] = { false,false }; + for (int i = 0; i < 2; ++i) { - // quad uses entire image_delta x image_delta region - quad_texture_delta.Set(1.0, 1.0); - quad_texture_domain = quad_texture_delta; - } - else - { - for (int k = 0; k < 2; ++k) - { - if (quad_image_size[k] >= 16.0) - { - // An image with image_delta.x X image_delta.y pixels - // doesn't have to share a pixel with its neighbors. - quad_texture_delta[k] = floor(quad_image_size[k]) / image_size[k]; - quad_texture_domain[k] = (floor(quad_image_size[k]) - 1.0) / image_size[k]; - } - else - { - // Not as good, but a quad is getting at most 16x16 pixels in from a texture image - // with image_delta x image_delta pixels, so the texture will look bad anyway. - quad_texture_delta[k] = (quad_image_size[k] / image_size[k]); - quad_texture_domain[k] = (15.0 / 16.0)*quad_texture_delta[k]; - } - } + bApplyGap[i] = (subrect_gap_size[i] >= 1.0 && 8.0 * subrect_gap_size[i] <= subrect_image_size[i]); + if (bApplyGap[i]) + subrect_image_size[i] = floor(subrect_image_size[i]) - subrect_gap_size[i]; } + // Dividing by subd_image_size converts pixel values to normalized dimensionless pack subrect values. + ngon_sub_pack_rect_size = ON_2dVector( + bApplyGap[0] ? (subrect_image_size[0] / subd_image_size) : (1.0 - ON_EPSILON) * (ngon_face_pack_rect_size.x / ((double)(grid_size.i))), + bApplyGap[1] ? (subrect_image_size[1] / subd_image_size) : (1.0 - ON_EPSILON) * (ngon_face_pack_rect_size.y / ((double)(grid_size.j))) + ); + ngon_sub_pack_rect_delta = ON_2dVector( + ngon_sub_pack_rect_size.x + (bApplyGap[0] ? (subrect_gap_size[0] / subd_image_size) : 0.0), + ngon_sub_pack_rect_size.y + (bApplyGap[1] ? (subrect_gap_size[1] / subd_image_size) : 0.0) + ); + return grid_size; } -bool ON_SubD::SetTextureDomains( - ON_SubDTextureDomainType texture_domain_type, - bool bLazy, - bool bSetFragmentTextureCoordinates +void ON_SubD::SetTextureCoordinateType( + ON_SubDTextureCoordinateType texture_coordinate_type ) const { const ON_SubDimple* subdimple = SubDimple(); - if (nullptr == subdimple) - return (ON_SubDTextureDomainType::Unset == texture_domain_type); + if (nullptr != subdimple) + subdimple->SetTextureCoordinateType(texture_coordinate_type); +} - if (ON_SubDTextureDomainType::Unset == texture_domain_type || ON_SubDTextureDomainType::Custom == texture_domain_type) - bLazy = true; - if (bLazy && texture_domain_type == subdimple->TextureDomainType()) - return true; - - if (0 == FaceCount()) +bool ON_SubD::TextureMappingRequired() const +{ + if (ON_SubDTextureCoordinateType::FromMapping == TextureCoordinateType()) { - subdimple->SetTextureDomainType(ON_SubDTextureDomainType::Unset); - return (ON_SubDTextureDomainType::Unset == texture_domain_type); + const ON_MappingTag texture_mapping_tag = TextureMappingTag(false); + if ( + ON_TextureMapping::TYPE::no_mapping != texture_mapping_tag.m_mapping_type + && ON_TextureMapping::TYPE::srfp_mapping != texture_mapping_tag.m_mapping_type + && texture_mapping_tag.IsSet() + ) + return true; // need an explicit ON_TextureMapping class to evaluate texture coordinates. + } + return false; +} + +ON_SubDTextureCoordinateType ON_SubD::Internal_BestChoiceTextureCoordinateType( + const class ON_TextureMapping& available_mapping +) const +{ + ON_SubDTextureCoordinateType subd_texture_coordinate_type = TextureCoordinateType(); + + if (ON_SubDTextureCoordinateType::FromMapping == subd_texture_coordinate_type) + { + const ON_MappingTag texture_mapping_tag = TextureMappingTag(false); + switch (texture_mapping_tag.m_mapping_type) + { + case ON_TextureMapping::TYPE::no_mapping: + subd_texture_coordinate_type = ON_SubDTextureCoordinateType::Unset; + break; + case ON_TextureMapping::TYPE::srfp_mapping: + subd_texture_coordinate_type = ON_SubDTextureCoordinateType::Packed; // <- breakpoint here when debugging texture issues + break; + default: + { + const bool bMappingAvailable + = ON_TextureMapping::TYPE::srfp_mapping != texture_mapping_tag.m_mapping_type + && texture_mapping_tag.IsSet() + && texture_mapping_tag.m_mapping_type == available_mapping.m_type + && texture_mapping_tag.m_mapping_crc == available_mapping.MappingCRC() + ; + if (false == bMappingAvailable) + subd_texture_coordinate_type = ON_SubDTextureCoordinateType::Unset; // <- breakpoint here when debugging texture issues + } + break; + } } - bool rc = false; + if (ON_SubDTextureCoordinateType::Unset == subd_texture_coordinate_type) + subd_texture_coordinate_type = ON_SubD::DefaultTextureCoordinateType; - switch (texture_domain_type) + return subd_texture_coordinate_type; +} + +bool ON_SubD::Internal_SetFragmentTextureCoordinatesWithoutMapping() const +{ + + // face_corners[] initialized to unpacked. + ON_SubDTextureCoordinateType subd_texture_coordinate_type = Internal_BestChoiceTextureCoordinateType(ON_TextureMapping::Unset); + + bool bPacked = false; + bool bUnpacked = false; + bool bConstant = false; + bool bFromFaceTexturePoints = false; + ON_3dPoint face_corners[4]; + + switch (subd_texture_coordinate_type) { - case ON_SubDTextureDomainType::Unset: - rc = true; - break; - - case ON_SubDTextureDomainType::PerFace: - case ON_SubDTextureDomainType::Packed: - case ON_SubDTextureDomainType::Zero: - case ON_SubDTextureDomainType::Nan: - { - ON_SubDFaceIterator fit(*this); - rc = ON_SubD::SetTextureDomains(fit, texture_domain_type, bSetFragmentTextureCoordinates); - } - break; - - case ON_SubDTextureDomainType::Custom: - rc = false; - break; - default: - rc = false; + // no break here + case ON_SubDTextureCoordinateType::FromMapping: + ON_SUBD_ERROR("Bug - these cases should have been eliminated in code above."); + subd_texture_coordinate_type = ON_SubDTextureCoordinateType::Packed; + // no break here + + case ON_SubDTextureCoordinateType::Packed: + bPacked = true; + // If packing information is present, these values get updated for each face. + face_corners[0] = ON_3dPoint::NanPoint; + face_corners[1] = ON_3dPoint::NanPoint; + face_corners[2] = ON_3dPoint::NanPoint; + face_corners[3] = ON_3dPoint::NanPoint; + break; + + case ON_SubDTextureCoordinateType::Unpacked: + bUnpacked = true; + // fragment grid order for unit texture corners + face_corners[0] = ON_3dPoint(0, 0, 0); + face_corners[1] = ON_3dPoint(1, 0, 0); + face_corners[2] = ON_3dPoint(0, 1, 0); + face_corners[3] = ON_3dPoint(1, 1, 0); + break; + + case ON_SubDTextureCoordinateType::Zero: + bConstant = true; + face_corners[0] = ON_3dPoint::Origin; + face_corners[1] = ON_3dPoint::Origin; + face_corners[2] = ON_3dPoint::Origin; + face_corners[3] = ON_3dPoint::Origin; + break; + + case ON_SubDTextureCoordinateType::Nan: + bConstant = true; + face_corners[0] = ON_3dPoint::NanPoint; + face_corners[1] = ON_3dPoint::NanPoint; + face_corners[2] = ON_3dPoint::NanPoint; + face_corners[3] = ON_3dPoint::NanPoint; + break; + + case ON_SubDTextureCoordinateType::FromFaceTexturePoints: + bFromFaceTexturePoints = true; + face_corners[0] = ON_3dPoint::NanPoint; + face_corners[1] = ON_3dPoint::NanPoint; + face_corners[2] = ON_3dPoint::NanPoint; + face_corners[3] = ON_3dPoint::NanPoint; break; } - if (rc && nullptr != subdimple) - subdimple->SetTextureDomainType(texture_domain_type); - - return rc; -} - -bool ON_SubD::SetTextureDomains( - ON_SubDFaceIterator& fit, - ON_SubDTextureDomainType texture_domain_type, - bool bSetFragmentTextureCoordinates -) -{ - if (ON_SubDTextureDomainType::Custom == texture_domain_type) - return ON_SUBD_RETURN_ERROR(false); - - bool bFragmentsExist = false; - const double default_tc = (ON_SubDTextureDomainType::Zero == texture_domain_type) ? 0.0 : ON_DBL_QNAN; - const ON_2dPoint default_origin(default_tc, default_tc); - const ON_2dVector default_delta(default_tc, default_tc); - unsigned face_count = 0; - for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + ON_SHA1_Hash hash = ON_SHA1_Hash::EmptyContentHash; + if (bPacked || bUnpacked || bConstant || bFromFaceTexturePoints) { - f->SetTextureDomain(texture_domain_type, default_origin, default_delta, 0); - if (f->m_edge_count >= 3) - ++face_count; - if (f->MeshFragments()) - bFragmentsExist = true; - } + ON_SubDFaceIterator fit(*this); - bool rc - = ON_SubDTextureDomainType::Unset == texture_domain_type - || ON_SubDTextureDomainType::Zero == texture_domain_type - || ON_SubDTextureDomainType::Nan == texture_domain_type; - if (ON_SubDTextureDomainType::Packed == texture_domain_type) - { - texture_domain_type = ON_SubDTextureDomainType::PerFace; - } + // DELETE // // estimate face image size - used to add empty space around ngon-subdfragments + // DELETE // const unsigned int face_image_size = ON_SubD::TextureImageSuggestedMinimumSize(ON_SubD::TextureDomainGridSize(fit.FaceCount(), 0, 0)); - if (ON_SubDTextureDomainType::PerFace == texture_domain_type) - { - const ON_2udex grid_size = ON_SubD::TextureDomainGridSize(face_count, 0.0, 0.0); - const double image_width = ON_SubD::TextureImageSuggestedMinimumSize(grid_size); - const double image_height = image_width; - ON_2dVector face_texture_domain = ON_2dVector::NanVector; - ON_2dVector face_texture_delta = ON_2dVector::NanVector; - ON_SubD::GetTextureDomainAndDelta( - face_count, - image_height, - image_width, - face_texture_domain, - face_texture_delta - ); - - ON_2udex k(0, 0); - unsigned int face_dex = 0; - for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace(), ++face_dex) + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { - if (0 != face_dex) + if (f->m_edge_count < 3) + continue; + const ON_SubDMeshFragment* fragment = f->MeshFragments(); + if (nullptr == fragment) + continue; + + const unsigned short frag_count = (4 == f->m_edge_count) ? 1 : f->m_edge_count; + if (bUnpacked || bConstant) { - k.i = (++k.i % grid_size.i); - if (0 == k.i) - ++k.j; + for (unsigned short frag_dex = 0; frag_dex < frag_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment) + fragment->SetTextureCoordinateCornersForExperts(true, face_corners, true); + continue; } - const ON_2dPoint face_texture_origin(k.i*face_texture_delta.x, k.j*face_texture_delta.y); - f->SetTextureDomain(texture_domain_type, face_texture_origin, face_texture_domain, 0); - if (false == bFragmentsExist && nullptr != f->MeshFragments()) - bFragmentsExist = true; - } - rc = true; - } - // Use the domains to set fragment texture coordinates - if (bFragmentsExist && bSetFragmentTextureCoordinates) - ON_SubD::SetTextureCoordinatesFromFaceDomains(fit); - - return rc; -} - -bool ON_SubD::SetTextureCoordinatesFromFaceDomains() const -{ - if (ON_SubDTextureDomainType::Unset == this->TextureDomainType()) - { - // uset default to packed - if (false == SetTextureDomains(ON_SubDTextureDomainType::Packed, false, false)) - return false; - } - ON_SubDFaceIterator fit(*this); - const bool rc = ON_SubD::SetTextureCoordinatesFromFaceDomains(fit); - const ON_MappingTag& mapping_tag - = (rc) - ? ON_MappingTag::SurfaceParameterMapping - : ON_MappingTag::Unset; - SetTextureMappingTag(mapping_tag); - return rc; -} - - -bool ON_SubD::SetTextureCoordinatesFromFaceDomains(ON_SubDFaceIterator& fit) -{ - // estimate face image size - used to add empty space around ngon-subdfragments - const unsigned int face_image_size = ON_SubD::TextureImageSuggestedMinimumSize(ON_SubD::TextureDomainGridSize(fit.FaceCount(), 0, 0)); - - for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) - { - if (f->m_edge_count < 3) - continue; - const ON_SubDMeshFragment* fragment = f->MeshFragments(); - if (nullptr == fragment) - continue; - const ON_2dPoint face_corners[4] = { - f->TextureDomainCorner(true, false, 0), - f->TextureDomainCorner(true, false, 1), - f->TextureDomainCorner(true, false, 2), - f->TextureDomainCorner(true, false, 3) - }; - if (4 == f->m_edge_count) - { - // This face in a quad with a single fragment and that fragment gets the entire face_texture_domain - fragment->SetTextureCoordinateCorners(true, face_corners, true); - } - else - { - // This face in an n-gon with n fragments that each get a portion of the face_texture_domain - ON_2dVector frag_texture_domain = ON_2dVector::NanVector; - ON_2dVector frag_texture_delta = ON_2dVector::NanVector; - const ON_2udex fragment_grid_size = ON_SubD::GetTextureDomainAndDelta( - f->m_edge_count, - face_image_size, - face_image_size, - frag_texture_domain, - frag_texture_delta - ); - ON_2udex frag_k(0, 0); - for (unsigned short frag_dex = 0; frag_dex < f->m_edge_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment) + if (bFromFaceTexturePoints && f->TexturePointsAreSet()) { - if (0 != frag_dex) + // All the code in this if clause handles setting custom texture coordinates. + ON_3dPoint frag_texture_corners[4]; + if (1 == frag_count) { - frag_k.i = (++frag_k.i % fragment_grid_size.i); - if (0 == frag_k.i) - ++frag_k.j; + // f is a quad face and there is one fragment + frag_texture_corners[0] = f->TexturePoint(0); + frag_texture_corners[1] = f->TexturePoint(1); + frag_texture_corners[2] = f->TexturePoint(2); + frag_texture_corners[3] = f->TexturePoint(3); + fragment->SetTextureCoordinateCornersForExperts(false, frag_texture_corners, true); } - const double s0 = frag_k.i*frag_texture_delta.x; - const double t0 = frag_k.j*frag_texture_delta.y; - const double s2 = s0 + frag_texture_domain.x; - const double t2 = t0 + frag_texture_domain.y; - fragment->SetTextureCoordinateCorners(true, face_corners, s0, s2, t0, t2, true); + else if ( frag_count >= 3) + { + // f is a n-gon with n subd fragments + const int k = 2; + frag_texture_corners[(2 + k) % 4] = f->TextureCenterPoint(); + ON_Line L(f->TexturePoint(f->m_edge_count - 1), f->TexturePoint(0)); + frag_texture_corners[(1 + k) % 4] = L.PointAt(0.5); + for (unsigned short frag_dex = 0; frag_dex < frag_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment) + { + L.from = L.to; + L.to = f->TexturePoint((frag_dex + 1) % frag_count); + frag_texture_corners[(3 + k) % 4] = frag_texture_corners[(1 + k) % 4]; + frag_texture_corners[(0 + k) % 4] = L.from; + frag_texture_corners[(1 + k) % 4] = L.PointAt(0.5); + fragment->SetTextureCoordinateCornersForExperts(false, frag_texture_corners, true); + } + } + continue; + } + + // The remaining code in this scope handles setting packed texture coordinates on face fragments. + // We end up here when bPacked or a face is missing texture points. + if (f->PackRectIsSet()) + { + // even when bPacked is false, use packed coordinates as a fallback + face_corners[0] = f->PackRectCorner(true, 0); + face_corners[1] = f->PackRectCorner(true, 1); + face_corners[2] = f->PackRectCorner(true, 2); + face_corners[3] = f->PackRectCorner(true, 3); + } + else + { + face_corners[0] = ON_2dPoint::NanPoint; + face_corners[1] = ON_2dPoint::NanPoint; + face_corners[2] = ON_2dPoint::NanPoint; + face_corners[3] = ON_2dPoint::NanPoint; + } + + if (1 == frag_count || 3 == frag_count) + { + // A quad ON_SubDFace has a single fragment and that fragment gets the entire face_texture_domain + // A 3-gon ON_SubDFace has 3 fragments that get a portion of the entire face_texture_domain + for (unsigned short frag_dex = 0; frag_dex < frag_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment) + fragment->SetQuadOr3gonFaceFragmentTextureCoordinateCorners(true, face_corners, true); + } + else if (frag_count >= 5) + { + // A n-gon ON_SubDFace (n has n fragments that get a portion of the entire face_texture_domain + + // General case n-gon with n >= 5. + // This face in an n-gon with n fragments that each get a portion of the + // texture domain assigned to the face. + const ON_2dVector face_pack_rect_size = f->PackRectIsSet() ? f->PackRectSize() : ON_2dVector(1.0, 1.0); + ON_2dVector ngon_sub_pack_rect_size = ON_2dVector::NanVector; + ON_2dVector ngon_sub_pack_rect_delta = ON_2dVector::NanVector; + const ON_2udex ngon_grid_size = ON_SubDFace::GetNgonSubPackRectSizeAndDelta( + frag_count, + face_pack_rect_size, + ngon_sub_pack_rect_size, + ngon_sub_pack_rect_delta + ); + for (unsigned short frag_dex = 0; frag_dex < frag_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment) + fragment->SetNgonFaceFragmentTextureCoordinateCorners(true, face_corners, face_pack_rect_size, ngon_grid_size, ngon_sub_pack_rect_size, ngon_sub_pack_rect_delta, true); } } + hash = ON_SubD::TextureSettingsHash(subd_texture_coordinate_type, ON_MappingTag::Unset); + ChangeRenderContentSerialNumber(); } + Internal_SetFragmentTextureCoordinatesTextureSettingsHash(hash); + return true; } @@ -455,105 +738,159 @@ void ON_SubD::SetTextureMappingTag(const ON_MappingTag &mapping_tag) const subdimple->SetTextureMappingTag(mapping_tag); } -bool ON_SubD::SetTextureCoordinates( +void ON_SubD::ClearFragmentTextureCoordinatesTextureSettingsHash() const +{ + Internal_SetFragmentTextureCoordinatesTextureSettingsHash(ON_SHA1_Hash::EmptyContentHash); +} + +bool ON_SubD::SetFragmentTextureCoordinates( const class ON_TextureMapping& mapping, - const class ON_Xform* subd_xform, bool bLazy ) const { - const ON_TextureMapping::TYPE mt = mapping.m_type; + const ON_SubDTextureCoordinateType subd_texture_coordinate_type = Internal_BestChoiceTextureCoordinateType(mapping); + const ON_MappingTag mapping_tag = this->TextureMappingTag(false); + const ON_SHA1_Hash hash = ON_SubD::TextureSettingsHash(subd_texture_coordinate_type, mapping_tag); - if (nullptr != subd_xform) + if (bLazy) { - if (ON_TextureMapping::TYPE::srfp_mapping == mt || ON_TextureMapping::TYPE::no_mapping == mt) - subd_xform = nullptr; - else if (false == subd_xform->IsValid() || subd_xform->IsIdentity() || subd_xform->IsZero()) - subd_xform = nullptr; + // fragment texture coordinates match what's be asked for. + const ON_SHA1_Hash current_hash = FragmentTextureCoordinatesTextureSettingsHash(); + if (hash == current_hash) + return true; } - const ON_MappingTag tag(mapping, subd_xform); - if (0 == TextureMappingTag().Compare(tag)) - return true; + if (ON_SubDTextureCoordinateType::FromMapping != subd_texture_coordinate_type || false == this->TextureMappingRequired()) + return Internal_SetFragmentTextureCoordinatesWithoutMapping(); - if (ON_TextureMapping::TYPE::srfp_mapping == mt || ON_TextureMapping::TYPE::no_mapping == mt) - SetTextureCoordinatesFromFaceDomains(); + // we have a valid and nontrivial mapping. + const ON_Xform subd_xform(mapping_tag.Transform()); + const bool bApplySubDXform = ON_MappingTag::TransformTreatedIsIdentity(&subd_xform) ? false : true; + ON_Xform P_xform, N_xform; + if (bApplySubDXform) + subd_xform.GetMappingXforms(P_xform, N_xform); - const bool bApplyUVW = (mapping.m_uvw.IsValid() && !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero()); - if (ON_TextureMapping::TYPE::srfp_mapping != mt || bApplyUVW) + const bool bApplyUVW = ON_MappingTag::TransformTreatedIsIdentity(&mapping.m_uvw) ? false : true; + + ON_3dPoint tc; + ON_SubDMeshFragmentIterator frit(*this); + for (const ON_SubDMeshFragment* fragment = frit.FirstFragment(); nullptr != fragment; fragment = frit.NextFragment()) { - // - // NOTE WELL: - // ON_SubDMeshFragment store the mesh used to render ON_SubD objects - // The mesh topology, 3d vertex locations, 3d vertex normals - // CANNOT be modified to insert "texture seams." - // - - ON_Xform P_xform, N_xform; - if (subd_xform && ON_TextureMapping::TYPE::srfp_mapping != mt) + const unsigned P_count = fragment->PointCount(); + if (P_count < 4) + continue; + const double* P = fragment->m_P; + const size_t P_stride = fragment->m_P_stride; + unsigned T_count = fragment->TextureCoordinateCount(); + if (P_count != T_count) + continue; + double* T = fragment->m_T; + if (nullptr == T) + continue; + size_t T_stride = fragment->m_T_stride; + if (0 == T_stride) { - subd_xform->GetMappingXforms(P_xform, N_xform); + T_stride = 3; + T_count = 1; } - - ON_3dPoint tc; - ON_SubDMeshFragmentIterator frit(*this); - for (const ON_SubDMeshFragment* fragment = frit.FirstFragment(); nullptr != fragment; fragment = frit.NextFragment()) + const unsigned N_count = fragment->NormalCount(); + const double* N = (N_count == P_count) ? fragment->m_N : &ON_3dVector::ZeroVector.x; + const size_t N_stride = (N_count == P_count) ? fragment->m_N_stride : 0; + for (double* T1 = T + T_stride * T_count; T < T1; T += T_stride, P += P_stride, N += N_stride) { - const unsigned P_count = fragment->PointCount(); - if (P_count < 4) - continue; - const double* P = fragment->m_P; - const size_t P_stride = fragment->m_P_stride; - unsigned T_count = fragment->TextureCoordinateCount(); - if (P_count != T_count) - continue; - double* T = fragment->m_T; - if (nullptr == T) - continue; - size_t T_stride = fragment->m_T_stride; - if (0 == T_stride) - { - T_stride = 3; - T_count = 1; - } - const unsigned N_count = fragment->NormalCount(); - const double* N = (N_count == P_count) ? fragment->m_N : &ON_3dVector::ZeroVector.x; - const size_t N_stride = (N_count == P_count) ? fragment->m_N_stride : 0; - for (double* T1 = T + T_stride * T_count; T < T1; T += T_stride, P += P_stride, N += N_stride) - { - if (ON_TextureMapping::TYPE::srfp_mapping == mt) - tc = ON_3dPoint(T[0], T[1], 0.0); - else - { - bool ok = subd_xform ? - mapping.Evaluate(ON_3dPoint(P), ON_3dVector(N), &tc, P_xform, N_xform) : - mapping.Evaluate(ON_3dPoint(P), ON_3dVector(N), &tc); - - if(!ok) - tc = ON_3dPoint::NanPoint; - } - - if (bApplyUVW) - tc = mapping.m_uvw * tc; - T[0] = tc.x; - T[1] = tc.y; - T[2] = tc.z; - } + const bool ok = bApplySubDXform ? + mapping.Evaluate(ON_3dPoint(P), ON_3dVector(N), &tc, P_xform, N_xform) : + mapping.Evaluate(ON_3dPoint(P), ON_3dVector(N), &tc); + if(!ok) + tc = ON_3dPoint::NanPoint; + else if (bApplyUVW) + tc = mapping.m_uvw * tc; + T[0] = tc.x; + T[1] = tc.y; + T[2] = tc.z; } } - SetTextureMappingTag(tag); + Internal_SetFragmentTextureCoordinatesTextureSettingsHash(hash); return true; } - -const ON_MappingTag ON_SubD::TextureMappingTag() const +void ON_SubD::Internal_SetFragmentTextureCoordinatesTextureSettingsHash(ON_SHA1_Hash hash) const { const ON_SubDimple* subdimple = SubDimple(); - return (nullptr != subdimple) ? subdimple->TextureMappingTag() : ON_MappingTag(); + if (nullptr != subdimple) + subdimple->Internal_SetFragmentTextureCoordinatesTextureSettingsHash(hash); } +const ON_SHA1_Hash ON_SubD::FragmentTextureCoordinatesTextureSettingsHash() const +{ + const ON_SubDimple* subdimple = SubDimple(); + return (nullptr != subdimple) ? subdimple->FragmentTextureCoordinatesTextureSettingsHash() : ON_SHA1_Hash::EmptyContentHash; +} + +const ON_MappingTag ON_SubD::TextureMappingTag(bool bIgnoreTextureCoordinateType) const +{ + const ON_SubDimple* subdimple = SubDimple(); + return (nullptr != subdimple) ? subdimple->TextureMappingTag(bIgnoreTextureCoordinateType) : ON_MappingTag(); +} + +const ON_SHA1_Hash ON_SubD::TextureSettingsHash(ON_SubDTextureCoordinateType texture_coordinate_type, const ON_MappingTag& texture_mapping_tag) +{ + bool bHashMapping = false; + switch (texture_coordinate_type) + { + case ON_SubDTextureCoordinateType::Unset: + break; + case ON_SubDTextureCoordinateType::Unpacked: + break; + case ON_SubDTextureCoordinateType::Packed: + break; + case ON_SubDTextureCoordinateType::Zero: + break; + case ON_SubDTextureCoordinateType::Nan: + break; + case ON_SubDTextureCoordinateType::FromFaceTexturePoints: + break; + case ON_SubDTextureCoordinateType::FromMapping: + switch (texture_mapping_tag.m_mapping_type) + { + case ON_TextureMapping::TYPE::no_mapping: + texture_coordinate_type = ON_SubD::DefaultTextureCoordinateType; + break; + case ON_TextureMapping::TYPE::srfp_mapping: + texture_coordinate_type = ON_SubDTextureCoordinateType::Packed; + break; + default: + if (texture_mapping_tag.IsSet()) + bHashMapping = true; + else + texture_coordinate_type = ON_SubD::DefaultTextureCoordinateType; + break; + } + break; + default: + ON_SUBD_ERROR("Invalid texture_coordinate_type parameter"); + texture_coordinate_type = ON_SubDTextureCoordinateType::Unset; + break; + } + + ON_SHA1 sha1; + + sha1.AccumulateBytes(&texture_coordinate_type, sizeof(texture_coordinate_type)); // 1 byte + if (bHashMapping) + sha1.AccumulateSubHash(texture_mapping_tag.Hash()); + + return sha1.Hash(); +} + +const ON_SHA1_Hash ON_SubD::TextureSettingsHash() const +{ + return ON_SubD::TextureSettingsHash(this->TextureCoordinateType(), this->TextureMappingTag(false)); +} + + #pragma endregion #endif @@ -561,62 +898,6 @@ const ON_MappingTag ON_SubD::TextureMappingTag() const // // ON_SubDFace - texture coordinates // -#if 1 -#pragma region ON_SubDFace - texture coordinates - -void ON_SubDFace::SetTextureDomain( - ON_SubDTextureDomainType texture_domain_type, - ON_2dPoint origin, - ON_2dVector delta, - int packing_rotation_degrees -) const -{ - m_texture_coordinate_bits = 0; - unsigned char domain_type_bits = static_cast(texture_domain_type); - if (domain_type_bits < 8) - { - domain_type_bits <<= 4; - if (domain_type_bits != (domain_type_bits & TextureCoordinateBits::DomainTypeMask)) - domain_type_bits = 0; - } - if ( - 0 != domain_type_bits - && origin.IsValid() - && delta.x >= 0.0 && delta.x < ON_UNSET_POSITIVE_VALUE - && delta.y >= 0.0 && delta.y < ON_UNSET_POSITIVE_VALUE - && 0 == packing_rotation_degrees % 90 - ) - { - m_texture_coordinate_origin[0] = origin.x; - m_texture_coordinate_origin[1] = origin.y; - m_texture_coordinate_delta[0] = delta.x; - m_texture_coordinate_delta[1] = delta.y; - - ON_SubDFace::TextureCoordinateBits packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate0; - switch (((packing_rotation_degrees % 360) + 360) % 360) - { - case 90: - packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate90; - break; - case 180: - packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate180; - break; - case 270: - packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate270; - break; - } - m_texture_coordinate_bits |= packing_rotation; - m_texture_coordinate_bits |= domain_type_bits; - } - else - { - m_texture_coordinate_origin[0] = ON_DBL_QNAN; - m_texture_coordinate_origin[1] = ON_DBL_QNAN; - m_texture_coordinate_delta[0] = ON_DBL_QNAN; - m_texture_coordinate_delta[1] = ON_DBL_QNAN; - } -} - void ON_SubDFace::SetMaterialChannelIndex(int material_channel_index) const { if ( material_channel_index > 0 && material_channel_index <= ON_Material::MaximumMaterialChannelIndex ) @@ -677,6 +958,10 @@ unsigned int ON_SubD::ClearPerFaceMaterialChannelIndices() ++change_count; } } + + if (change_count>0) + ChangeRenderContentSerialNumber(); + return change_count; } @@ -703,6 +988,8 @@ unsigned int ON_SubD::ClearPerFaceColors() const ++change_count; } } + if (change_count > 0) + ChangeRenderContentSerialNumber(); return change_count; } @@ -717,118 +1004,23 @@ bool ON_SubD::HasPerFaceColors() const return false; } -const bool ON_SubDFace::TextureDomainIsSet() const +void ON_SubD::SetPerFaceColorsFromPackId() const { - return (ON_SubDTextureDomainType::Unset != TextureDomainType()); -} + if (FaceCount() <= 0) + return; -const ON_2dPoint ON_SubDFace::TextureDomainOrigin() const -{ - return ON_2dPoint(m_texture_coordinate_origin); -} - -const ON_2dVector ON_SubDFace::TextureDomainDelta() const -{ - return ON_2dVector(m_texture_coordinate_delta); -} - -ON_SubDTextureDomainType ON_SubDFace::TextureDomainType() const -{ - return ON_SubD::TextureDomainTypeFromUnsigned((m_texture_coordinate_bits & TextureCoordinateBits::DomainTypeMask) >> 4); -} - -unsigned int ON_SubDFace::TextureDomainRotationDegrees() const -{ - unsigned int packing_rotation_degrees = 0; - switch (m_texture_coordinate_bits & ON_SubDFace::TextureCoordinateBits::PackingRotateMask) + ON_SubDFaceIterator fit(*this); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { - case ON_SubDFace::TextureCoordinateBits::PackingRotate90: - packing_rotation_degrees = 90; - break; - case ON_SubDFace::TextureCoordinateBits::PackingRotate180: - packing_rotation_degrees = 180; - break; - case ON_SubDFace::TextureCoordinateBits::PackingRotate270: - packing_rotation_degrees = 270; - break; + const unsigned pack_id = f->PackId(); + if (0 == pack_id) + f->ClearPerFaceColor(); + else + f->SetPerFaceColor(ON_Color::RandomColor(pack_id)); } - return packing_rotation_degrees; + ChangeRenderContentSerialNumber(); // face color changes. } -double ON_SubDFace::TextureDomainRotationRadians() const -{ - double x = 0.0; - switch (m_texture_coordinate_bits & ON_SubDFace::TextureCoordinateBits::PackingRotateMask) - { - case ON_SubDFace::TextureCoordinateBits::PackingRotate90: - x = 1.0; - break; - case ON_SubDFace::TextureCoordinateBits::PackingRotate180: - x = 2.0; - break; - case ON_SubDFace::TextureCoordinateBits::PackingRotate270: - x = 3.0; - break; - } - return x * 0.5*ON_PI; -} - -const ON_2dPoint ON_SubDFace::TextureDomainCorner(bool bGridOrder, bool bNormalized, int corner_index) const -{ - if (false == TextureDomainIsSet()) - return ON_2dPoint::Origin; - - corner_index = ((corner_index % 4) + 4) % 4; - // now corner_index = 0,1,2 or 3. - - if (bGridOrder) - { - if (2 == corner_index) - corner_index = 3; - else if (3 == corner_index) - corner_index = 2; - } - // now corner index is a counter-clockwise corner index - - int packrot_dex = 0; - switch (m_texture_coordinate_bits & ON_SubDFace::TextureCoordinateBits::PackingRotateMask) - { - case ON_SubDFace::TextureCoordinateBits::PackingRotate90: - packrot_dex = 3; - break; - case ON_SubDFace::TextureCoordinateBits::PackingRotate180: - packrot_dex = 2; - break; - case ON_SubDFace::TextureCoordinateBits::PackingRotate270: - packrot_dex = 1; - break; - } - - corner_index = (corner_index + packrot_dex) % 4; - // now the packing rotation is taken into account. - - ON_2dPoint corner = bNormalized ? ON_2dPoint::Origin : TextureDomainOrigin(); - const ON_2dVector delta = bNormalized ? ON_2dVector(1.0, 1.0) : TextureDomainDelta(); - switch (corner_index) - { - case 1: - corner.x += delta.x; - break; - case 2: - corner.x += delta.x; - corner.y += delta.y; - break; - case 3: - corner.y += delta.y; - break; - } - - return corner; -} - -#pragma endregion -#endif - ////////////////////////////////////////////////////////////////////////////// // // ON_SubDMeshFragment - texture coordinates @@ -841,9 +1033,9 @@ bool ON_SubDMeshFragment::TextureCoordinatesExist() const return (0 != (m_vertex_count_etc & ON_SubDMeshFragment::EtcTextureCoordinatesExistBit)); } -void ON_SubDMeshFragment::SetTextureCoordinatesExist(bool TextureCoordinatesExist) const +void ON_SubDMeshFragment::SetTextureCoordinatesExist(bool bTextureCoordinatesExist) const { - if (TextureCoordinatesExist) + if (bTextureCoordinatesExist) m_vertex_count_etc |= ON_SubDMeshFragment::EtcTextureCoordinatesExistBit; else m_vertex_count_etc &= ~ON_SubDMeshFragment::EtcTextureCoordinatesExistBit; @@ -907,105 +1099,566 @@ bool ON_SubDMeshFragment::GetTextureCoordinteCorners( return false; } -static const ON_2dPoint InternalFragTextureCorner( +static const ON_2dPoint Internal_NgonFragmentPackRectCorner( bool bGridOrder, - const ON_2dPoint Tcorners[4], + const ON_2dPoint face_pack_rect_corners[4], double s, double t ) { - return (1 - s)*(1 - t)*Tcorners[0] + s * (1 - t)*Tcorners[1] + (1 - s)*t*Tcorners[bGridOrder ? 2 : 3] + s * t*Tcorners[bGridOrder ? 3 : 2]; + // used for 3-gons and for n-gons with n >= 5 + return (1.0 - s) * (1.0 - t) * face_pack_rect_corners[0] + s * (1.0 - t) * face_pack_rect_corners[1] + (1.0 - s) * t * face_pack_rect_corners[bGridOrder ? 2 : 3] + s * t * face_pack_rect_corners[bGridOrder ? 3 : 2]; } -void ON_SubDMeshFragment::SetTextureCoordinateCorners( +static const ON_3dPoint Internal_NgonFragmentTextureCoordinateCorner( bool bGridOrder, - const ON_2dPoint texture_coordinate_corners[4], - double s0, - double s1, - double t0, - double t1, - bool bSetTextureCoordinates -) const + const ON_3dPoint face_texture_coordinate_corners[4], + double s, + double t +) { - const ON_2dPoint c[4] = { - InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s0,t0), - InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s1,t0), - InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s0,t1), - InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s0,t1) - }; - - bGridOrder = true; // c[] is in grid order - SetTextureCoordinateCorners(bGridOrder, c, bSetTextureCoordinates); + // used for 3-gons and for n-gons with n >= 5 + return (1.0 - s) * (1.0 - t) * face_texture_coordinate_corners[0] + s * (1.0 - t) * face_texture_coordinate_corners[1] + (1.0 - s) * t * face_texture_coordinate_corners[bGridOrder ? 2 : 3] + s * t * face_texture_coordinate_corners[bGridOrder ? 3 : 2]; } -void ON_SubDMeshFragment::SetTextureCoordinateCorners( + +void ON_SubDMeshFragment::SetPackRectCornersForExperts( bool bGridOrder, - const ON_2dPoint texture_coordinate_corners[4], - bool bSetTextureCoordinates -) const + const ON_2dPoint pack_rect_corners[4] +) { - if (nullptr != texture_coordinate_corners) + if (nullptr != pack_rect_corners) { // m_ctrlnetT[] is in grid order. // lower left - m_ctrlnetT[0][0] = texture_coordinate_corners[0].x; - m_ctrlnetT[0][1] = texture_coordinate_corners[0].y; - m_ctrlnetT[0][2] = 0.0; + m_pack_rect[0][0] = pack_rect_corners[0].x; + m_pack_rect[0][1] = pack_rect_corners[0].y; // lower right - m_ctrlnetT[1][0] = texture_coordinate_corners[1].x; - m_ctrlnetT[1][1] = texture_coordinate_corners[1].y; - m_ctrlnetT[1][2] = 0.0; + m_pack_rect[1][0] = pack_rect_corners[1].x; + m_pack_rect[1][1] = pack_rect_corners[1].y; // upper left int i = bGridOrder ? 2 : 3; - m_ctrlnetT[2][0] = texture_coordinate_corners[i].x; - m_ctrlnetT[2][1] = texture_coordinate_corners[i].y; - m_ctrlnetT[2][2] = 0.0; + m_pack_rect[2][0] = pack_rect_corners[i].x; + m_pack_rect[2][1] = pack_rect_corners[i].y; // upper right i = bGridOrder ? 3 : 2; - m_ctrlnetT[3][0] = texture_coordinate_corners[i].x; - m_ctrlnetT[3][1] = texture_coordinate_corners[i].y; - m_ctrlnetT[3][2] = 0.0; - - if (bSetTextureCoordinates) - SetTextureCoordinatesFromCorners(); + m_pack_rect[3][0] = pack_rect_corners[i].x; + m_pack_rect[3][1] = pack_rect_corners[i].y; } } -void ON_SubDMeshFragment::SetTextureCoordinateCorners( +void ON_SubDMeshFragment::SetQuadOr3gonFaceFragmentTextureCoordinateCorners( bool bGridOrder, - const ON_3dPoint texture_coordinate_corners[4], + const ON_3dPoint face_texture_coordinate_corners[4], bool bSetTextureCoordinates ) const { - if (nullptr != texture_coordinate_corners) + if (1 == m_face_fragment_count && 0 == m_face_fragment_index) + { + // A quad ON_SubDFace is rendered with one ON_SubDMeshFragments. + SetTextureCoordinateCornersForExperts(bGridOrder, face_texture_coordinate_corners, bSetTextureCoordinates); + } + else + { + ON_3dPoint fragment_corners[4]; + + if (3 == m_face_fragment_count) + { + // A 3-gon ON_SubDFace is rendered with three ON_SubDMeshFragments. + // The pack rect assigned to the face is divided into quarters and + // 3 of those quarters are assigned to the 3-gon fragments + // in a continuous manner. + + const int i2 = bGridOrder ? 2 : 3; + const int i3 = bGridOrder ? 3 : 2; + + fragment_corners[0] = ON_3dPoint::Midpoint( + ON_3dPoint::Midpoint(face_texture_coordinate_corners[0], face_texture_coordinate_corners[1]), + ON_3dPoint::Midpoint(face_texture_coordinate_corners[i2], face_texture_coordinate_corners[i3]) + ); + + switch (m_face_fragment_index) + { + case 0: + // fragment_corners[] in counter-clockwise order for 3-gon's 1st fragment + fragment_corners[1] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[0], face_texture_coordinate_corners[i2]); + fragment_corners[2] = face_texture_coordinate_corners[0]; + fragment_corners[3] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[0], face_texture_coordinate_corners[1]); + break; + + case 1: + // fragment_corners[] in counter-clockwise order for 3-gon's 2nd fragment in + fragment_corners[1] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[0], face_texture_coordinate_corners[1]); + fragment_corners[2] = face_texture_coordinate_corners[1]; + fragment_corners[3] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[1], face_texture_coordinate_corners[i3]); + break; + + case 2: + // fragment_corners[] in counter-clockwise order for 3-gon's 3rd fragment + fragment_corners[1] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[1], face_texture_coordinate_corners[i3]); + fragment_corners[2] = face_texture_coordinate_corners[i3]; + fragment_corners[3] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[i2], face_texture_coordinate_corners[i3]); + break; + + default: + ON_SUBD_ERROR("Invalid m_face_fragment_index value"); + fragment_corners[0] = ON_3dPoint::NanPoint; + fragment_corners[1] = ON_3dPoint::NanPoint; + fragment_corners[2] = ON_3dPoint::NanPoint; + fragment_corners[3] = ON_3dPoint::NanPoint; + break; + } + } + else + { + // n-gon with n >= 5 or ininitialized m_face_fragment_count. + // If m_face_fragment_count >= 5, then you should be calling SetNgonFaceFragmentTextureCoordinateCorners(). + ON_SUBD_ERROR("Invalid m_face_fragment_count value"); + fragment_corners[0] = ON_3dPoint::NanPoint; + fragment_corners[1] = ON_3dPoint::NanPoint; + fragment_corners[2] = ON_3dPoint::NanPoint; + fragment_corners[3] = ON_3dPoint::NanPoint; + } + + SetTextureCoordinateCornersForExperts(false, fragment_corners, true); + } +} + +bool ON_SubDFace::GetMeshFragmentPackRectCorners( + bool bGridOrder, + unsigned int fragment_index, + ON_2dPoint mesh_fragment_pack_rect_corners[4] +) const +{ + if (nullptr == mesh_fragment_pack_rect_corners) + return true; // confused caller + + if (fragment_index >= ( (4 == m_edge_count) ? 1U : ((unsigned)m_edge_count) ) ) + { + return ON_SUBD_RETURN_ERROR(false); + } + + if (PackRectIsSet()) + { + switch (m_edge_count) + { + case 0: + case 1: + case 2: + mesh_fragment_pack_rect_corners[0] = ON_2dPoint::NanPoint; + mesh_fragment_pack_rect_corners[1] = ON_2dPoint::NanPoint; + mesh_fragment_pack_rect_corners[2] = ON_2dPoint::NanPoint; + mesh_fragment_pack_rect_corners[3] = ON_2dPoint::NanPoint; + return false; + break; + + case 3: // triangle + { + // A 3-gon ON_SubDFace is rendered with three ON_SubDMeshFragments. + // The pack rect assigned to the face is divided into quarters and + // 3 of those quarters are assigned to the 3-gon fragments + // in a continuous manner. + + const ON_2dPoint face_pack_rect_corners[4] = + { + PackRectCorner(bGridOrder, 0), + PackRectCorner(bGridOrder, 1), + PackRectCorner(bGridOrder, 2), + PackRectCorner(bGridOrder, 3) + }; + const int i2 = bGridOrder ? 2 : 3; + const int i3 = bGridOrder ? 3 : 2; + + mesh_fragment_pack_rect_corners[0] = ON_2dPoint::Midpoint( + ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]), + ON_2dPoint::Midpoint(face_pack_rect_corners[i2], face_pack_rect_corners[i3]) + ); + + switch (fragment_index) + { + case 0: + // mesh_fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 1st fragment + mesh_fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[i2]); + mesh_fragment_pack_rect_corners[i2] = face_pack_rect_corners[0]; + mesh_fragment_pack_rect_corners[i3] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]); + break; + + case 1: + // mesh_fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 2nd fragment in + mesh_fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]); + mesh_fragment_pack_rect_corners[i2] = face_pack_rect_corners[1]; + mesh_fragment_pack_rect_corners[i3] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]); + break; + + case 2: + // mesh_fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 3rd fragment + mesh_fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]); + mesh_fragment_pack_rect_corners[i2] = face_pack_rect_corners[i3]; + mesh_fragment_pack_rect_corners[i3] = ON_2dPoint::Midpoint(face_pack_rect_corners[i2], face_pack_rect_corners[i3]); + break; + } + } + return true; + break; + + case 4: // quad + return this->GetFacePackRectCorners(bGridOrder,mesh_fragment_pack_rect_corners); + break; + + default: // n-gon with n >= 5 + { + // An n-gon ON_SubDFace is rendered with n ON_SubDMeshFragments. + ON_2dPoint face_pack_rect_corners[4]; + if (this->GetFacePackRectCorners(bGridOrder, face_pack_rect_corners)) + { + const ON_2dVector face_pack_rect_size = this->PackRectSize(); + ON_2dVector ngon_sub_pack_rect_size; + ON_2dVector ngon_sub_pack_rect_delta; + const ON_2udex ngon_grid_size = ON_SubDFace::GetNgonSubPackRectSizeAndDelta( + m_edge_count, + face_pack_rect_size, + ngon_sub_pack_rect_size, + ngon_sub_pack_rect_delta + ); + if (ngon_grid_size.i > 0 && ngon_grid_size.j > 0 && ON_SubDMeshFragment::GetNgonFaceFragmentPackRectCorners( + m_edge_count, + fragment_index, + bGridOrder, + face_pack_rect_corners, + face_pack_rect_size, + ngon_grid_size, + ngon_sub_pack_rect_size, + ngon_sub_pack_rect_delta, + mesh_fragment_pack_rect_corners + )) + { + return true; + } + } + } + break; + } + } + + mesh_fragment_pack_rect_corners[0] = ON_2dPoint::NanPoint; + mesh_fragment_pack_rect_corners[1] = ON_2dPoint::NanPoint; + mesh_fragment_pack_rect_corners[2] = ON_2dPoint::NanPoint; + mesh_fragment_pack_rect_corners[3] = ON_2dPoint::NanPoint; + return false; +} + + +static const ON_2dPoint Internal_PackRectCenterPoint( + const ON_2dPoint face_pack_rect_corners[4] +) +{ + // This code works for face_pack_rect_corners[] in either grid order or counter clockwise order + // because swapping 2 and 3 does not change the result. + return ON_2dPoint::Midpoint( + ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]), + ON_2dPoint::Midpoint(face_pack_rect_corners[2], face_pack_rect_corners[3]) + ); +} + +bool ON_SubDMeshFragment::Get3gonFaceFragmentPackRectCorners( + bool bFaceGridOrder, + const ON_2dPoint face_pack_rect_corners[4], + unsigned int fragment_index, + bool bFragmentGridOrder, + ON_2dPoint fragment_pack_rect_corners[4]) +{ + fragment_pack_rect_corners[0] = Internal_PackRectCenterPoint(face_pack_rect_corners); + // A 3-gon ON_SubDFace is rendered with three ON_SubDMeshFragments. + // The pack rect assigned to the face is divided into quarters and + // 3 of those quarters are assigned to the 3-gon fragments + // in a continuous manner. + + const int i2 = bFaceGridOrder ? 2 : 3; + const int i3 = bFaceGridOrder ? 3 : 2; + + const int j2 = bFragmentGridOrder ? 3 : 2; + const int j3 = bFragmentGridOrder ? 2 : 3; + + fragment_pack_rect_corners[0] = Internal_PackRectCenterPoint(face_pack_rect_corners); + + bool rc = true; + switch (fragment_index) + { + case 0: + // fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 1st fragment + fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[i2]); + fragment_pack_rect_corners[j2] = face_pack_rect_corners[0]; + fragment_pack_rect_corners[j3] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]); + break; + + case 1: + // fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 2nd fragment in + fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]); + fragment_pack_rect_corners[j2] = face_pack_rect_corners[1]; + fragment_pack_rect_corners[j3] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]); + break; + + case 2: + // fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 3rd fragment + fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]); + fragment_pack_rect_corners[j2] = face_pack_rect_corners[i3]; + fragment_pack_rect_corners[j3] = ON_2dPoint::Midpoint(face_pack_rect_corners[i2], face_pack_rect_corners[i3]); + break; + + default: + ON_SUBD_ERROR("Invalid m_face_fragment_index value"); + fragment_pack_rect_corners[0] = ON_2dPoint::NanPoint; + fragment_pack_rect_corners[1] = ON_2dPoint::NanPoint; + fragment_pack_rect_corners[2] = ON_2dPoint::NanPoint; + fragment_pack_rect_corners[3] = ON_2dPoint::NanPoint; + rc = false; + break; + } + return rc; + +} + +void ON_SubDMeshFragment::SetQuadOr3gonFaceFragmentPackRectCorners( + bool bGridOrder, + const ON_2dPoint face_pack_rect_corners[4] +) +{ + if (1 == m_face_fragment_count && 0 == m_face_fragment_index) + { + // A quad ON_SubDFace is rendered with one ON_SubDMeshFragments. + SetPackRectCornersForExperts(bGridOrder, face_pack_rect_corners); + } + else + { + ON_2dPoint fragment_corners[4]; + + if (3 == m_face_fragment_count) + { + // A 3-gon ON_SubDFace is rendered with three ON_SubDMeshFragments. + // The pack rect assigned to the face is divided into quarters and + // 3 of those quarters are assigned to the 3-gon fragments + // in a continuous manner. + + const int i2 = bGridOrder ? 2 : 3; + const int i3 = bGridOrder ? 3 : 2; + + fragment_corners[0] = Internal_PackRectCenterPoint(face_pack_rect_corners); + + switch (m_face_fragment_index) + { + case 0: + // fragment_corners[] in counter-clockwise order for 3-gon's 1st fragment + fragment_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[i2]); + fragment_corners[2] = face_pack_rect_corners[0]; + fragment_corners[3] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]); + break; + + case 1: + // fragment_corners[] in counter-clockwise order for 3-gon's 2nd fragment in + fragment_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]); + fragment_corners[2] = face_pack_rect_corners[1]; + fragment_corners[3] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]); + break; + + case 2: + // fragment_corners[] in counter-clockwise order for 3-gon's 3rd fragment + fragment_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]); + fragment_corners[2] = face_pack_rect_corners[i3]; + fragment_corners[3] = ON_2dPoint::Midpoint(face_pack_rect_corners[i2], face_pack_rect_corners[i3]); + break; + + default: + ON_SUBD_ERROR("Invalid m_face_fragment_index value"); + fragment_corners[0] = ON_2dPoint::NanPoint; + fragment_corners[1] = ON_2dPoint::NanPoint; + fragment_corners[2] = ON_2dPoint::NanPoint; + fragment_corners[3] = ON_2dPoint::NanPoint; + break; + } + } + else + { + // n-gon with n >= 5 or ininitialized m_face_fragment_count. + // If m_face_fragment_count >= 5, then you should be calling SetNgonFaceFragmentPackRectCorners(). + ON_SUBD_ERROR("Invalid m_face_fragment_count value"); + fragment_corners[0] = ON_2dPoint::NanPoint; + fragment_corners[1] = ON_2dPoint::NanPoint; + fragment_corners[2] = ON_2dPoint::NanPoint; + fragment_corners[3] = ON_2dPoint::NanPoint; + } + + SetPackRectCornersForExperts(false, fragment_corners); + } +} + +static double Internal_ClampBetweenZeroAndOne(double x) +{ + if (x < 0.0) + return 0.0; + if (x > 1.0) + return 1.0; + return x; +} + +void ON_SubDMeshFragment::SetNgonFaceFragmentTextureCoordinateCorners( + bool bGridOrder, + const ON_3dPoint face_texture_coordinate_corners[4], + ON_2dVector face_pack_rect_size, + ON_2udex ngon_grid_size, + ON_2dVector ngon_sub_pack_rect_size, + ON_2dVector ngon_sub_pack_rect_delta, + bool bSetTextureCoordinates +) const +{ + for (;;) + { + if ( + face_texture_coordinate_corners[0] == face_texture_coordinate_corners[1] + && face_texture_coordinate_corners[0] == face_texture_coordinate_corners[2] + && face_texture_coordinate_corners[0] == face_texture_coordinate_corners[3] + ) + { + // constant texture coordinates + SetTextureCoordinateCornersForExperts(true, face_texture_coordinate_corners, bSetTextureCoordinates); + return; + } + + const unsigned ngon_edge_count = m_face_fragment_count; + const unsigned ngon_fragment_index = m_face_fragment_index; + if (ngon_edge_count < 5 || ngon_edge_count > ON_SubDFace::MaximumEdgeCount) + break; + if (ngon_fragment_index >= ngon_edge_count) + break; + if (ngon_grid_size.i * ngon_grid_size.j < ngon_edge_count) + break; + const ON_2udex frag_k(ngon_fragment_index % ngon_grid_size.i, ngon_fragment_index / ngon_grid_size.i); + const double s0 = Internal_ClampBetweenZeroAndOne(frag_k.i * (ngon_sub_pack_rect_delta.x/face_pack_rect_size.x)); + const double s1 = Internal_ClampBetweenZeroAndOne(s0 + (ngon_sub_pack_rect_size.x/ face_pack_rect_size.x)); + const double t0 = Internal_ClampBetweenZeroAndOne(frag_k.j * (ngon_sub_pack_rect_delta.y/ face_pack_rect_size.y)); + const double t1 = Internal_ClampBetweenZeroAndOne(t0 + (ngon_sub_pack_rect_size.y/ face_pack_rect_size.y)); + const ON_3dPoint fragment_texture_coordinate_corners[4] = { + Internal_NgonFragmentTextureCoordinateCorner(bGridOrder,face_texture_coordinate_corners,s0,t0), + Internal_NgonFragmentTextureCoordinateCorner(bGridOrder,face_texture_coordinate_corners,s1,t0), + Internal_NgonFragmentTextureCoordinateCorner(bGridOrder,face_texture_coordinate_corners,s0,t1), + Internal_NgonFragmentTextureCoordinateCorner(bGridOrder,face_texture_coordinate_corners,s1,t1) + }; + // fragment_texture_coordinate_corners[] is in grid order + SetTextureCoordinateCornersForExperts(true, fragment_texture_coordinate_corners, bSetTextureCoordinates); + return; + } + ON_SUBD_ERROR("Invalid input."); + const ON_3dPoint nan_corners[4] = { ON_3dPoint ::NanPoint,ON_3dPoint::NanPoint ,ON_3dPoint::NanPoint ,ON_3dPoint::NanPoint }; + SetTextureCoordinateCornersForExperts(bGridOrder, nan_corners, bSetTextureCoordinates); +} + + +bool ON_SubDMeshFragment::GetNgonFaceFragmentPackRectCorners( + unsigned int ngon_edge_count, + unsigned int ngon_fragment_index, + bool bGridOrder, + const ON_2dPoint face_pack_rect_corners[4], + ON_2dVector face_pack_rect_size, + ON_2udex ngon_grid_size, + ON_2dVector ngon_sub_pack_rect_size, + ON_2dVector ngon_sub_pack_rect_delta, + ON_2dPoint fragment_pack_rect_corners[4] +) +{ + for (;;) + { + if (ngon_edge_count < 5 || ngon_edge_count > ON_SubDFace::MaximumEdgeCount) + break; + if (ngon_fragment_index >= ngon_edge_count) + break; + if (ngon_grid_size.i * ngon_grid_size.j < ngon_edge_count) + break; + if (nullptr == fragment_pack_rect_corners) + break; + const ON_2udex frag_k(ngon_fragment_index % ngon_grid_size.i, ngon_fragment_index / ngon_grid_size.i); + const double s0 = Internal_ClampBetweenZeroAndOne(frag_k.i * (ngon_sub_pack_rect_delta.x / face_pack_rect_size.x)); + const double s1 = Internal_ClampBetweenZeroAndOne(s0 + (ngon_sub_pack_rect_size.x / face_pack_rect_size.x)); + const double t0 = Internal_ClampBetweenZeroAndOne(frag_k.j * (ngon_sub_pack_rect_delta.y / face_pack_rect_size.y)); + const double t1 = Internal_ClampBetweenZeroAndOne(t0 + (ngon_sub_pack_rect_size.y / face_pack_rect_size.y)); + const int i2 = bGridOrder ? 2 : 3; + const int i3 = bGridOrder ? 3 : 2; + fragment_pack_rect_corners[0] = Internal_NgonFragmentPackRectCorner(bGridOrder,face_pack_rect_corners,s0,t0); + fragment_pack_rect_corners[1] = Internal_NgonFragmentPackRectCorner(bGridOrder,face_pack_rect_corners,s1,t0); + fragment_pack_rect_corners[i2] = Internal_NgonFragmentPackRectCorner(bGridOrder,face_pack_rect_corners,s0,t1); + fragment_pack_rect_corners[i3] = Internal_NgonFragmentPackRectCorner(bGridOrder,face_pack_rect_corners,s1,t1); + return true; + } + + ON_SUBD_ERROR("Invalid input."); + if (nullptr != fragment_pack_rect_corners) + { + fragment_pack_rect_corners[0] = ON_2dPoint::NanPoint; + fragment_pack_rect_corners[1] = ON_2dPoint::NanPoint; + fragment_pack_rect_corners[2] = ON_2dPoint::NanPoint; + fragment_pack_rect_corners[3] = ON_2dPoint::NanPoint; + } + + return false; +} + +void ON_SubDMeshFragment::SetNgonFaceFragmentPackRectCorners( + bool bGridOrder, + const ON_2dPoint face_pack_rect_corners[4], + ON_2dVector face_pack_rect_size, + ON_2udex ngon_grid_size, + ON_2dVector ngon_sub_pack_rect_size, + ON_2dVector ngon_sub_pack_rect_delta +) +{ + ON_2dPoint fragment_pack_rect_corners[4]; + ON_SubDMeshFragment::GetNgonFaceFragmentPackRectCorners( + m_face_fragment_count, + m_face_fragment_index, + bGridOrder, + face_pack_rect_corners, + face_pack_rect_size, + ngon_grid_size, + ngon_sub_pack_rect_size, + ngon_sub_pack_rect_delta, + fragment_pack_rect_corners + ); + SetPackRectCornersForExperts(bGridOrder, fragment_pack_rect_corners); +} + +void ON_SubDMeshFragment::SetTextureCoordinateCornersForExperts( + bool bGridOrder, + const ON_3dPoint fragment_texture_coordinate_corners[4], + bool bSetTextureCoordinates +) const +{ + if (nullptr != fragment_texture_coordinate_corners) { // m_ctrlnetT[] is in grid order. // lower left - m_ctrlnetT[0][0] = texture_coordinate_corners[0].x; - m_ctrlnetT[0][1] = texture_coordinate_corners[0].y; - m_ctrlnetT[0][2] = texture_coordinate_corners[0].z; + m_ctrlnetT[0][0] = fragment_texture_coordinate_corners[0].x; + m_ctrlnetT[0][1] = fragment_texture_coordinate_corners[0].y; + m_ctrlnetT[0][2] = fragment_texture_coordinate_corners[0].z; // lower right - m_ctrlnetT[1][0] = texture_coordinate_corners[1].x; - m_ctrlnetT[1][1] = texture_coordinate_corners[1].y; - m_ctrlnetT[1][2] = texture_coordinate_corners[1].z; + m_ctrlnetT[1][0] = fragment_texture_coordinate_corners[1].x; + m_ctrlnetT[1][1] = fragment_texture_coordinate_corners[1].y; + m_ctrlnetT[1][2] = fragment_texture_coordinate_corners[1].z; // upper left int i = bGridOrder ? 2 : 3; - m_ctrlnetT[2][0] = texture_coordinate_corners[i].x; - m_ctrlnetT[2][1] = texture_coordinate_corners[i].y; - m_ctrlnetT[2][2] = texture_coordinate_corners[i].z; + m_ctrlnetT[2][0] = fragment_texture_coordinate_corners[i].x; + m_ctrlnetT[2][1] = fragment_texture_coordinate_corners[i].y; + m_ctrlnetT[2][2] = fragment_texture_coordinate_corners[i].z; // upper right i = bGridOrder ? 3 : 2; - m_ctrlnetT[3][0] = texture_coordinate_corners[i].x; - m_ctrlnetT[3][1] = texture_coordinate_corners[i].y; - m_ctrlnetT[3][2] = texture_coordinate_corners[i].z; + m_ctrlnetT[3][0] = fragment_texture_coordinate_corners[i].x; + m_ctrlnetT[3][1] = fragment_texture_coordinate_corners[i].y; + m_ctrlnetT[3][2] = fragment_texture_coordinate_corners[i].z; if (bSetTextureCoordinates) SetTextureCoordinatesFromCorners(); @@ -1062,31 +1715,79 @@ const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinateFromCorners(unsigne return VertexTextureCoordinateFromCorners(m_grid.Grid2dexFromPointIndex(grid_point_index)); } - void ON_SubDMeshFragment::SetTextureCoordinatesFromCorners() const +{ + Internal_SetTextureCoordinatesFromCorners(m_ctrlnetT[0], m_ctrlnetT[1], m_ctrlnetT[2], m_ctrlnetT[3], ON_DBL_QNAN, 3); +} + +void ON_SubDMeshFragment::SetPackedTextureCoordinates() const +{ + Internal_SetTextureCoordinatesFromCorners(m_pack_rect[0], m_pack_rect[1], m_pack_rect[2], m_pack_rect[3], 0.0, 2); +} + +void ON_SubDMeshFragment::SetUnpackedTextureCoordinates() const +{ + const double unpacked_corners[4][2] = + { + {0.0,0.0}, + {1.0,0.0}, + {0.0,1.0}, + {1.0,1.0}, + }; + Internal_SetTextureCoordinatesFromCorners(unpacked_corners[0], unpacked_corners[1], unpacked_corners[2], unpacked_corners[3], 0.0, 2); +} + +void ON_SubDMeshFragment::Internal_SetTextureCoordinatesFromCorners( + const double* corner0, + const double* corner1, + const double* corner2, + const double* corner3, + double default_coordinate_value, + int corner_dim +) const { const unsigned n = m_grid.SideSegmentCount(); if (n <= 0U || n > ON_SubDMeshFragment::MaximumSideSegmentCount) return; - if (TextureCoordinateCapacity() < n*n) + // TextureCoordinateCapacity() check insures m_T_stride >= 3 + if (TextureCoordinateCapacity() < n * n) + return; + if (corner_dim <= 0) return; SetTextureCoordinatesExist(true); - double * T = m_T; + double* T = m_T; const double d = (double)n; double s, t; - ON_3dPoint tc; + ON_3dPoint tc(default_coordinate_value, default_coordinate_value, default_coordinate_value); + bool bConstant[3] = { corner_dim < 1, corner_dim < 2, corner_dim < 3 }; + for (int i = 0; i < corner_dim; ++i) + { + double c = corner0[i]; + if (c == corner1[i] && c == corner2[i] && c == corner3[i]) + { + tc[i] = c; + bConstant[i] = true; + } + else if (false == (c == c) || false == (corner1[i] == corner1[i]) || false == (corner2[i] == corner2[i]) || false == (corner3[i] == corner3[i])) + { + tc[i] = ON_DBL_QNAN; + bConstant[i] = true; + } + } + for (unsigned j = 0U; j <= n; ++j) { t = ((double)j) / d; for (unsigned i = 0U; i <= n; ++i) { s = ((double)i) / d; - const double c[4] = { (1.0 - s)*(1.0 - t), s*(1.0 - t), (1.0 - s)*t, s*t }; - tc = ON_3dPoint( - c[0] * m_ctrlnetT[0][0] + c[1] * m_ctrlnetT[1][0] + c[2] * m_ctrlnetT[2][0] + c[3] * m_ctrlnetT[3][0], - c[0] * m_ctrlnetT[0][1] + c[1] * m_ctrlnetT[1][1] + c[2] * m_ctrlnetT[2][1] + c[3] * m_ctrlnetT[3][1], - c[0] * m_ctrlnetT[0][2] + c[1] * m_ctrlnetT[1][2] + c[2] * m_ctrlnetT[2][2] + c[3] * m_ctrlnetT[3][2] - ); + const double c[4] = { (1.0 - s) * (1.0 - t), s * (1.0 - t), (1.0 - s) * t, s * t }; + if (false == bConstant[0]) + tc.x = c[0] * corner0[0] + c[1] * corner1[0] + c[2] * corner2[0] + c[3] * corner3[0]; + if (false == bConstant[1]) + tc.y = c[0] * corner0[1] + c[1] * corner1[1] + c[2] * corner2[1] + c[3] * corner3[1]; + if (false == bConstant[2]) + tc.z = c[0] * corner0[2] + c[1] * corner1[2] + c[2] * corner2[2] + c[3] * corner3[2]; T[0] = tc.x; T[1] = tc.y; T[2] = tc.z; @@ -1096,6 +1797,7 @@ void ON_SubDMeshFragment::SetTextureCoordinatesFromCorners() const return; } + bool ON_SubDMeshFragment::SetVertexTextureCoordinate( ON_2udex grid2dex, ON_3dPoint texture_coordinate diff --git a/opennurbs_sumsurface.cpp b/opennurbs_sumsurface.cpp index 339c9212..fb54a34f 100644 --- a/opennurbs_sumsurface.cpp +++ b/opennurbs_sumsurface.cpp @@ -480,29 +480,37 @@ bool ON_SumSurface::GetSurfaceSize( { if ( ptr[j] == nullptr ) continue; + *ptr[j] = 0.0; - if ( m_curve[j] == nullptr ) - rc = false; - - if ( ! (*ptr[j] > 0.0) ) + if (m_curve[j] == nullptr) { - int i, imax = 64, hint = 0; - double length_estimate = 0.0, d = 1.0/((double)imax); - ON_Interval cdom = m_curve[j]->Domain(); - ON_3dPoint pt0 = ON_3dPoint::UnsetPoint; - ON_3dPoint pt; - for ( i = 0; i <= imax; i++ ) - { - if ( m_curve[j]->EvPoint( cdom.ParameterAt(i*d), pt, 0, &hint ) ) - { - if ( pt0 != ON_3dPoint::UnsetPoint ) - length_estimate += pt0.DistanceTo(pt); - pt0 = pt; - } - } - *ptr[j] = length_estimate; + rc = false; + continue; } + + + int i, imax = 64, hint = 0; + double length_estimate = 0.0; + const double d = 1.0 / ((double)imax); + ON_Interval cdom = m_curve[j]->Domain(); + ON_3dPoint pt0 = ON_3dPoint::UnsetPoint; + ON_3dPoint pt; + for ( i = 0; i <= imax; i++ ) + { + if (m_curve[j]->EvPoint(cdom.ParameterAt(i * d), pt, 0, &hint) && pt.IsValid()) + { + if (pt0 != ON_3dPoint::UnsetPoint) + length_estimate += pt0.DistanceTo(pt); + pt0 = pt; + } + else + rc = false; + } + if (length_estimate > 0.0) + *ptr[j] = length_estimate; + else + rc = false; } return rc; diff --git a/opennurbs_text.cpp b/opennurbs_text.cpp index 6acef1b8..a733b41f 100644 --- a/opennurbs_text.cpp +++ b/opennurbs_text.cpp @@ -2476,7 +2476,8 @@ bool ON_TextContent::FormatAngleMeasurement( else if (ON_DimStyle::angle_format::DegMinSec == dimstyle->AngleFormat()) { wchar_t decimal_char = dimstyle->DecimalSeparator(); - ON_TextContent::FormatAngleStringDMS(angle_radians, decimal_char, sAngle); + int resolution = dimstyle->AngleResolution(); + ON_TextContent::FormatAngleStringDMS(angle_radians, decimal_char, resolution, sAngle); } formatted_string += sAngle; @@ -2511,7 +2512,16 @@ bool ON_TextContent::FormatAngleStringDMS( wchar_t decimal_char, ON_wString& formatted_string) { - bool rc = ON_NumberFormatter::FormatAngleStringDMS(angle_degrees, formatted_string); + return ON_TextContent::FormatAngleStringDMS(angle_degrees, decimal_char, 2, formatted_string); +} + +bool ON_TextContent::FormatAngleStringDMS( + double angle_degrees, + wchar_t decimal_char, + int resolution, + ON_wString& formatted_string) +{ + bool rc = ON_NumberFormatter::FormatAngleStringDMS(angle_degrees, resolution, formatted_string); if (rc && ON_wString::DecimalAsPeriod != decimal_char) formatted_string.Replace(ON_wString::DecimalAsPeriod, decimal_char); return rc; diff --git a/opennurbs_text.h b/opennurbs_text.h index 822179d6..bc41cd34 100644 --- a/opennurbs_text.h +++ b/opennurbs_text.h @@ -582,6 +582,12 @@ public: wchar_t decimal_char, ON_wString& formatted_string); + static bool FormatAngleStringDMS( + double angle_degrees, + wchar_t decimal_char, + int resolution, + ON_wString& formatted_string); + static bool FormatAngleStringDecimal( double angle_radians, int resolution, diff --git a/opennurbs_textiterator.cpp b/opennurbs_textiterator.cpp index c2e9baa2..b58befed 100644 --- a/opennurbs_textiterator.cpp +++ b/opennurbs_textiterator.cpp @@ -3369,10 +3369,13 @@ bool RtfComposer::Compose( } - temp.Format(L"}\\f%d \\fs40", stylefont_key); + if(!ComposeFS()) + temp.Format(L"}\\f%d", stylefont_key); + else + temp.Format(L"}\\f%d \\fs40", stylefont_key); + rtf += temp; - //rtf += L"}\\fs40"; if (style_bold) rtf += L"\\b"; if (style_italic) @@ -3402,6 +3405,17 @@ void RtfComposer::SetRecomposeRTF(bool b) RtfComposer::m_bComposeRTF = b; } +// Turns on or off adding \FS40 to composed strings +bool RtfComposer::m_bComposeFS = true; +bool RtfComposer::ComposeFS() +{ + return RtfComposer::m_bComposeFS; +} +void RtfComposer::SetComposeFS(bool b) +{ + RtfComposer::m_bComposeFS = b; +} + static const ON_wString Internal_PostScriptNameIfAvailable(const ON_Font& managed_font) { ON_wString style_fontname = managed_font.PostScriptName(); @@ -3604,9 +3618,12 @@ const ON_wString RtfComposer::ComposeAppleRTF( rtf_string += fonttable_string; } - temp.Format(L"}\\f%d \\fs40", deffont_key); + if (!ComposeFS()) + temp.Format(L"}\\f%d", deffont_key); + else + temp.Format(L"}\\f%d \\fs40", deffont_key); + rtf_string += temp; - //rtf_string += L"}\\fs40"; rtf_string += run_strings; rtf_string += L"}"; diff --git a/opennurbs_textiterator.h b/opennurbs_textiterator.h index f7687c54..b936b26c 100644 --- a/opennurbs_textiterator.h +++ b/opennurbs_textiterator.h @@ -893,8 +893,12 @@ public: static bool RecomposeRTF(); static void SetRecomposeRTF(bool b); + static bool ComposeFS(); + static void SetComposeFS(bool b); + private: static bool m_bComposeRTF; + static bool m_bComposeFS; RtfComposer(); diff --git a/opennurbs_texture_mapping.h b/opennurbs_texture_mapping.h index ea746baf..109775e4 100644 --- a/opennurbs_texture_mapping.h +++ b/opennurbs_texture_mapping.h @@ -84,7 +84,8 @@ public: mesh_mapping_primitive = 6, // m_mapping_primitive is an ON_Mesh srf_mapping_primitive = 7, // m_mapping_primitive is an ON_Surface brep_mapping_primitive = 8, // m_mapping_primitive is an ON_Brep - ocs_mapping = 9 // same as plane_mapping - used to differentiate between OCS and plane mapping in the UI + ocs_mapping = 9, // same as plane_mapping - used to differentiate between OCS and plane mapping in the UI + false_colors = 10 // some kind of false color mapping used to set per vertex colors. }; static ON_TextureMapping::TYPE TypeFromUnsigned( diff --git a/opennurbs_viewport.cpp b/opennurbs_viewport.cpp index 0ea10c4c..d9a4630e 100644 --- a/opennurbs_viewport.cpp +++ b/opennurbs_viewport.cpp @@ -556,6 +556,55 @@ ON_SHA1_Hash ON_Viewport::ViewProjectionContentHash() const return m_projection_content_sha1; } +ON_Viewport* ON_Viewport::ShallowCopy(ON_Viewport* destination) const +{ + if (nullptr == destination) + destination = new ON_Viewport(); + else + { + destination->PurgeUserData(); + if (this == destination) + return destination; // The caller is probably confused but this is what they should get. + *destination = ON_Viewport::DefaultTopViewYUp; // default ctor values + } + +#define ON_INTERNAL_SHALLOW_FIELD_COPY(field) destination->field = this->field + ON_INTERNAL_SHALLOW_FIELD_COPY(m_bValidCamera); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_bValidFrustum); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_bValidPort); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_bValidCameraFrame); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_projection); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_bLockCamUp); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_bLockCamDir); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_bLockCamLoc); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_frustum_symmetry_flags); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_CamLoc); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_CamDir); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_CamUp); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_CamX); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_CamY); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_CamZ); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_frus_left); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_frus_right); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_frus_bottom); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_frus_top); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_frus_near); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_frus_far); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_port_left); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_port_right); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_port_bottom); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_port_top); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_port_near); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_port_far); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_target_point); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_viewport_id); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_clip_mods); + ON_INTERNAL_SHALLOW_FIELD_COPY(m_clip_mods_inverse); + ON_INTERNAL_SHALLOW_FIELD_COPY(m__MIN_NEAR_DIST); + ON_INTERNAL_SHALLOW_FIELD_COPY(m__MIN_NEAR_OVER_FAR); +#undef ON_INTERNAL_SHALLOW_COPY + return destination; +} bool ON_Viewport::IsValidCameraLocation( ON_3dPoint candidate_point @@ -4348,10 +4397,12 @@ void ON_Viewport::GetViewScale( double* x, double* y ) const && 1.0 == m_clip_mods.m_xform[3][3] ) { + // 04 May 2020 S. Baer (RH-37076) + // Allow for negative scale values. See comments in SetViewScale double sx = m_clip_mods.m_xform[0][0]; double sy = m_clip_mods.m_xform[1][1]; - if ( sx > ON_ZERO_TOLERANCE - && sy > ON_ZERO_TOLERANCE + if ( fabs(sx) > ON_ZERO_TOLERANCE + && fabs(sy) > ON_ZERO_TOLERANCE && 0.0 == m_clip_mods.m_xform[0][1] && 0.0 == m_clip_mods.m_xform[0][2] && 0.0 == m_clip_mods.m_xform[1][0] @@ -4386,10 +4437,18 @@ bool ON_Viewport::SetViewScale( double x, double y ) // and it is hard to be sure that we could accurately extract these values // when calling GetViewScale. Removing the requirement to have one of the // values == 1 + // 04 May 2020 S. Baer (RH-37076) + // Users are requesting mirrored parallel viewports (reflected ceiling plans). + // Removing the limitation that this function imposes of only positive values + // allowed for scaling to see if a -1.0 for x is what these users are after. + // Scaling values are not saved with the 3dm file so this is not a long term + // solution for supporting RCP viewports. What this does do is let us have users + // experiment with a -1.0 horizontal scale in a custom display mode and tell + // us if this is the desired display that they are after. bool rc = false; if ( IsParallelProjection() - && x > ON_ZERO_TOLERANCE && ON_IsValid(x) - && y > ON_ZERO_TOLERANCE && ON_IsValid(y) + && fabs(x) > ON_ZERO_TOLERANCE && ON_IsValid(x) + && fabs(y) > ON_ZERO_TOLERANCE && ON_IsValid(y) // && (1.0 == x || 1.0 == y) ) { @@ -4426,7 +4485,7 @@ double ON_Viewport::ClipCoordDepthBias( double relative_depth_bias, double clip_ // // Note that there "should" be a small adjustment to the // x and y coordinates that is not performed by tweaking - // the z clipping coordiante + // the z clipping coordinate // z += vp->ClipCoordDepthBias( rel_bias, z, w ); // but the effect is actually better when the goal is to // make wires that are on shaded surfaces appear because diff --git a/opennurbs_viewport.h b/opennurbs_viewport.h index 60801584..f2dfce05 100644 --- a/opennurbs_viewport.h +++ b/opennurbs_viewport.h @@ -64,6 +64,16 @@ public: ON_Viewport( const ON_Viewport& ) = default; ON_Viewport& operator=( const ON_Viewport& ) = default; + /* + Description: + Creates a shallow copy of this (no base class member including user data are copied). + Parameters: + destination - [in] + If destination is not nullptr, then the copy is put in this class. Otherwise, the copy is put + into a viewport created by calling new ON_Viewport(). + */ + ON_Viewport* ShallowCopy(ON_Viewport* destination) const; + /* Returns: A sha1 hash of all the settings that effect view projection matrices. diff --git a/opennurbs_xform.h b/opennurbs_xform.h index eb20e748..0889788e 100644 --- a/opennurbs_xform.h +++ b/opennurbs_xform.h @@ -1544,6 +1544,47 @@ private: void* m_buffer = nullptr; }; +#pragma region +/// +/// ON_PickType specifies what type of pick is occuring. +/// +enum class ON_PickType : unsigned char +{ + /// + /// Type has not been set. + /// + Unset = 0, + + /// + /// A point pick is centered on a ray in the view frustum. + /// Often the pick region is a small frustum centered on the pick + /// ray defined by a mouse click in viewport. The size of the + /// frustum is typically a few pixels in diameter at the most + /// likely depth the user is focused on. An object must intersect + /// the pick region for it to be considered. + /// + PointPick = 1, + + /// + /// The pick region for a window pick is a rectangular frustum inside + /// the view frustum. An object must be completely inside the frustum + /// for it to be considered. + /// The pick frustum is defined by a rectangle in the view plane. + /// The size of the rectangle has varies widely in aspect and size. + /// + WindowPick = 2, + + /// + /// The pick region for a crossing pick is a rectangular frustum inside + /// the view frustum. An object must intersect the frustum + /// for it to be considered. + /// The pick frustum is defined by a rectangle in the view plane. + /// The size of the rectangle has varies widely in aspect and size. + /// + CrossingPick = 3 +}; +#pragma endregion + class ON_CLASS ON_PickPoint { public: @@ -1556,7 +1597,7 @@ public: /* Returns: - +1: a is a better pick pont than b. + +1: a is a better pick point than b. -1: b is a better pick point than a. 0: a and b are the same. */