diff --git a/opennurbs_3dm_properties.cpp b/opennurbs_3dm_properties.cpp index 65f6bfe5..e1661401 100644 --- a/opennurbs_3dm_properties.cpp +++ b/opennurbs_3dm_properties.cpp @@ -226,12 +226,13 @@ void ON_3dmRevisionHistory::Dump( ON_TextLog& dump ) const const ON_3dmNotes ON_3dmNotes::Empty; ON_3dmNotes::ON_3dmNotes() -: m_bVisible(0) -, m_bHTML(0) +: m_bVisible(false) +, m_bHTML(false) , m_window_left(0) , m_window_top(0) , m_window_right(0) , m_window_bottom(0) +, m_bLocked(false) {} ON_3dmNotes::~ON_3dmNotes() @@ -280,6 +281,14 @@ bool ON_3dmNotes::Read( ON_BinaryArchive& file ) break; if (!file.ReadInt( &m_window_bottom )) break; + + if (minor_version >= 1) + { + //https://mcneel.myjetbrains.com/youtrack/issue/RH-74718/Lock-notes + if (!file.ReadBool(&m_bLocked)) + break; + } + rc = true; break; } @@ -288,7 +297,7 @@ bool ON_3dmNotes::Read( ON_BinaryArchive& file ) bool ON_3dmNotes::Write( ON_BinaryArchive& file ) const { - bool rc = file.Write3dmChunkVersion(1,0); + bool rc = file.Write3dmChunkVersion(1,1); if ( rc ) rc = file.WriteInt( m_bHTML ); if ( rc ) rc = file.WriteString( m_notes ); if ( rc ) rc = file.WriteInt( m_bVisible ); @@ -296,6 +305,9 @@ bool ON_3dmNotes::Write( ON_BinaryArchive& file ) const if ( rc ) rc = file.WriteInt( m_window_top ); if ( rc ) rc = file.WriteInt( m_window_right ); if ( rc ) rc = file.WriteInt( m_window_bottom ); + + if (rc) rc = file.WriteBool(m_bLocked); + return rc; } diff --git a/opennurbs_3dm_properties.h b/opennurbs_3dm_properties.h index 6c159122..10336417 100644 --- a/opennurbs_3dm_properties.h +++ b/opennurbs_3dm_properties.h @@ -105,6 +105,9 @@ public: bool m_bVisible; // true if notes window is showing bool m_bHTML; // true if notes are in HTML + bool m_bLocked = false; + unsigned char reserved1 = 0; + // last window position int m_window_left; int m_window_top; diff --git a/opennurbs_brep.cpp b/opennurbs_brep.cpp index 21ed67b7..6b03b711 100644 --- a/opennurbs_brep.cpp +++ b/opennurbs_brep.cpp @@ -15,6 +15,7 @@ #include #include +#include #if !defined(ON_COMPILING_OPENNURBS) // This check is included in all opennurbs source .c and .cpp files to insure @@ -788,6 +789,7 @@ public: std::shared_ptr m_render_mesh; std::shared_ptr m_analysis_mesh; std::shared_ptr m_preview_mesh; + std::mutex m_mesh_mutex; }; ON_BrepFace::ON_BrepFace() @@ -808,6 +810,8 @@ ON_BrepFace::ON_BrepFace(int face_index) unsigned int ON_BrepFace::SizeOf() const { + std::lock_guard lock(m_pImpl->m_mesh_mutex); + unsigned int sz = ON_SurfaceProxy::SizeOf(); sz += (sizeof(*this) - sizeof(ON_SurfaceProxy)); sz += m_li.SizeOfArray(); @@ -836,6 +840,10 @@ ON_BrepFace& ON_BrepFace::operator=(const ON_BrepFace& src) m_face_material_channel = src.m_face_material_channel; m_face_uuid = src.m_face_uuid; m_per_face_color = src.m_per_face_color; + + std::lock_guard lock(m_pImpl->m_mesh_mutex); + std::lock_guard lock2(src.m_pImpl->m_mesh_mutex); + m_pImpl->m_render_mesh = src.m_pImpl->m_render_mesh ? src.m_pImpl->m_render_mesh : nullptr; m_pImpl->m_analysis_mesh = src.m_pImpl->m_analysis_mesh ? src.m_pImpl->m_analysis_mesh : nullptr; m_pImpl->m_preview_mesh = src.m_pImpl->m_preview_mesh ? src.m_pImpl->m_preview_mesh : nullptr; @@ -1088,8 +1096,11 @@ const std::shared_ptr& ON_BrepFace::UniqueMesh(ON::mesh_type mesh } + const std::shared_ptr& ON_BrepFace::SharedMesh(ON::mesh_type mesh_type) const { + std::lock_guard lock(m_pImpl->m_mesh_mutex); + std::shared_ptr* pMesh = nullptr; switch (mesh_type) @@ -1129,6 +1140,8 @@ const ON_Mesh* ON_BrepFace::Mesh( ON::mesh_type mt ) const bool ON_BrepFace::SetMesh(ON::mesh_type mt, const std::shared_ptr& mesh) { + std::lock_guard lock(m_pImpl->m_mesh_mutex); + bool rc = true; switch (mt) { @@ -1150,6 +1163,8 @@ bool ON_BrepFace::SetMesh(ON::mesh_type mt, const std::shared_ptr bool ON_BrepFace::SetMesh(ON::mesh_type mt, ON_Mesh* mesh) { + std::lock_guard lock(m_pImpl->m_mesh_mutex); + bool rc = true; switch (mt) { @@ -1176,6 +1191,8 @@ void ON_BrepFace::DestroyMesh( ON::mesh_type mt, bool bDeleteMesh ) void ON_BrepFace::DestroyMesh(ON::mesh_type mt) { + std::lock_guard lock(m_pImpl->m_mesh_mutex); + switch(mt) { case ON::render_mesh: m_pImpl->m_render_mesh.reset(); diff --git a/opennurbs_linetype.cpp b/opennurbs_linetype.cpp index c2601738..076df3ba 100644 --- a/opennurbs_linetype.cpp +++ b/opennurbs_linetype.cpp @@ -26,6 +26,7 @@ class ON_LinetypePrivate public: ON_SimpleArray m_segments; ON_SimpleArray m_taper_points; + bool m_always_model_distances = false; }; bool ON_IsHairlinePrintWidth(double width_mm) @@ -313,8 +314,10 @@ enum ON_LinetypeTypeCodes : unsigned char Width = 3, WidthUnits = 4, Taper = 5, + //minor version = 3 + AlwaysModelDistance = 6, - LastLinetypeTypeCode = 5 + LastLinetypeTypeCode = 6 }; bool ON_Linetype::Write( ON_BinaryArchive& file) const @@ -352,7 +355,9 @@ bool ON_Linetype::Write( ON_BinaryArchive& file) const // minor_version = 1: add cap and join styles to linetype // 29 Nov 2022 S. Baer (RH-7262) // minor_version = 2: add width, width units, and taper - const int minor_version = 2; + // 13 Feb 2024 S. Baer (RH-79551) + // minor_version = 3: add AlwaysModelDistance + const int minor_version = 3; if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 2, minor_version)) return false; for (;;) @@ -414,6 +419,16 @@ bool ON_Linetype::Write( ON_BinaryArchive& file) const break; } + if (AlwaysModelDistances() == true) + { + const unsigned char itemType = ON_LinetypeTypeCodes::AlwaysModelDistance; // 6 + if (!file.WriteChar(itemType)) + break; + + if (!file.WriteBool(AlwaysModelDistances())) + break; + } + // 0 indicates end of new linetype attributes const unsigned char attributes_end = 0; if (!file.WriteChar(attributes_end)) @@ -565,6 +580,25 @@ bool ON_Linetype::Read( ON_BinaryArchive& file) } } + if (minor_version >= 3) + { + if (ON_LinetypeTypeCodes::AlwaysModelDistance == item_id) + { + bool always = false; + if (!file.ReadBool(&always)) + break; + SetAlwaysModelDistances(always); + + if (!file.ReadChar(&item_id)) + break; + } + + if (3 == minor_version && item_id != 0) + { + ON_ERROR("Bug in ON_Linetype::Read for chunk version 2.3"); + } + } + if (item_id > ON_LinetypeTypeCodes::LastLinetypeTypeCode) { // we are reading file written with code newer @@ -785,3 +819,13 @@ void ON_Linetype::ClearBits() m_is_set_bits = 0; m_is_locked_bits = 0; } + +bool ON_Linetype::AlwaysModelDistances() const +{ + return m_private->m_always_model_distances; +} + +void ON_Linetype::SetAlwaysModelDistances(bool on) +{ + m_private->m_always_model_distances = on; +} diff --git a/opennurbs_linetype.h b/opennurbs_linetype.h index 187c0ed7..919f06e4 100644 --- a/opennurbs_linetype.h +++ b/opennurbs_linetype.h @@ -286,6 +286,20 @@ public: */ void ClearBits(); + /* + Description: + When true, pattern lengths and widths are always in models distances + When false (default), lengths and widths are interpreted as lengths + and widths on a page layout or in actual output prints + */ + bool AlwaysModelDistances() const; + + /* + Description: + Set how lengths and widths are interpreted when displayed in layouts + or printed + */ + void SetAlwaysModelDistances(bool on); private: mutable class ON_LinetypePrivate* m_private = nullptr; unsigned char m_is_set_bits = 0; diff --git a/opennurbs_lookup.cpp b/opennurbs_lookup.cpp index a3cf7a12..f2da3e18 100644 --- a/opennurbs_lookup.cpp +++ b/opennurbs_lookup.cpp @@ -696,6 +696,19 @@ ON__UINT64 ON_SerialNumberMap::ActiveIdCount() const return m_active_id_count; } +// 10 March 2024 S. Baer (RH-80748) +// The VS2022 compiler crashes in a release build when compiling the +// following function. Disable optimizations for this specific compile. +#if defined(ON_RUNTIME_WIN) && defined(_MSC_VER) && !defined(ON_DEBUG) +// Visual Studio 2022 initial release is 1930 +#if _MSC_VER >= 1930 +#define ON_VS2022_COMPILER_CRASH +#endif +#endif + +#if defined(ON_VS2022_COMPILER_CRASH) +#pragma optimize("", off) +#endif ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FirstElement() const { SN_ELEMENT* e=nullptr; @@ -756,6 +769,10 @@ ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FirstElement() const } return e; } +#if defined(ON_VS2022_COMPILER_CRASH) +#pragma optimize("", on) +#endif + struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::LastElement() const { diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index 53f805f2..c9e95bd4 100644 --- a/opennurbs_public_version.h +++ b/opennurbs_public_version.h @@ -6,7 +6,7 @@ // To update version numbers, edit ..\build\build_dates.msbuild #define RMA_VERSION_MAJOR 8 -#define RMA_VERSION_MINOR 5 +#define RMA_VERSION_MINOR 6 //////////////////////////////////////////////////////////////// // @@ -14,9 +14,9 @@ // first step in each build. // #define RMA_VERSION_YEAR 2024 -#define RMA_VERSION_MONTH 3 -#define RMA_VERSION_DATE 12 -#define RMA_VERSION_HOUR 13 +#define RMA_VERSION_MONTH 4 +#define RMA_VERSION_DATE 2 +#define RMA_VERSION_HOUR 11 #define RMA_VERSION_MINUTE 0 //////////////////////////////////////////////////////////////// @@ -35,8 +35,8 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 8,5,24072,13000 -#define VERSION_WITH_PERIODS 8.5.24072.13000 +#define VERSION_WITH_COMMAS 8,6,24093,11000 +#define VERSION_WITH_PERIODS 8.6.24093.11000 #define COPYRIGHT "Copyright (C) 1993-2024, Robert McNeel & Associates. All Rights Reserved." #define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library." @@ -44,11 +44,11 @@ #define RMA_VERSION_NUMBER_MAJOR_WSTRING L"8" #define RMA_PREVIOUS_VERSION_NUMBER_MAJOR_WSTRING L"7" -#define RMA_VERSION_NUMBER_SR_STRING "SR5" -#define RMA_VERSION_NUMBER_SR_WSTRING L"SR5" +#define RMA_VERSION_NUMBER_SR_STRING "SR6" +#define RMA_VERSION_NUMBER_SR_WSTRING L"SR6" -#define RMA_VERSION_WITH_PERIODS_STRING "8.5.24072.13000" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.5.24072.13000" +#define RMA_VERSION_WITH_PERIODS_STRING "8.6.24093.11000" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.6.24093.11000" diff --git a/opennurbs_rand.cpp b/opennurbs_rand.cpp index d4e0241f..655737f6 100644 --- a/opennurbs_rand.cpp +++ b/opennurbs_rand.cpp @@ -428,3 +428,16 @@ void ON_RandomNumberGenerator::RandomPermutation(void* base, size_t nel, size_t } } +void ON_RandomNumberGenerator::TwoGaussians(double* u, double* v) +{ + if (!u || !v) return; + double t = RandomDouble(); + double s = RandomDouble(); + // if there is a problem with the random number generator, we don't want to crash the FPU in the logarithm + if (t < 1e-20 || t > 1.0) + t = .5; + double m = sqrt(-2.0 * log(t)); + *u = m * cos(ON_2PI * s); + *v = m * sin(ON_2PI * s); +} + diff --git a/opennurbs_rand.h b/opennurbs_rand.h index d5fa22a6..90a02089 100644 --- a/opennurbs_rand.h +++ b/opennurbs_rand.h @@ -184,6 +184,18 @@ public: */ void RandomPermutation(void* base, size_t nel, size_t sizeof_element ); + /* + Description: + Places two Gaussians with mean zero and standard devation one, + generated by a Box-Muller transform, into the doubles pointed to by its arguments. + /* + Parameters: + u - [out] + v - [out] + Returns: + */ + void TwoGaussians(double* u, double* v); + private: struct ON_RANDOM_NUMBER_CONTEXT m_rand_context; }; diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp index dc43e6e0..750ec677 100644 --- a/opennurbs_subd.cpp +++ b/opennurbs_subd.cpp @@ -2912,8 +2912,8 @@ const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromVer = (nullptr != v) ? ON_SubD_ComponentIdTypeAndTag::CreateFromVertexId(v->m_id, v->m_vertex_tag) : ON_SubD_ComponentIdTypeAndTag::Unset; - if (itt.m_id > 0) - itt.m_cptr = ON_SubDComponentPtr::Create(v); + //if (itt.m_id > 0) + // itt.m_cptr = ON_SubDComponentPtr::Create(v); return itt; } @@ -2924,7 +2924,7 @@ const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromVer { itt.m_id = vertex_id; itt.m_type = ON_SubDComponentPtr::Type::Vertex; - itt.m_tag = static_cast(vtag); + itt.Internal_SetTag(static_cast(vtag)); } return itt; } @@ -2933,10 +2933,18 @@ const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromEdg { ON_SubD_ComponentIdTypeAndTag itt = (nullptr != e) - ? ON_SubD_ComponentIdTypeAndTag::CreateFromEdgeId(e->m_id, e->m_edge_tag) + ? ON_SubD_ComponentIdTypeAndTag::CreateFromEdgeId(e->m_id, e->m_edge_tag, e->Sharpness(false)) : ON_SubD_ComponentIdTypeAndTag::Unset; - if (itt.m_id > 0) - itt.m_cptr = ON_SubDComponentPtr::Create(e); + //if (itt.m_id > 0) + // itt.m_cptr = ON_SubDComponentPtr::Create(e); + return itt; +} + +const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromEdge(const class ON_SubDEdgePtr eptr) +{ + ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::CreateFromEdge(eptr.Edge()); + if (ON_SubDEdgeTag::Unset != itt.EdgeTag()) + itt.Internal_SetDir(0 == eptr.EdgeDirection() ? 0 : 1); return itt; } @@ -2946,9 +2954,29 @@ const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromEdg ON_SubD_ComponentIdTypeAndTag itt; if (edge_id > 0) { + itt.m_sharpness = ON_SubDEdgeSharpness::Smooth; itt.m_id = edge_id; itt.m_type = ON_SubDComponentPtr::Type::Edge; - itt.m_tag = static_cast(ON_SubDEdgeTag::SmoothX == etag ? ON_SubDEdgeTag::Smooth : etag); + itt.Internal_SetTag(static_cast(ON_SubDEdgeTag::SmoothX == etag ? ON_SubDEdgeTag::Smooth : etag)); + } + return itt; +} + +const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromEdgeId(unsigned edge_id, ON_SubDEdgeTag etag, ON_SubDEdgeSharpness sharpness) +{ + ON_SubD_ComponentIdTypeAndTag itt = CreateFromEdgeId(edge_id, etag); + if (ON_SubDComponentPtr::Type::Edge == itt.m_type) + { + switch (itt.EdgeTag()) + { + case ON_SubDEdgeTag::Smooth: + case ON_SubDEdgeTag::SmoothX: + if (sharpness.IsSharp()) + itt.m_sharpness = sharpness; + break; + default: + break; + } } return itt; } @@ -2959,8 +2987,8 @@ const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromFac = (nullptr != f) ? ON_SubD_ComponentIdTypeAndTag::CreateFromFaceId(f->m_id, ftag) : ON_SubD_ComponentIdTypeAndTag::Unset; - if (itt.m_id > 0) - itt.m_cptr = ON_SubDComponentPtr::Create(f); + //if (itt.m_id > 0) + // itt.m_cptr = ON_SubDComponentPtr::Create(f); return itt; } @@ -2971,7 +2999,7 @@ const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromFac { itt.m_id = face_id; itt.m_type = ON_SubDComponentPtr::Type::Vertex; - itt.m_tag = ftag; + itt.Internal_SetTag(ftag); } return itt; } @@ -3004,68 +3032,73 @@ int ON_SubD_ComponentIdTypeAndTag::CompareTypeAndIdAndTag(const ON_SubD_Componen return 1; if (nullptr == rhs) return -1; - if (lhs->m_tag < rhs->m_tag) + const unsigned char lhs_tag = lhs->Internal_Tag(); + const unsigned char rhs_tag = rhs->Internal_Tag(); + if (lhs_tag < rhs_tag) return -1; - if (lhs->m_tag > rhs->m_tag) + if (lhs_tag > rhs_tag) return 1; return 0; } +const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::FindFromTypeAndId(ON_SubDComponentPtr::Type type, unsigned id, const ON_SimpleArray< ON_SubD_ComponentIdTypeAndTag>& sorted_tags) +{ + if (0 == id || ON_SubDComponentPtr::Type::Unset == type) + return ON_SubD_ComponentIdTypeAndTag::Unset; + ON_SubD_ComponentIdTypeAndTag itt; + itt.m_id = id; + itt.m_type = type; + const int i = sorted_tags.BinarySearch(&itt, ON_SubD_ComponentIdTypeAndTag::CompareTypeAndId); + return (i >= 0) ? sorted_tags[i] : ON_SubD_ComponentIdTypeAndTag::Unset; +} + ON_SubDVertexTag ON_SubD_ComponentIdTypeAndTag::OriginalVertexTag(unsigned vertex_id, const ON_SimpleArray< ON_SubD_ComponentIdTypeAndTag>& sorted_tags) { if (0 == vertex_id) return ON_SubDVertexTag::Unset; - const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::CreateFromVertexId(vertex_id,ON_SubDVertexTag::Unset); - const int i = sorted_tags.BinarySearch(&itt, ON_SubD_ComponentIdTypeAndTag::CompareTypeAndId); - return (i >= 0) ? sorted_tags[i].VertexTag() : ON_SubDVertexTag::Unset; + const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::FindFromTypeAndId(ON_SubDComponentPtr::Type::Vertex, vertex_id, sorted_tags); + return itt.VertexTag(); } ON_SubDVertexTag ON_SubD_ComponentIdTypeAndTag::OriginalVertexTag(const ON_SubDVertex* v, const ON_SimpleArray< ON_SubD_ComponentIdTypeAndTag>& sorted_tags) { if (nullptr == v) - return ON_SubDVertexTag::Unset; - const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::CreateFromVertexId(v->m_id,ON_SubDVertexTag::Unset); - const int i = sorted_tags.BinarySearch(&itt, ON_SubD_ComponentIdTypeAndTag::CompareTypeAndId); - return (i >= 0) ? sorted_tags[i].VertexTag() : v->m_vertex_tag; + return ON_SubD_ComponentIdTypeAndTag::OriginalVertexTag((unsigned)0, sorted_tags); + const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::FindFromTypeAndId(ON_SubDComponentPtr::Type::Vertex, v->m_id, sorted_tags); + return (ON_SubDComponentPtr::Type::Vertex == itt.m_type && v->m_id == itt.m_id) ? itt.VertexTag() : v->m_vertex_tag; } ON_SubDEdgeTag ON_SubD_ComponentIdTypeAndTag::OriginalEdgeTag(unsigned edge_id, const ON_SimpleArray< ON_SubD_ComponentIdTypeAndTag>& sorted_tags) { if (0 == edge_id) return ON_SubDEdgeTag::Unset; - const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::CreateFromEdgeId(edge_id, ON_SubDEdgeTag::Unset); - const int i = sorted_tags.BinarySearch(&itt, ON_SubD_ComponentIdTypeAndTag::CompareTypeAndId); - return (i >= 0) ? sorted_tags[i].EdgeTag() : ON_SubDEdgeTag::Unset; + const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::FindFromTypeAndId(ON_SubDComponentPtr::Type::Edge, edge_id, sorted_tags); + return itt.EdgeTag(); } - ON_SubDEdgeTag ON_SubD_ComponentIdTypeAndTag::OriginalEdgeTag(const ON_SubDEdge * e, const ON_SimpleArray< ON_SubD_ComponentIdTypeAndTag>&sorted_tags) { if (nullptr == e) - return ON_SubDEdgeTag::Unset; - const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::CreateFromEdgeId(e->m_id,ON_SubDEdgeTag::Unset); - const int i = sorted_tags.BinarySearch(&itt, ON_SubD_ComponentIdTypeAndTag::CompareTypeAndId); - return (i >= 0) ? sorted_tags[i].EdgeTag() : e->m_edge_tag; + return ON_SubD_ComponentIdTypeAndTag::OriginalEdgeTag((unsigned)0,sorted_tags); + const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::FindFromTypeAndId(ON_SubDComponentPtr::Type::Edge, e->m_id, sorted_tags); + return (ON_SubDComponentPtr::Type::Edge == itt.m_type && e->m_id == itt.m_id) ? itt.EdgeTag() : e->m_edge_tag; } unsigned char ON_SubD_ComponentIdTypeAndTag::OriginalFaceTag(unsigned face_id, const ON_SimpleArray< ON_SubD_ComponentIdTypeAndTag>& sorted_tags) { if (0 == face_id) return 0; - const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::CreateFromFaceId(face_id, 0); - const int i = sorted_tags.BinarySearch(&itt, ON_SubD_ComponentIdTypeAndTag::CompareTypeAndId); - return (i >= 0) ? sorted_tags[i].FaceTag() : 0; + const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::FindFromTypeAndId(ON_SubDComponentPtr::Type::Face, face_id, sorted_tags); + return itt.FaceTag(); } unsigned char ON_SubD_ComponentIdTypeAndTag::OriginalFaceTag(const ON_SubDFace* f, const ON_SimpleArray< ON_SubD_ComponentIdTypeAndTag>& sorted_tags) { if (nullptr == f) - return 0; - const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::CreateFromFaceId(f->m_id,0); - const int i = sorted_tags.BinarySearch(&itt, ON_SubD_ComponentIdTypeAndTag::CompareTypeAndId); - const unsigned char ftag = (i >= 0) ? sorted_tags[i].FaceTag() : 0; - return ftag; + return ON_SubD_ComponentIdTypeAndTag::OriginalFaceTag((unsigned)0, sorted_tags); + const ON_SubD_ComponentIdTypeAndTag itt = ON_SubD_ComponentIdTypeAndTag::FindFromTypeAndId(ON_SubDComponentPtr::Type::Face, f->m_id, sorted_tags); + return (ON_SubDComponentPtr::Type::Face == itt.m_type && f->m_id == itt.m_id) ? itt.FaceTag() : 0; } ON_SubDComponentPtr::Type ON_SubD_ComponentIdTypeAndTag::ComponentType() const @@ -3073,6 +3106,33 @@ ON_SubDComponentPtr::Type ON_SubD_ComponentIdTypeAndTag::ComponentType() const return m_type; } +unsigned char ON_SubD_ComponentIdTypeAndTag::Internal_Tag() const +{ + return (0x07 & m_tag_and_dirx); +} + +void ON_SubD_ComponentIdTypeAndTag::Internal_SetTag(unsigned char tag) +{ + const unsigned char dir = 0x80 & m_tag_and_dirx; + m_tag_and_dirx = (dir | (0x07 & tag)); +} + +// returns (0x80 & m_tag_and_dir) != 0 ? 1 : 0; +unsigned char ON_SubD_ComponentIdTypeAndTag::Internal_Dir() const +{ + return (0 == (0x80 & m_tag_and_dirx)) ? 0 : 1; +} + +void ON_SubD_ComponentIdTypeAndTag::Internal_SetDir(unsigned char dir) +{ + if (dir <= 1) + { + + const unsigned char tag = 0x07 & m_tag_and_dirx; + m_tag_and_dirx = (tag | ((dir != 0) ? 0x70 : 0x00)); + } +} + unsigned ON_SubD_ComponentIdTypeAndTag::VertexId() const { return (ON_SubDComponentPtr::Type::Vertex == m_type) ? m_id : 0; @@ -3083,6 +3143,38 @@ unsigned ON_SubD_ComponentIdTypeAndTag::EdgeId() const return (ON_SubDComponentPtr::Type::Edge == m_type) ? m_id : 0; } +const ON_SubDEdgeSharpness ON_SubD_ComponentIdTypeAndTag::EdgeSharpness(bool bUseCreaseSharpness) const +{ + switch (EdgeTag()) + { + case ON_SubDEdgeTag::Smooth: + case ON_SubDEdgeTag::SmoothX: + return m_sharpness; + break; + case ON_SubDEdgeTag::Crease: + if (bUseCreaseSharpness) + return ON_SubDEdgeSharpness::Crease; + break; + default: + break; + } + return ON_SubDEdgeSharpness::Nan; +} + +const double ON_SubD_ComponentIdTypeAndTag::VertexSharpness() const +{ + return + (ON_SubDComponentPtr::Type::Vertex == m_type && m_sharpness.IsConstant()) + ? m_sharpness[0] + : 0.0; +} + +void ON_SubD_ComponentIdTypeAndTag::SetVertexSharpness(double s) +{ + m_sharpness = ON_SubDEdgeSharpness::FromConstant(ON_SubDEdgeSharpness::IsValidValue(s, false) ? s : 0.0); +} + + unsigned ON_SubD_ComponentIdTypeAndTag::FaceId() const { return (ON_SubDComponentPtr::Type::Face == m_type) ? m_id : 0; @@ -3090,17 +3182,17 @@ unsigned ON_SubD_ComponentIdTypeAndTag::FaceId() const ON_SubDVertexTag ON_SubD_ComponentIdTypeAndTag::VertexTag() const { - return (ON_SubDComponentPtr::Type::Vertex == m_type) ? ON_SubD::VertexTagFromUnsigned(m_tag) : ON_SubDVertexTag::Unset; + return (ON_SubDComponentPtr::Type::Vertex == m_type) ? ON_SubD::VertexTagFromUnsigned(this->Internal_Tag()) : ON_SubDVertexTag::Unset; } ON_SubDEdgeTag ON_SubD_ComponentIdTypeAndTag::EdgeTag() const { - return (ON_SubDComponentPtr::Type::Edge == m_type) ? ON_SubD::EdgeTagFromUnsigned(m_tag) : ON_SubDEdgeTag::Unset; + return (ON_SubDComponentPtr::Type::Edge == m_type) ? ON_SubD::EdgeTagFromUnsigned(this->Internal_Tag()) : ON_SubDEdgeTag::Unset; } unsigned char ON_SubD_ComponentIdTypeAndTag::FaceTag() const { - return (ON_SubDComponentPtr::Type::Face == m_type) ? m_tag : 0; + return (ON_SubDComponentPtr::Type::Face == m_type) ? this->Internal_Tag() : 0; } ////////////////////////////////////////////////////////////////////////// @@ -3702,6 +3794,16 @@ bool ON_SubDVertex::IsCrease() const return (ON_SubDVertexTag::Crease == m_vertex_tag); } +bool ON_SubDVertex::IsOneSectorCrease() const +{ + return (ON_SubDVertexTag::Crease == m_vertex_tag && m_edge_count == m_face_count+1 && m_face_count >= 1); +} + +bool ON_SubDVertex::IsTwoSectorCrease() const +{ + return (ON_SubDVertexTag::Crease == m_vertex_tag && m_edge_count == m_face_count && m_face_count >= 2); +} + bool ON_SubDVertex::IsCorner() const { return (ON_SubDVertexTag::Corner == m_vertex_tag); @@ -4355,7 +4457,8 @@ const ON_SubDEdgeSharpness ON_SubDEdgeSharpness::FromInterval( double sharpness1 ) { - if (sharpness0 >= 0.0 && sharpness0 <= ON_SubDEdgeSharpness::MaximumValue && sharpness1 >= 0.0 && sharpness1 <= ON_SubDEdgeSharpness::MaximumValue) + if (sharpness0 >= 0.0 && sharpness0 <= ON_SubDEdgeSharpness::MaximumValue + && sharpness1 >= 0.0 && sharpness1 <= ON_SubDEdgeSharpness::MaximumValue) { ON_SubDEdgeSharpness s; s.m_edge_sharpness[0] = (float)ON_SubDEdgeSharpness::Sanitize(sharpness0, 0.0); @@ -4528,7 +4631,7 @@ double ON_SubDEdgeSharpness::VertexSharpness( double ON_SubDEdgeSharpness::VertexSharpness( ON_SubDVertexTag vertex_tag, - double global_vertex_sharpness, + double interior_crease_vertex_sharpness, unsigned sharp_edge_end_count, double maximum_edge_end_sharpness ) @@ -4555,21 +4658,30 @@ double ON_SubDEdgeSharpness::VertexSharpness( } else if (ON_SubDVertexTag::Crease == vertex_tag) { - if (global_vertex_sharpness > 0.0) + if (interior_crease_vertex_sharpness > 0.0) { + // In the comments below, + // VSS = vertex sector sharpness = maximum edges sharpness + // at the vertex's end of all smooth edges in the sector. if (sharp_edge_end_count <= 0) { - // no edges have nonzero end sharpness at this vertex - // If this isn't the tricky 2 sector crease vertex case, then the vertex sharpness is zero. - return global_vertex_sharpness; + // No edges have nonzero end sharpness at this vertex + // It should be the case that the origin of this vertex + // is an interior crease vertex where: + // 1) One sector (A) had sharp edges and VSS > 0. + // 2) The other sector (B) had no sharp edges and VSS = 0. + // 3) This vertex is part of a single sector B copy (or a subdivision of such a copy). + return interior_crease_vertex_sharpness; } - if (global_vertex_sharpness > maximum_edge_end_sharpness) + if (interior_crease_vertex_sharpness > maximum_edge_end_sharpness) { - // This happens when the sharp edges attached to the vertex - // have variable sharpness with 0 at the vertex - // and nonzero sharpness at the other ends. - return global_vertex_sharpness; + // It should be the case that the origin of this vertex + // is an interior crease vertex where: + // 1) Both sectors had sharp edges and the VSS of the sectors was different (one could be zero). + // 2) This vertex is part of a single sector copy of the sector with + // the smaller VSS (or a subdivision of such a copy). + return interior_crease_vertex_sharpness; } } @@ -4587,7 +4699,6 @@ double ON_SubDEdgeSharpness::VertexSharpness( if (sharp_edge_end_count + crease_edge_count < 2U) { - // 1 sharp edge and no creases // No sharp bias for the vertex subdivision point. return 0.0; } @@ -4766,26 +4877,154 @@ unsigned int ON_SubD::ClearEdgeSharpness() } -double ON_SubDVertex::Internal_CreaseSectorVertexSharpnessForExperts() const +double ON_SubDVertex::Internal_InteriorCreaseVertexSharpnessForExperts() const { - return (ON_SubDVertexTag::Crease == m_vertex_tag) ? m_crease_sector_vertex_sharpness : 0.0; + // This function cannot check that this->IsInteriorCrease() is true because + // this function is used on partial subsets of a SubD in low level evaluation code. + // When a vertex is an interior crease in the complete SubD, these partial + // SubD include only one of the sectors. Thus, all that can be checked is the + // crease tag settings + return (ON_SubDVertexTag::Crease == m_vertex_tag) ? m_interior_crease_vertex_sharpness : 0.0; } -void ON_SubDVertex::Internal_UpdateCreaseSectorVertexSharpnessForExperts(double crease_sector_vertex_sharpness) const +void ON_SubDVertex::Internal_SetInteriorCreaseVertexSharpnessForExperts( + double interior_crease_vertex_sharpness, + bool bSkipEdgeCountTest +) const { // NOTE WELL: - // - if (ON_SubDVertexTag::Crease == m_vertex_tag && crease_sector_vertex_sharpness > 0.0 && ON_SubDEdgeSharpness::IsValidValue(crease_sector_vertex_sharpness,false)) - this->m_crease_sector_vertex_sharpness = (float)crease_sector_vertex_sharpness; + // This function is used to set m_crease_sector_vertex_sharpness when the vertex is part + // of a complete SubD. + if (ON_SubDVertexTag::Crease == m_vertex_tag + && interior_crease_vertex_sharpness > 0.0 + && ON_SubDEdgeSharpness::IsValidValue(interior_crease_vertex_sharpness, false) + ) + { + // nonzero vertex sharpness on a crease vertex. + // Because, even with the bSingleSectorSubset parameter, it's impossible to know the context + // when ON_SubDVertex.VertexSharpness() is called (complete SubD or singel sector subset) + // we test for a change to the value before setting it so debugger breakpoints can + // be set and triggered at useful times. + const float f = (float)interior_crease_vertex_sharpness; + if (m_edge_count == m_face_count && m_face_count >= 2) + { + // This is a 2 sector crease vertex and is exactly + // the case where m_crease_sector_vertex_sharpness is useful. + // If the sectors have different maximum edge end shaprnesses at this + // vertex, then this value is critical. The sector with the smaller + // maximum edge end sharpness must use this value when the + // vertex subdivision point is calculated so the subdivision + // and limit surface point are correct when calculated from + // either sector. + this->m_interior_crease_vertex_sharpness = f; + } + else + { + const bool bOneSector = m_edge_count == m_face_count + 1 && m_face_count >= 1; + if (bOneSector && m_level > 0) + { + // The situation should be that the calling code knows for sure + // this is a 1 sector crease vertex and that sector is a + // subset taken from a 2 sector crease vertex (in low level evaluation code) + // and interior_crease_vertex_sharpness was calculated using both sectors + // (or is a subdivision of a value calculated using both sectors). + // One place this happens is in ON_SubDVertexQuadSector::Subdivide(). + // This is a good location for a breakpoint. + this->m_interior_crease_vertex_sharpness = f; + } + else if (bSkipEdgeCountTest) + { + // When bSkipEdgeCountTest = true, the expert caller is stating + // this is some type of special case when the vertex isn't completely + // connected the the entire SubD. It is typically at some stage of subdivision + // where either a the subdivided SubD is under construction or + // during low level evaluation where a copy of 1 side of a 2 sector crease + // is being created so limit surface information can be calculated. + // + // Each case is separated out so it's posible to surgically debug with simple breakpoints. + if (bOneSector) + { + // One sector of a level 0 bertex is being copied and the expert caller + // set bSkipEdgeCountTest=true to inform us this is a special case. + // One place this happens is in + // ON_SubDVertexQuadSector::Initialize(ON_SubDVertexTag center_vertex_tag, double center_vertex_sharpness, ...). + this->m_interior_crease_vertex_sharpness = f; + } + else if (0 == m_edge_count && 0 == m_face_count) + { + // A vertex is being bopied, the edges and faces have not been attached yet, + // and the expert caller set bSkipEdgeCountTest=true to inform us this is a + // special case. + // One place this happens is in + // ON_SubD_FixedSizeHeap::AllocateVertex(ON_SubDVertex* vertex0,unsigned int edge_capacity) + // where the subdivision vertex of vertex0 is being created and the edges and faces + // have not been allocated (and hence not attached) yet. + // Good place for a breakpoint. + this->m_interior_crease_vertex_sharpness = f; + } + else + { + // If you end up here and it's a valid state, add a comment. + // If it's confusing, please ask Dale Lear for some help. + ON_ERROR("This probably should not be happening."); + // Nothing is accomplished by setting this value and + // having it set can cause errors when sharpnesses are edited. + // Good place for a breakpoint. + this->Internal_ClearInteriorCreaseVertexSharpnessForExperts(); + } + } + } + } else - this->m_crease_sector_vertex_sharpness = 0.0f; + this->Internal_ClearInteriorCreaseVertexSharpnessForExperts(); +} + +void ON_SubDVertex::Internal_ClearInteriorCreaseVertexSharpnessForExperts() const +{ + // NO tests - set m_interior_crease_vertex_sharpness = 0 unconditionally + this->m_interior_crease_vertex_sharpness = 0.0f; +} + +static double Internal_VertexSharpnessCalculationHelper( + const ON_SubDVertex* v, // caller insures v is not nullptr + unsigned int sharp_edge_count, // caller insures sharp_edge_count is valid + double max_end_sharpeness // caller insures max_end_sharpeness is valid +) +{ + // In low level evluation code, interior crease vertices + // only have informtion from one sector and we need the value + // calculated from both sectors. In all other cases, the + // value of max_end_sharpeness calculated above is the + // correct value to use. + const bool bIsOneSectorCrease = v->IsOneSectorCrease(); + const double two_sector_crease_vertex_sharpness + = bIsOneSectorCrease + ? v->Internal_InteriorCreaseVertexSharpnessForExperts() + : 0.0; + const double vertex_sharpness = ON_SubDEdgeSharpness::VertexSharpness(v->m_vertex_tag, two_sector_crease_vertex_sharpness, sharp_edge_count, max_end_sharpeness); + if (false == bIsOneSectorCrease) + { + if (vertex_sharpness > 0.0 && v->IsTwoSectorCrease()) + { + // This interior crease vertex has both sectors present and the value + // of vertex_sharpness can be saved for future situations where + // single sector copies are used in low level evaluation code. + v->Internal_SetInteriorCreaseVertexSharpnessForExperts(vertex_sharpness, false); + } + else + v->Internal_ClearInteriorCreaseVertexSharpnessForExperts(); + } + // else DO NOTHING - THIS IS IMPORTANT + // The 1 sector case might be low level code passing in copy of just one of two sectors + // and, at this point, there is not enough information to determine the correct action. + return vertex_sharpness; } double ON_SubDVertex::VertexSharpness() const { // NOTE WELL: - // For performance reasone in calculating limit surface meshes, + // For performance reasons in calculating limit surface meshes, // the code in this function does the same calculation and // returns the same value as // @@ -4818,14 +5057,11 @@ double ON_SubDVertex::VertexSharpness() const } } - // In the low level evaluation code, this function is called - // when the sector center vertex is the original in the entire - // SubD. That insures crease_sector_vertex_sharpness is accurate. - // It's updated on the original SubD so that when the sector subset - // is created, the value is copied. - const double crease_sector_vertex_sharpness = this->Internal_CreaseSectorVertexSharpnessForExperts(); - vertex_sharpness = ON_SubDEdgeSharpness::VertexSharpness(m_vertex_tag, crease_sector_vertex_sharpness, sharp_edge_count, max_end_sharpeness); - this->Internal_UpdateCreaseSectorVertexSharpnessForExperts(vertex_sharpness); + vertex_sharpness = Internal_VertexSharpnessCalculationHelper( + this, + sharp_edge_count, + max_end_sharpeness + ); } else { @@ -4961,14 +5197,12 @@ double ON_SubDVertex::GetSharpSubdivisionPoint( } } - // In the low level evaluation code, this function is called - // when the sector center vertex is the original in the entire - // SubD. That insures crease_sector_vertex_sharpness is accurate. - // It's updated on the original SubD so that when the sector subset - // is created, the value is copied. - const double crease_sector_vertex_sharpness = this->Internal_CreaseSectorVertexSharpnessForExperts(); - const double vertex_sharpness = ON_SubDEdgeSharpness::VertexSharpness(m_vertex_tag, crease_sector_vertex_sharpness, sharp_edge_count, max_end_sharpeness); - this->Internal_UpdateCreaseSectorVertexSharpnessForExperts(vertex_sharpness); + const double vertex_sharpness = Internal_VertexSharpnessCalculationHelper( + this, + sharp_edge_count, + max_end_sharpeness + ); + if (vertex_sharpness > 0.0) { @@ -8604,6 +8838,7 @@ bool ON_SubDimple::IsValidLevel( } } + return true; } @@ -8656,12 +8891,23 @@ bool ON_SubD::IsValid(ON_TextLog* text_logx) const void ON_SubD::Dump(ON_TextLog& text_log) const { // At maximum verbosity, dump all vertices, edges, faces, else dump the first 16. - const unsigned component_sample_count = text_log.LevelOfDetailIsAtLeast(ON_TextLog::LevelOfDetail::Maximum) ? 0x7FFFFFFF : 16; + const unsigned maxcount = 0x7FFFFFFF; - ON_2udex id_range; - id_range.i = component_sample_count; - id_range.j = 0; - DumpTopology(id_range,id_range,id_range,text_log); + const unsigned face_count = text_log.LevelOfDetailIsAtLeast(ON_TextLog::LevelOfDetail::Maximum) ? maxcount : 16; + + // Bump vertex_count up a bit to deal with small models with lots of hexagons. + const unsigned vertex_count = face_count < maxcount ? (3 * face_count) / 2 : maxcount; + + // And ... edge_count is set this way because in a typical control net, + // vertex count - edge count + face count is nearish to zero. + // (Euler's formula) + const unsigned edge_count = face_count < maxcount ? (vertex_count + face_count + 8) : maxcount; + + const ON_2udex vertex_id_range(vertex_count, 0); + const ON_2udex edge_id_range(edge_count, 0); + const ON_2udex face_id_range(face_count, 0); + + DumpTopology(vertex_id_range,edge_id_range,face_id_range,text_log); } unsigned int ON_SubD::DumpTopology(ON_TextLog & text_log) const @@ -10060,6 +10306,13 @@ unsigned int ON_SubDLevel::DumpTopology( text_log.PushIndent(); + const double vs = v->VertexSharpness(); + if (false == (0.0 == vs)) + { + const ON_wString vspct = ON_SubDEdgeSharpness::ToPercentageText(vs); + text_log.Print(L"v.VertexSharpness = %ls (%g)\n", static_cast(vspct), vs); + } + ON_3dPoint P1(ON_3dPoint::NanPoint); if (v->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid()) text_log.Print( "v.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z ); @@ -10287,6 +10540,18 @@ unsigned int ON_SubDLevel::DumpTopology( text_log.PushIndent(); + if (e->IsSharp()) + { + const ON_SubDEdgeSharpness sharpness = e->Sharpness(false); + const ON_wString ss = sharpness.ToPercentageText(false); + ON_wString s + = (sharpness.IsConstant()) + ? ON_String::FormatToString("e.Sharpness: %ls (%g)", static_cast(ss), sharpness.EndSharpness(0)) + : ON_String::FormatToString("e.Sharpness: %ls (%g-%g)", static_cast(ss), sharpness.EndSharpness(0), sharpness.EndSharpness(1)); + text_log.PrintString(s); + text_log.PrintNewLine(); + } + ON_3dPoint P1(ON_3dPoint::NanPoint); if (e->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid()) text_log.Print( "e.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z ); @@ -11690,6 +11955,29 @@ ON_SubDEdge* ON_SubD::AddEdgeWithSectorCoefficients( return ON_SUBD_RETURN_ERROR(nullptr); } +ON_SubDEdge* ON_SubD::AddEdgeWithSectorCoefficients( + ON_SubDEdgeTag edge_tag, + class ON_SubDVertex* v0, + double v0_sector_coefficient, + class ON_SubDVertex* v1, + double v1_sector_coefficient, + ON_SubDEdgeSharpness sharpness +) +{ + ON_SubDimple* subdimple = SubDimple(true); + if (nullptr != subdimple) + { + ON_SubDEdge* e = subdimple->AddEdge(edge_tag, v0, v0_sector_coefficient, v1, v1_sector_coefficient); + if (nullptr != e) + { + if (e->IsSmooth()) + e->SetSharpnessForExperts(sharpness); + return e; + } + } + return ON_SUBD_RETURN_ERROR(nullptr); +} + class ON_SubDEdge* ON_SubD::AddEdgeForExperts( unsigned int candidate_edge_id, ON_SubDEdgeTag edge_tag, @@ -11706,6 +11994,31 @@ class ON_SubDEdge* ON_SubD::AddEdgeForExperts( return ON_SUBD_RETURN_ERROR(nullptr); } +ON_SubDEdge* ON_SubD::AddEdgeForExperts( + unsigned int candidate_edge_id, + ON_SubDEdgeTag edge_tag, + class ON_SubDVertex* v0, + double v0_sector_coefficient, + class ON_SubDVertex* v1, + double v1_sector_coefficient, + ON_SubDEdgeSharpness sharpness, + unsigned int initial_face_capacity +) +{ + ON_SubDimple* subdimple = SubDimple(true); + if (nullptr != subdimple) + { + ON_SubDEdge* e = subdimple->AddEdge(candidate_edge_id, edge_tag, v0, v0_sector_coefficient, v1, v1_sector_coefficient, initial_face_capacity); + if (nullptr != e) + { + if (e->IsSmooth()) + e->SetSharpnessForExperts(sharpness); + return e; + } + } + return ON_SUBD_RETURN_ERROR(nullptr); +} + bool ON_SubD::ReturnEdgeForExperts( ON_SubDEdge* e ) @@ -13709,8 +14022,8 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ // sharp crease vertex with 0 < sharpness < 1 at the current level const double a = 1.0 - edge_sharpness; subdivision_point[0] = a * subdivision_point[0] + edge_sharpness * sharp_subdivision_point.x; - subdivision_point[1] = a * subdivision_point[1] + edge_sharpness * sharp_subdivision_point.x; - subdivision_point[2] = a * subdivision_point[2] + edge_sharpness * sharp_subdivision_point.x; + subdivision_point[1] = a * subdivision_point[1] + edge_sharpness * sharp_subdivision_point.y; + subdivision_point[2] = a * subdivision_point[2] + edge_sharpness * sharp_subdivision_point.z; } return true; } @@ -14464,11 +14777,8 @@ bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint( { // We found the two crease edges that share this crease vertex. ON_3dPoint sharp_subdivision_point = ON_3dPoint::NanPoint; - - const double crease_sector_vertex_sharpness = this->Internal_CreaseSectorVertexSharpnessForExperts(); - const double vertex_sharpness - = (bSharpEdges || crease_sector_vertex_sharpness > 0.0) + = (bSharpEdges || this->Internal_InteriorCreaseVertexSharpnessForExperts() > 0.0) ? this->GetSharpSubdivisionPoint(sharp_subdivision_point) : 0.0; if (vertex_sharpness >= 1.0) @@ -16495,6 +16805,8 @@ const ON_SubDEdge* ON_SubDimple::SplitEdge( break; } + const ON_SubDEdgeSharpness sharpness0 = edge->Sharpness(false); + ON_SubDVertex* end_vertex[2] = { const_cast(edge->m_vertex[0]), const_cast(edge->m_vertex[1]) }; ON_SubDVertex* new_vertex = nullptr; @@ -16614,6 +16926,16 @@ const ON_SubDEdge* ON_SubDimple::SplitEdge( AddVertexToLevel(new_vertex); AddEdgeToLevel(new_edge); + if (sharpness0.IsSharp() && edge->IsSmooth() && new_edge->IsSmooth()) + { + // Dale Lear 2024 Feb 29 - Fix RH-80388 + const double smid = sharpness0.Average(); + const ON_SubDEdgeSharpness s0 = ON_SubDEdgeSharpness::FromInterval(sharpness0[0], smid); + const ON_SubDEdgeSharpness s1 = ON_SubDEdgeSharpness::FromInterval(smid, sharpness0[1]); + edge->SetSharpnessForExperts(s0); + new_edge->SetSharpnessForExperts(s1); + } + end_vertex[0]->VertexModifiedNofification(); end_vertex[1]->VertexModifiedNofification(); @@ -19886,7 +20208,10 @@ bool ON_SubDVertexQuadSector::Initialize( } this->SetCenterVertexSharpness(center_vertex_sharpness); - m_v[0].Internal_UpdateCreaseSectorVertexSharpnessForExperts(this->CenterVertexSharpness()); + if (ON_SubDVertexTag::Crease == m_v[0].m_vertex_tag) + m_v[0].Internal_SetInteriorCreaseVertexSharpnessForExperts(this->CenterVertexSharpness(), true); + else + m_v[0].Internal_ClearInteriorCreaseVertexSharpnessForExperts(); m_r[0] = ON_SubDComponentPtr::Create(&m_v[0]); for (unsigned ri = 1u; ri < sector_vertex_count; ri += 2u) @@ -20393,9 +20718,7 @@ bool ON_SubDVertexQuadSector::Subdivide() R[2U + 2U * fi] = m_f[fi].SubdivisionPoint(); } - const double crease_sector_vertex_sharpness0 = m_v[0].Internal_CreaseSectorVertexSharpnessForExperts(); - - const double center_vertex_sharpness0 = this->CenterVertexSharpness(); + const double crease_sector_vertex_sharpness0 = m_v[0].Internal_InteriorCreaseVertexSharpnessForExperts(); double center_vertex_sharpness1 = 0.0; @@ -20404,7 +20727,6 @@ bool ON_SubDVertexQuadSector::Subdivide() for (unsigned vi = 0; vi < sector_vertex_count; ++vi) { m_v[vi].ClearSavedSubdivisionPoints(); // <- 2023-May-5 Dale Lear. This line fixes RH-74520. - m_v[vi].Internal_UpdateCreaseSectorVertexSharpnessForExperts(0.0); m_v[vi].SetControlNetPoint(R[vi],false); m_v[vi].SetSubdivisionLevel(subdivision_level); if (1 == vi) @@ -20423,7 +20745,7 @@ bool ON_SubDVertexQuadSector::Subdivide() const double crease_sector_vertex_sharpness1 = ON_SubDEdgeSharpness::Sanitize(crease_sector_vertex_sharpness0 - 1.0); if (crease_sector_vertex_sharpness1 > 0.0) { - m_v[0].Internal_UpdateCreaseSectorVertexSharpnessForExperts(crease_sector_vertex_sharpness1); + m_v[0].Internal_SetInteriorCreaseVertexSharpnessForExperts(crease_sector_vertex_sharpness1, false); if (center_vertex_sharpness1 < crease_sector_vertex_sharpness1) center_vertex_sharpness1 = crease_sector_vertex_sharpness1; } diff --git a/opennurbs_subd.h b/opennurbs_subd.h index 380b03ed..f4fbe919 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -820,7 +820,7 @@ public: /// Sharpness or ON_DBL_QNAN if end_index is out of range. double EndSharpness(int end_index) const; - ON_DEPRECATED_MSG("Use the version with a global_vertex_sharpness parameter.") + ON_DEPRECATED_MSG("Use the version with an interior_crease_vertex_sharpness parameter.") static double VertexSharpness( ON_SubDVertexTag vertex_tag, unsigned sharp_edge_end_count, @@ -837,14 +837,15 @@ public: /// For smooth, crease, and dart, this is used to determine the number of attached crease edges. /// COrner vertices always have sharpness = 0. /// - /// - /// The cases where maximum_edge_end_sharpness arise only when the vertex_tag is crease, - /// a subset of a SubD is being used to calculate maximum_edge_end_sharpness, - /// the crease vertex has two sectors and the maximum_edge_end_sharpness is different - /// for the two sectors, and only one sector of a 2 sector crease vertex is in the subset. - /// This is a very specialized situation thot occurs only in low level SubD evaluation - /// code. In all other cases, this value doesn't matter as long - /// as it is 0 <= global_vertex_sharpness <= maximum_edge_end_sharpness. + /// + /// If the original source of the vertex is an interior crease vertex + /// (vertex_tag = ON_SubDVertexTag::Crease, 2 sectors, EdgeCount() = FaceCount() >= 2), + /// then interior_crease_vertex_sharpness is the maximum edge sharpness at the + /// vertex's end of all smooth edges from both sectors. + /// This paramter is important in special situations that occur + /// in low level SubD evaluation code where information from only one + /// sector is present. In all other cases, this value doesn't matter as long + /// as <= global_vertex_sharpness <= maximum_edge_end_sharpness. /// When in doubt pass 0.0. /// /// @@ -857,12 +858,11 @@ public: /// static double VertexSharpness( ON_SubDVertexTag vertex_tag, - double global_vertex_sharpness, + double interior_crease_vertex_sharpness, unsigned sharp_edge_end_count, double maximum_edge_end_sharpness ); - /// /// Verify 0 <= sharpness <= ON_SubDEdgeSharpness::MaximumValue and return an integer value when /// the input sharpenss is within ON_SubDEdgeSharpness::Tolerance of an integer. @@ -3006,8 +3006,25 @@ class ON_CLASS ON_SubDComponentPtrPairHashTable : ON_Hash32Table public: ON_SubDComponentPtrPairHashTable(); + /// + /// This is a good choice of constructor when you have a good estimate of + /// the number of pairs that will be in the hash table. + /// + /// + /// A good estimate of the number of pairs that will be in this hash table. + /// + ON_SubDComponentPtrPairHashTable(size_t pair_count_estimate); + + + /// + /// This is a good choice of constructor when the hash table is used + /// to find pairs of vertices and edges. + /// + /// + /// subd.VertexCount() + subd.EdgeCount() is used as the pair count estimate. + /// ON_SubDComponentPtrPairHashTable(const class ON_SubD& subd); - + ~ON_SubDComponentPtrPairHashTable() = default; bool AddComponentPair( @@ -3047,6 +3064,10 @@ public: const class ON_SubDEdge* second_e ); + const ON_SubDComponentPtrPair PairFromSecondFace( + const class ON_SubDFace* second_f + ); + private: ON_FixedSizePool m_pairs_fsp; @@ -3088,6 +3109,16 @@ public: */ static const ON_SubD_ComponentIdTypeAndTag CreateFromEdge(const class ON_SubDEdge* e); + /* + Parameters: + eptr - [in] + If e->m_edge_tag is ON_SubDEdgeTag::SmoothX, the ON_SubD_ComponentIdTypeAndTag EdgeTag() will be ON_SubDEdgeTag::Smoooth. + Returns: + If e is not nullptr and e->m_id > 0, a ON_SubD_ComponentIdTypeAndTag with EdgeTag() = e->m_edge_tag is returned. + Otherwise ON_SubD_ComponentIdTypeAndTag::Unset is returned. + */ + static const ON_SubD_ComponentIdTypeAndTag CreateFromEdge(const class ON_SubDEdgePtr eptr); + /* Parameters: @@ -3100,6 +3131,17 @@ public: */ static const ON_SubD_ComponentIdTypeAndTag CreateFromEdgeId(unsigned edge_id, ON_SubDEdgeTag etag); + /* + Parameters: + edge_id - [in] + etag - [in] + If etag is ON_SubDEdgeTag::SmoothX, the ON_SubD_ComponentIdTypeAndTag EdgeTag() will be ON_SubDEdgeTag::Smoooth. + Returns: + If edge_id > 0, a ON_SubD_ComponentIdTypeAndTag with EdgeTag() = etag is returned. + Otherwise ON_SubD_ComponentIdTypeAndTag::Unset is returned. + */ + static const ON_SubD_ComponentIdTypeAndTag CreateFromEdgeId(unsigned edge_id, ON_SubDEdgeTag etag, ON_SubDEdgeSharpness sharpness); + /* Parameters: @@ -3119,7 +3161,7 @@ public: Parameters: face_id - [in] ftag - [in] - Any value and the interpretation is up to the context using the ON_SubD_ComponentIdTypeAndTag. + Any value from 0 to 7. The interpretation is up to the context using the ON_SubD_ComponentIdTypeAndTag. Returns: If face_id > 0, a ON_SubD_ComponentIdTypeAndTag with FaceTag() = vtag is returned. Otherwise ON_SubD_ComponentIdTypeAndTag::Unset is returned. @@ -3141,6 +3183,8 @@ public: */ static int CompareTypeAndIdAndTag(const ON_SubD_ComponentIdTypeAndTag* lhs, const ON_SubD_ComponentIdTypeAndTag* rhs); + static const ON_SubD_ComponentIdTypeAndTag FindFromTypeAndId(ON_SubDComponentPtr::Type type, unsigned id, const ON_SimpleArray< ON_SubD_ComponentIdTypeAndTag>& sorted_tags); + /* Parameters: v - [in] @@ -3239,6 +3283,11 @@ public: unsigned EdgeId() const; + const ON_SubDEdgeSharpness EdgeSharpness(bool bUseCreaseSharpness) const; + + const double VertexSharpness() const; + void SetVertexSharpness(double s); + unsigned char FaceTag() const; unsigned FaceId() const; @@ -3246,11 +3295,34 @@ public: const ON_wString ToString() const; private: - ON_SubDComponentPtr m_cptr = ON_SubDComponentPtr::Null; + // Dale lear 2024 Feb 20 + // Replaced unsued + // ON_SubDComponentPtr m_cptr + // with + // ON_SubDEdgeSharpness m_sharpness. + // 8 = sizeof(ON_SubDComponentPtr) = ON_SubDEdgeSharpness(ON_SubDEdgeSharpness), + // so this switch does not change the sizeof(ON_SubD_ComponentIdTypeAndTag) + // and everything involved is a private member. + // Thus, this change does not "break the SDK." + ON_SubDEdgeSharpness m_sharpness = ON_SubDEdgeSharpness::Smooth; unsigned m_id = 0; ON_SubDComponentPtr::Type m_type = ON_SubDComponentPtr::Type::Unset; - unsigned char m_tag = 0; - unsigned char m_bits = 0; + // tag = (0x07 & m_tag_and_dir). + // dir = (0x80 & dir) ? 1 : 0; 1 = reversed. + unsigned char m_tag_and_dirx = 0; + + // returns (0x07 & m_tag_and_dir); + unsigned char Internal_Tag() const; + + void Internal_SetTag(unsigned char tag); + + // returns (0x80 & m_tag_and_dir) != 0 ? 1 : 0; + unsigned char Internal_Dir() const; + + void Internal_SetDir(unsigned char dir); + + + unsigned short m_reserved = 0; }; #if defined(ON_DLL_TEMPLATE) @@ -7224,6 +7296,15 @@ public: double v1_sector_coefficient ); + class ON_SubDEdge* AddEdgeWithSectorCoefficients( + ON_SubDEdgeTag edge_tag, + class ON_SubDVertex* v0, + double v0_sector_coefficient, + class ON_SubDVertex* v1, + double v1_sector_coefficient, + ON_SubDEdgeSharpness sharpness + ); + /* Description: Expert user tool to add an edge with specified information. This function @@ -7256,6 +7337,17 @@ public: unsigned int initial_face_capacity ); + class ON_SubDEdge* AddEdgeForExperts( + unsigned int candidate_edge_id, + ON_SubDEdgeTag edge_tag, + class ON_SubDVertex* v0, + double v0_sector_coefficient, + class ON_SubDVertex* v1, + double v1_sector_coefficient, + ON_SubDEdgeSharpness sharpness, + unsigned int initial_face_capacity + ); + /* Parameters: e - [in] @@ -12931,52 +13023,77 @@ private: unsigned short m_reserved2 = 0; private: - // The vertex sharpness is the maximum edge end sharpenss of the - // ends attached to the vertex. When a SubD is complete, - // all edges are present and the value can be calculated as needed. - // - // In low level subdivision point evaluation code, a subset of the SubD containing - // only the components from the sector being evaluated is used. There is one situation - // where m_crease_sector_vertex_sharpness needs to be set to properly - // evaluate the subdivision points and limit points. - // 1) The vertex is an interior crease - // (vertex has a crease tag and 2 sectors - implies both crease will have 2 faces). - // 2) The sharp edges in the two sectors generate different vertex sharpnesses. - // Again, this only occurs in low level evauation code. - // This value should be zero or be the same as the maximum sharpness - // of all smooth edge ends at this vertex in the complete SubD. - // The value is mutable because it is updated in the case above - // at appropriate times in the evaluation calculation. - // If per vertex sharpness is ever added, that value must be stored someplace else - // as, depending on the initial sharpness configuration, this value can be - // larger, smaller, or equal to a per vertex sharpness. - mutable float m_crease_sector_vertex_sharpness = 0.0; - + /// The level 0 interior crease vertex sharpness is the maximum edge end sharpenss + /// at the vertex of all smooth edges in both sectors of an interior crease vertex. + /// It can also be set by subdividing a previous level's value. + /// + /// In low level subdivision point evaluation code, a subset of the SubD containing + /// only the components from the sector being evaluated is used. There is one situation + /// where m_interior_crease_vertex_sharpness needs to be set to properly + /// evaluate the subdivision points and limit points. + /// 1) The vertex is an interior crease. + /// (The vertex has a crease tag and 2 sectors. + /// This implies both crease edges have 2 faces and the vertex's m_edge_count = m_face_count >= 2). + /// 2) The sharp edges in the two sectors generate different vertex sharpnesses. + /// Again, this only occurs in low level evauation code. + /// This value should be zero or be the same as the maximum sharpness + /// of all smooth edge ends at this vertex in the complete SubD. + /// The value is mutable because it is updated in the case above + /// at appropriate times in the evaluation calculation. + /// If per vertex sharpness is ever added, that value must be stored someplace else + /// as, depending on the initial sharpness configuration, this value can be + /// larger, smaller, or equal to a per vertex sharpness. + mutable float m_interior_crease_vertex_sharpness = 0.0; private: #if defined(ON_COMPILING_OPENNURBS) public: /// - /// This function is for use in ON_SubDVertexQuadSector - /// when a subset of a SubD is being used - /// to calculate a global subdivision point. - /// Always use the tools that set edge sharpnesses to - /// edit SubD sharpness. Never use this function as - /// a way to modify SubD sharpness properties. + /// This function is used to save vertex sharpnesses at interior crease vertices + /// so that low level evaulation code (like that in ON_SubDVertexQuadSector) will + /// get correct subdivision and limit points for interior crease vertices that + /// have different maximum edge end shaprness values in their two sectors. + /// The level 0 value passed in must be calucated while the vertex is in the orginal + /// complete configuration with two sectors. Subsets used in low level N > 0 evaluations + /// sometimes pass in a value calculated by subdividing the previous level's value. /// - /// - /// Maximum edge end sharpness at this vertex in the complete SubD. + /// + /// Maximum edge end sharpness of the interior crease vertex calculated + /// from all level 0 smooth edges in both sectors or calculated by subdividing + /// a previous level's value. /// - void Internal_UpdateCreaseSectorVertexSharpnessForExperts(double crease_sector_vertex_sharpness) const; + /// + /// If bSkipEdgeCountTest is false, then v must be a level 0 crease vertex + /// with m_edge_count = m_face_count >= 2 of a level N > 0 vertex + /// with m_edge_count = 1+m_face_count >= 1. + /// If bSkipEdgeCountTest the edge count tests are skipped. The expert user + /// is certain this vertex is being initialized before the edges and faces are + /// attached and it's control net topology will satisify the above conditions + /// before the vertex is used. + /// + void Internal_SetInteriorCreaseVertexSharpnessForExperts( + double interior_crease_vertex_sharpness, + bool bSkipEdgeCountTest + ) const; /// - /// This function is for use by experts in low level subdivisino point - /// evaluations when the center vertex is a crease with two sectors. + /// Unconditionally sets the interior crease vertex sharpness to zero. + /// + void Internal_ClearInteriorCreaseVertexSharpnessForExperts() const; + + + /// + /// This function is for use by experts in low level subdivision point + /// evaluations when the center vertex is an interior crease with two sectors. + /// It exists so that low level evaluation code using information about + /// only one of the sectors is present (like the situation that occurs in + /// ON_SubDVertexQuadSector functions). /// /// - /// The crease sector vertex sharpness. + /// The interior crease sector vertex sharpness. This value must be properly + /// epertly used with other information to get the correct subdivision point. /// - double Internal_CreaseSectorVertexSharpnessForExperts() const; + double Internal_InteriorCreaseVertexSharpnessForExperts() const; #endif public: @@ -13311,9 +13428,51 @@ public: /* Returns: True if m_vertex_tag is ON_SubDVertexTag::Crease. + If the vertex is a boundary crease, there will be one sector. + If the vertex is an interior crease, there will be two sectors. */ bool IsCrease() const; + /// + /// There are 2 configurations a valid crease vertex can have. + /// + /// 1) A crease vertex with one sector has EdgeCount() = 1+FaceCount(), + /// two crease edges that are attached to a single face, + /// and the other edges are smooth and are attached to two faces. + /// + /// 2) A crease vertex with two sectors has EdgeCount() = FaceCount(), + /// two crease edges, and all edges are attached to two faces. + /// + /// In a complete SubD (not a subset), a 1 sector crease vertex is + /// always on the boundary of the SubD. In all cases a 2 sector crease vertex + /// is an interior vertex. + /// + /// + /// True if the vertex tag is ON_SubDVertexTag::Crease, EdgeCount() = 1+FaceCount(), + /// and FaceCount() >= 1. + /// + bool IsOneSectorCrease() const; + + /// + /// There are 2 configurations a valid crease vertex can have. + /// + /// 1) A crease vertex with one sector has EdgeCount() = 1+FaceCount(), + /// two crease edges that are attached to a single face, + /// and the other edges are smooth and are attached to two faces. + /// + /// 2) A crease vertex with two sectors has EdgeCount() = FaceCount(), + /// two crease edges, and all edges are attached to two faces. + /// + /// In a complete SubD (not a subset), a 1 sector crease vertex is + /// always on the boundary of the SubD. In all cases a 2 sector crease vertex + /// is an interior vertex. + /// + /// + /// True if the vertex tag is ON_SubDVertexTag::Crease, EdgeCount() = FaceCount(), + /// and FaceCount() >= 2. + /// + bool IsTwoSectorCrease() const; + /* Returns: True if m_vertex_tag is ON_SubDVertexTag::Corner. diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h index 58322396..8d072571 100644 --- a/opennurbs_subd_data.h +++ b/opennurbs_subd_data.h @@ -3138,7 +3138,7 @@ public: double* m_cvx = nullptr; }; -class ON_SubDVertexQuadSector +class /*DO NOT EXPORT*/ ON_SubDVertexQuadSector { public: ON_SubDVertexQuadSector() = default; diff --git a/opennurbs_subd_eval.cpp b/opennurbs_subd_eval.cpp index f4f85cd7..90a64716 100644 --- a/opennurbs_subd_eval.cpp +++ b/opennurbs_subd_eval.cpp @@ -1027,6 +1027,9 @@ void ON_SubDVertex::ClearSavedSurfacePoints() const void ON_SubDVertex::ClearSavedSubdivisionPoints() const { + // Dale Lear 2024 Feb 27 - Fix RH-80633 + // Unconditionally clear cached crease sector sharpness. + this->Internal_ClearInteriorCreaseVertexSharpnessForExperts(); // clear vertex specific limit point cache ClearSavedSurfacePoints(); // clear general subdivision point cache diff --git a/opennurbs_subd_frommesh.cpp b/opennurbs_subd_frommesh.cpp index 125d890f..e9bb16ef 100644 --- a/opennurbs_subd_frommesh.cpp +++ b/opennurbs_subd_frommesh.cpp @@ -1258,10 +1258,6 @@ ON_SubD* ON_SubD::CreateSubDBox( ? ON_SubDEdgeTag::Crease : ON_SubDEdgeTag::Smooth; - const bool bSharpEdge - = edge_sharpness > ON_SubDEdgeSharpness::SmoothValue - && edge_sharpness <= ON_SubDEdgeSharpness::MaximumValue; - ON_SubD* subd = (nullptr != destination_subd) ? destination_subd diff --git a/opennurbs_subd_heap.cpp b/opennurbs_subd_heap.cpp index 5427c01e..a3ffb859 100644 --- a/opennurbs_subd_heap.cpp +++ b/opennurbs_subd_heap.cpp @@ -477,11 +477,13 @@ ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( v1->m_vertex_tag = vertex0->m_vertex_tag; - const double crease_sector_vertex_sharpness0 = vertex0->Internal_CreaseSectorVertexSharpnessForExperts(); + const double crease_sector_vertex_sharpness0 = vertex0->Internal_InteriorCreaseVertexSharpnessForExperts(); if (crease_sector_vertex_sharpness0 > 1.0) { + // subdivide this value const double crease_sector_vertex_sharpness1 = ON_SubDEdgeSharpness::Sanitize(crease_sector_vertex_sharpness0 - 1.0); - v1->Internal_UpdateCreaseSectorVertexSharpnessForExperts(crease_sector_vertex_sharpness1); + // The 2nd parameter is true because we have not attached the edges and faces yet. + v1->Internal_SetInteriorCreaseVertexSharpnessForExperts(crease_sector_vertex_sharpness1, true); } if (vertex0->SurfacePointIsSet()) diff --git a/opennurbs_subd_limit.cpp b/opennurbs_subd_limit.cpp index 44578383..bba69cac 100644 --- a/opennurbs_subd_limit.cpp +++ b/opennurbs_subd_limit.cpp @@ -714,7 +714,7 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( ++m_sharp_edge_count; bSharpQuadrant[i % 4U] = true; bSharpQuadrant[(i + 1U) % 4U] = true; - bSharpQuadrant[(i + 2U) % 4U] = true; + bSharpQuadrant[(i + 3U) % 4U] = true; } } @@ -733,15 +733,30 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( bExtraordinaryCornerVertex[fvi2] = bExtraordinaryCornerVertex[fvi1]; bExtraordinaryCornerVertex[fvi3] = bExtraordinaryCornerVertex[fvi1]; + + const ON_SubDVertex* quad_vertex[4] = { + m_vertex_grid[1][1], + m_vertex_grid[2][1], + m_vertex_grid[2][2], + m_vertex_grid[1][2] + }; + + for (unsigned int corner_index = 0; corner_index < 4; corner_index++) + { + // Dale Lear 2024 Feb 28 Fix RH-80676 + // Even when bSharpQuadrant[corner_index] is true from tests above, + // we have to call quad_vertex[corner_index]->VertexSharpness() to + // insure all sharp edges are taken into account. + if (quad_vertex[corner_index]->VertexSharpness() > 0.0) + { + bSharpQuadrant[corner_index] = true; + bSharpQuadrant[(corner_index + 1U) % 4U] = true; + bSharpQuadrant[(corner_index + 3U) % 4U] = true; + } + } + if (false == bEdgeTagX) { - const ON_SubDVertex* quad_vertex[4] = { - m_vertex_grid[1][1], - m_vertex_grid[2][1], - m_vertex_grid[2][2], - m_vertex_grid[1][2] - }; - const bool bQuadVertexIsSmoothOrCrease[4] = { quad_vertex[0]->IsSmoothOrCrease(), @@ -758,9 +773,6 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( if (quad_vertex[corner_index]->IsDart()) continue; - if (false == bSharpQuadrant[corner_index] && quad_vertex[corner_index]->Internal_CreaseSectorVertexSharpnessForExperts() > 0.0) - bSharpQuadrant[corner_index] = true; - if (false == bQuadVertexIsSmoothOrCrease[(corner_index+1)%4]) continue; if (false == bQuadVertexIsSmoothOrCrease[(corner_index+3)%4]) diff --git a/opennurbs_xml.cpp b/opennurbs_xml.cpp index ccd2551e..5a8dcfed 100644 --- a/opennurbs_xml.cpp +++ b/opennurbs_xml.cpp @@ -3419,7 +3419,7 @@ ON_XMLNode* ON_XMLNode::NextSibling(void) const ON__UINT32 ON_XMLNode::DataCRC(ON__UINT32 crc, bool recursive) const { - return _private->DataCRC(crc, 0); + return _private->DataCRC(crc, recursive); } void* ON_XMLNode::EVF(const wchar_t*, void*)