#include "opennurbs.h" #if !defined(ON_COMPILING_OPENNURBS) // This check is included in all opennurbs source .c and .cpp files to insure // ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. // When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined // and the opennurbs .h files alter what is declared and how it is declared. #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif #include "opennurbs_subd_data.h" /* // // Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert // McNeel & Associates. // // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF // MERCHANTABILITY ARE HEREBY DISCLAIMED. // // For complete openNURBS copyright information see . // //////////////////////////////////////////////////////////////// */ 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::LocalG1x); } return ON_SUBD_RETURN_ERROR(ON_SubDToBrepParameters::VertexProcess::None); } const ON_wString ON_SubDToBrepParameters::VertexProcessToString( ON_SubDToBrepParameters::VertexProcess vertex_process ) { const wchar_t* s; switch (vertex_process) { case ON_SubDToBrepParameters::VertexProcess::None: s = L"None"; break; case ON_SubDToBrepParameters::VertexProcess::LocalG1: s = L"G1"; break; case ON_SubDToBrepParameters::VertexProcess::LocalG2: s = L"G2"; break; case ON_SubDToBrepParameters::VertexProcess::LocalG1x: s = L"G1x"; break; default: s = L"INVALID"; break; } return ON_wString(s); } 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; } const ON_wString ON_SubDToBrepParameters::ToString( bool bVerbose ) const { const ON_wString exvtx(ON_SubDToBrepParameters::VertexProcessToString(ExtraordinaryVertexProcess())); const ON_wString faces(PackFaces() ? L"Packed" : L"Unpacked"); const ON_wString s = ON_wString::FormatToString(L"Faces = %ls ExtraordinaryVertex = %ls", static_cast(faces), static_cast(exvtx) ); return bVerbose ? (ON_wString(L"ON_SubDToBrepParameters: ") + s) : s; } bool ON_SubDToBrepParameters::Read(ON_BinaryArchive& archive) { *this = ON_SubDToBrepParameters::Default; int version = 0; if (false == archive.BeginRead3dmAnonymousChunk(&version)) return false; bool rc = false; for (;;) { if (version < 1) break; bool bPackFaces = this->PackFaces(); if (false == archive.ReadBool(&bPackFaces)) break; this->SetPackFaces(bPackFaces); unsigned u = static_cast(this->ExtraordinaryVertexProcess()); if (false == archive.ReadInt(&u)) break; ON_SubDToBrepParameters::VertexProcess exvtx = ON_SubDToBrepParameters::VertexProcessFromUnsigned(u); this->SetExtraordinaryVertexProcess(exvtx); rc = true; break; } if (false == archive.EndRead3dmChunk()) rc = false; return rc; } bool ON_SubDToBrepParameters::Write(ON_BinaryArchive& archive) const { if (false == archive.BeginWrite3dmAnonymousChunk(1)) return false; bool rc = false; for (;;) { if (false == archive.WriteBool(PackFaces())) break; const unsigned u = static_cast(this->ExtraordinaryVertexProcess()); if (false == archive.WriteInt(u)) break; rc = true; break; } if (false == archive.EndWrite3dmChunk()) rc = false; return rc; } ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentPtrTypeFromUnsigned( unsigned int element_pointer_type_as_unsigned ) { switch (element_pointer_type_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::Type::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::Type::Vertex); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::Type::Edge); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDComponentPtr::Type::Face); } return ON_SUBD_RETURN_ERROR(ON_SubDComponentPtr::Type::Unset); } ON_SubDVertexTag ON_SubD::VertexTagFromUnsigned( unsigned int vertex_tag_as_unsigned ) { switch (vertex_tag_as_unsigned) { 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_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_SubDVertexTag vertex_tag ) { return ( ON_SubDVertexTag::Smooth == vertex_tag || ON_SubDVertexTag::Crease == vertex_tag || ON_SubDVertexTag::Corner == vertex_tag || ON_SubDVertexTag::Dart == vertex_tag ); } ON_SubDEdgeTag ON_SubD::EdgeTagFromUnsigned( unsigned int edge_tag_as_unsigned ) { switch (edge_tag_as_unsigned) { 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_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_SubDEdgeTag edge_tag ) { return ( ON_SubDEdgeTag::Smooth == edge_tag || ON_SubDEdgeTag::Crease == edge_tag //|| ON_SubDEdgeTag::Sharp == edge_tag || ON_SubDEdgeTag::SmoothX == edge_tag ); } ON_SubD::VertexFacetType ON_SubD::VertexFacetTypeFromUnsigned( unsigned int vertex_facet_type_as_unsigned ) { switch (vertex_facet_type_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Tri); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Quad); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Ngon); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexFacetType::Mixed); } return ON_SUBD_RETURN_ERROR(ON_SubD::VertexFacetType::Unset); } unsigned int ON_SubDSectorType::SectorPointRingCountFromEdgeCount( 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_SubDVertexTag::Smooth == vertex_tag || ON_SubDVertexTag::Dart == vertex_tag) { // interior vertex return (2 * sector_edge_count + 1); } if (ON_SubDVertexTag::Crease == vertex_tag || ON_SubDVertexTag::Corner == vertex_tag) { // boundary vertex return (2 * sector_edge_count); } } return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDSectorType::SectorPointRingCountFromFaceCount( ON_SubDVertexTag vertex_tag, unsigned int sector_face_count ) { unsigned int sector_edge_count = ON_SubDSectorType::SectorEdgeCountFromFaceCount(vertex_tag,sector_face_count); return (sector_edge_count > 0) ? ON_SubDSectorType::SectorPointRingCountFromEdgeCount(vertex_tag,sector_edge_count) : ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDSectorType::SectorFaceCountFromEdgeCount( 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_SubDVertexTag::Crease == vertex_tag || ON_SubDVertexTag::Corner == vertex_tag) ? sector_edge_count-1 : sector_edge_count; return sector_face_count; } return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDSectorType::SectorEdgeCountFromFaceCount( 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_SubDVertexTag::Crease == vertex_tag || ON_SubDVertexTag::Corner == vertex_tag) ? sector_face_count+1 : sector_face_count; return sector_edge_count; } return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDSectorType::MinimumSectorEdgeCount( ON_SubDVertexTag vertex_tag ) { if (ON_SubDVertexTag::Smooth == vertex_tag || ON_SubDVertexTag::Dart == vertex_tag) return ON_SubDSectorType::MinimumSectorFaceCount(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_SubDVertexTag vertex_tag ) { unsigned int minimum_sector_face_count; switch (vertex_tag) { case ON_SubDVertexTag::Unset: ON_SUBD_ERROR("Unset tag."); minimum_sector_face_count = ON_UNSET_UINT_INDEX; break; 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_SubDVertexTag::Crease: // A "wire" crease can have zero faces - this is the minimum when faces exist minimum_sector_face_count = 1; break; 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_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. minimum_sector_face_count = 2; // 3 without special case handling break; default: ON_SUBD_ERROR("Invalid tag."); minimum_sector_face_count = ON_UNSET_UINT_INDEX; break; } return minimum_sector_face_count; } bool ON_SubD::IsValidSectorEdgeCount( ON_SubDVertexTag vertex_tag, unsigned int sector_edge_count ) { return (sector_edge_count >= ON_SubDSectorType::MinimumSectorEdgeCount(vertex_tag) && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount); } bool ON_SubD::IsValidSectorFaceCount( ON_SubDVertexTag vertex_tag, unsigned int sector_face_count ) { return (sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag) && sector_face_count <= ON_SubDVertex::MaximumFaceCount); } ////////////////////////////////////////////////////////////////////////// // // ON_SubDVertexPtr // // bool ON_SubDVertexPtr::IsNull() const { return (nullptr == ON_SUBD_VERTEX_POINTER(m_ptr)); } bool ON_SubDVertexPtr::IsNotNull() const { return (nullptr != ON_SUBD_VERTEX_POINTER(m_ptr)); } unsigned int ON_SubDVertexPtr::VertexId() const { const ON_SubDVertex* v = ON_SUBD_VERTEX_POINTER(m_ptr); return (nullptr != v) ? v->m_id : 0U; } class ON_SubDVertex* ON_SubDVertexPtr::Vertex() const { return ON_SUBD_VERTEX_POINTER(m_ptr); } ON__UINT_PTR ON_SubDVertexPtr::VertexDirection() const { return ON_SUBD_VERTEX_DIRECTION(m_ptr); } void ON_SubDVertexPtr::ClearSavedSubdivisionPoints() const { const ON_SubDVertex* v = ON_SUBD_VERTEX_POINTER(m_ptr); if (nullptr != v) v->ClearSavedSubdivisionPoints(); } void ON_SubDVertexPtr::ClearSavedSubdivisionPoints( bool bClearNeighborhood ) const { const ON_SubDVertex* v = ON_SUBD_VERTEX_POINTER(m_ptr); if (nullptr != v) v->ClearSavedSubdivisionPoints(bClearNeighborhood); } const ON_ComponentStatus ON_SubDVertexPtr::Status() const { const ON_SubDVertex* vertex = ON_SUBD_VERTEX_POINTER(m_ptr); return (nullptr == vertex) ? ON_ComponentStatus::NoneSet : vertex->m_status; } const ON_SubDVertexPtr ON_SubDVertexPtr::Create( const class ON_SubDVertex* vertex ) { return ON_SubDVertexPtr::Create(vertex,0); } const ON_SubDVertexPtr ON_SubDVertexPtr::Create( const class ON_SubDVertex* vertex, ON__UINT_PTR vertex_mark ) { ON_SubDVertexPtr vptr = { (ON__UINT_PTR)vertex | (vertex_mark & ON_SUBD_COMPONENT_DIRECTION_MASK) }; return vptr; } const ON_SubDVertexPtr ON_SubDVertexPtr::Create( const class ON_SubDComponentPtr& vertex_element ) { return ON_SubDVertexPtr::Create(vertex_element.Vertex(), vertex_element.ComponentDirection()); } ////////////////////////////////////////////////////////////////////////// // // ON_SubDEdgePtr // bool ON_SubDEdgePtr::IsNull() const { return (nullptr == ON_SUBD_EDGE_POINTER(m_ptr)); } bool ON_SubDEdgePtr::IsNotNull() const { return (nullptr != ON_SUBD_EDGE_POINTER(m_ptr)); } bool ON_SubDEdgePtr::IsNotNullAndVerticesAreNotNull() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr != e && nullptr != e->m_vertex[0] && nullptr != e->m_vertex[1]); } class ON_SubDEdge* ON_SubDEdgePtr::Edge() const { return ON_SUBD_EDGE_POINTER(m_ptr); } unsigned int ON_SubDEdgePtr::EdgeId() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr != e) ? e->m_id : 0U; } unsigned int ON_SubDEdgePtr::EdgeFaceCount() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr != e) ? ((unsigned int)e->m_face_count) : 0U; } bool ON_SubDEdgePtr::EdgeIsSmooth() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr != e) ? e->IsSmooth() : false; } bool ON_SubDEdgePtr::EdgeIsCrease() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr != e) ? e->IsCrease() : false; } bool ON_SubDEdgePtr::EdgeIsHardCrease() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr != e) ? e->IsHardCrease() : false; } bool ON_SubDEdgePtr::EdgeIsDartCrease() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr != e) ? e->IsDartCrease() : false; } bool ON_SubDEdgePtr::HasInteriorEdgeTopology( bool bRequireOppositeFaceDirections ) const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr != e) ? e->HasInteriorEdgeTopology(bRequireOppositeFaceDirections) : false; } ON__UINT_PTR ON_SubDEdgePtr::EdgeDirection() const { return ON_SUBD_EDGE_DIRECTION(m_ptr); } void ON_SubDEdgePtr::ClearSavedSubdivisionPoints() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); if (nullptr != e) e->ClearSavedSubdivisionPoints(); } void ON_SubDEdgePtr::ClearSavedSubdivisionPoints( bool bClearNeighborhood ) const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); if (nullptr != e) e->ClearSavedSubdivisionPoints(bClearNeighborhood); } const class ON_SubDVertex* ON_SubDEdgePtr::RelativeVertex( int relative_vertex_index ) const { for (;;) { if (relative_vertex_index < 0 || relative_vertex_index>1) break; const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); if (nullptr == edge) break; if (0 != ON_SUBD_EDGE_DIRECTION(m_ptr)) relative_vertex_index = 1 - relative_vertex_index; return edge->m_vertex[relative_vertex_index]; } return nullptr; } unsigned ON_SubDEdgePtr::RelativeVertexId(int relative_vertex_index) const { const ON_SubDVertex* v = this->RelativeVertex(relative_vertex_index); return (nullptr != v) ? v->m_id : 0U; } bool ON_SubDEdgePtr::RelativeVertexMark( int relative_vertex_index, bool missing_vertex_return_value ) const { const ON_SubDVertex* v = this->RelativeVertex(relative_vertex_index); return (nullptr != v) ? v->Mark() : missing_vertex_return_value; } ON__UINT8 ON_SubDEdgePtr::RelativeVertexMarkBits( int relative_vertex_index, ON__UINT8 missing_vertex_return_value ) const { const ON_SubDVertex* v = this->RelativeVertex(relative_vertex_index); return (nullptr != v) ? v->MarkBits() : missing_vertex_return_value; } const ON_3dPoint ON_SubDEdgePtr::RelativeControlNetPoint( int relative_vertex_index ) const { const ON_SubDVertex* v = RelativeVertex(relative_vertex_index); return (nullptr != v) ? v->ControlNetPoint() : ON_3dPoint::NanPoint; } const ON_Line ON_SubDEdgePtr::RelativeControlNetLine() const { return ON_Line(RelativeControlNetPoint(0), RelativeControlNetPoint(1)); } const ON_3dVector ON_SubDEdgePtr::RelativeControlNetDirection() const { return RelativeControlNetLine().Direction(); } double ON_SubDEdgePtr::RelativeSectorCoefficient( int relative_vertex_index ) const { for (;;) { if (relative_vertex_index < 0 || relative_vertex_index>1) break; const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); if (nullptr == edge) break; if (0 != ON_SUBD_EDGE_DIRECTION(m_ptr)) relative_vertex_index = 1 - relative_vertex_index; return edge->m_sector_coefficient[relative_vertex_index]; } return ON_SubDSectorType::ErrorSectorCoefficient; } const ON_3dVector ON_SubDEdgePtr::RelativeDirection() const { for (;;) { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); if (nullptr == edge) break; if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1]) break; const int i0 = (0 != ON_SUBD_EDGE_DIRECTION(m_ptr)) ? 1 : 0; const ON_3dPoint P0(edge->m_vertex[i0]->m_P); const ON_3dPoint P1(edge->m_vertex[1-i0]->m_P); return (P1 - P0); } return ON_3dVector::NanVector; } const class ON_SubDFace* ON_SubDEdgePtr::RelativeFace( int relative_face_index ) const { if (relative_face_index < 0 || relative_face_index > 1) return nullptr; // invalid input const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); if (nullptr == e ) return nullptr; // null input if (nullptr == e || e->m_face_count > 2) return nullptr; // nonmanifold edge const ON_SubDFace* LR[2] = {}; for (unsigned short efi = 0; efi < e->m_face_count; ++efi) { const ON__UINT_PTR ptr = e->m_face2[efi].m_ptr; const ON_SubDFace* f = ON_SUBD_FACE_POINTER(ptr); if (nullptr == f) continue; if (0 == ON_SUBD_FACE_DIRECTION(ptr)) { if (nullptr != LR[0]) return nullptr; // not an oriented manifold interior edge LR[0] = f; } else { if (nullptr != LR[1]) return nullptr; // not an oriented manifold interior edge LR[1] = f; } } if (0 != ON_SUBD_EDGE_DIRECTION(m_ptr)) relative_face_index = 1 - relative_face_index; return LR[relative_face_index]; } bool ON_SubDEdgePtr::RelativeFaceMark( int relative_face_index, bool missing_face_return_value ) const { const ON_SubDFace* f = this->RelativeFace(relative_face_index); return (nullptr != f) ? f->Mark() : missing_face_return_value; } ON__UINT8 ON_SubDEdgePtr::RelativeFaceMarkBits( int relative_face_index, ON__UINT8 missing_face_return_value ) const { const ON_SubDFace* f = this->RelativeFace(relative_face_index); return (nullptr != f) ? f->MarkBits() : missing_face_return_value; } const ON_ComponentStatus ON_SubDEdgePtr::Status() const { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr == edge) ? ON_ComponentStatus::NoneSet : edge->m_status; } const ON_SubDEdgePtr ON_SubDEdgePtr::Reversed() const { return ON_SubDEdgePtr::Create(ON_SUBD_EDGE_POINTER(m_ptr), 1 - (m_ptr & 1)); } const ON_SubDEdgePtr ON_SubDEdgePtr::Create( const class ON_SubDEdge* edge ) { ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge }; return eptr; } const ON_SubDEdgePtr ON_SubDEdgePtr::Create( const class ON_SubDEdge* edge, ON__UINT_PTR direction ) { ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge | (direction & ON_SUBD_COMPONENT_DIRECTION_MASK) }; return eptr; } const ON_SubDEdgePtr ON_SubDEdgePtr::Create( const class ON_SubDComponentPtr& edge_element ) { return ON_SubDEdgePtr::Create(edge_element.Edge(), edge_element.ComponentDirection()); } const ON_SubDEdgePtr ON_SubDEdgePtr::CreateFromStartVertex( const class ON_SubDEdge* edge, const ON_SubDVertex* start_vertex ) { for (;;) { if (nullptr == edge || nullptr == start_vertex) break; if (edge->m_vertex[0] == edge->m_vertex[1]) break; ON__UINT_PTR dir; if (start_vertex == edge->m_vertex[0]) dir = 0; else if (start_vertex == edge->m_vertex[1]) dir = 1; else break; return ON_SubDEdgePtr::Create(edge, dir); } return ON_SubDEdgePtr::Null; } const ON_SubDEdgePtr ON_SubDEdgePtr::CreateFromEndVertex( const class ON_SubDEdge* edge, const ON_SubDVertex* end_vertex ) { return CreateFromStartVertex(edge,end_vertex).Reversed(); } ////////////////////////////////////////////////////////////////////////// // // ON_SubDFacePtr // bool ON_SubDFacePtr::IsNull() const { return (nullptr == ON_SUBD_FACE_POINTER(m_ptr)); } bool ON_SubDFacePtr::IsNotNull() const { return (nullptr != ON_SUBD_FACE_POINTER(m_ptr)); } ON_SubDFace* ON_SubDFacePtr::Face() const { return ON_SUBD_FACE_POINTER(m_ptr); } void ON_SubDFacePtr::ClearSavedSubdivisionPoints() const { const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr); if (nullptr != f) f->ClearSavedSubdivisionPoints(); } void ON_SubDFacePtr::ClearSavedSubdivisionPoints( bool bClearNeighborhood ) const { const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr); if (nullptr != f) f->ClearSavedSubdivisionPoints(bClearNeighborhood); } unsigned int ON_SubDFacePtr::FaceId() const { const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr); return (nullptr != f) ? f->m_id : 0U; } unsigned int ON_SubDFacePtr::FaceEdgeCount() const { const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr); return (nullptr != f) ? ((unsigned int)f->m_edge_count) : 0U; } ON__UINT_PTR ON_SubDFacePtr::FaceDirection() const { return ON_SUBD_FACE_DIRECTION(m_ptr); } const ON_ComponentStatus ON_SubDFacePtr::Status() const { const ON_SubDFace* face = ON_SUBD_FACE_POINTER(m_ptr); return (nullptr == face) ? ON_ComponentStatus::NoneSet : face->m_status; } const ON_SubDFacePtr ON_SubDFacePtr::Reversed() const { return ON_SubDFacePtr::Create(ON_SUBD_FACE_POINTER(m_ptr), 1 - (m_ptr & 1)); } const ON_SubDFacePtr ON_SubDFacePtr::Create( const class ON_SubDFace* face, ON__UINT_PTR direction ) { ON_SubDFacePtr fptr = { (ON__UINT_PTR)face | (direction & ON_SUBD_COMPONENT_DIRECTION_MASK) }; return fptr; } const ON_SubDFacePtr ON_SubDFacePtr::Create( const class ON_SubDComponentPtr& face_element ) { return ON_SubDFacePtr::Create(face_element.Face(), face_element.ComponentDirection()); } int ON_SubDFacePtr::Compare( const ON_SubDFacePtr* lhs, const ON_SubDFacePtr* rhs ) { if ( nullptr == lhs ) return 1; if ( nullptr == rhs ) return -1; if (lhs->m_ptr < rhs->m_ptr) return -1; if (lhs->m_ptr > rhs->m_ptr) return 1; return 0; } int ON_SubDFacePtr::CompareFacePointer( const ON_SubDFacePtr* lhs, const ON_SubDFacePtr* rhs ) { if (lhs == rhs) return 0; if ( nullptr == lhs ) return 1; if ( nullptr == rhs ) return -1; const ON__UINT_PTR lhs_ptr = (lhs->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); const ON__UINT_PTR rhs_ptr = (rhs->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); if (lhs_ptr < rhs_ptr) return -1; if (lhs_ptr > rhs_ptr) return 1; return 0; } ////////////////////////////////////////////////////////////////////////// // // ON_SubDComponentPtr // bool ON_SubDComponentPtr::IsNull() const { return nullptr == ComponentBase(); } bool ON_SubDComponentPtr::IsNotNull() const { return nullptr != ComponentBase(); } unsigned int ON_SubDComponentPtr::ComponentId() const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->m_id : 0U; } void ON_SubDComponentPtr::ClearSavedSubdivisionPoints() const { switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr) { case ON_SUBD_COMPONENT_TYPE_VERTEX: { const ON_SubDVertex* v = Vertex(); if (nullptr != v) v->ClearSavedSubdivisionPoints(); } break; case ON_SUBD_COMPONENT_TYPE_EDGE: { const ON_SubDEdge* e = Edge(); if (nullptr != e) e->ClearSavedSubdivisionPoints(); } break; case ON_SUBD_COMPONENT_TYPE_FACE: { const ON_SubDFace* f = Face(); if (nullptr != f) f->ClearSavedSubdivisionPoints(); } break; } } void ON_SubDComponentPtr::ClearSavedSubdivisionPoints( bool bClearNeighborhood ) const { switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr) { case ON_SUBD_COMPONENT_TYPE_VERTEX: { const ON_SubDVertex* v = Vertex(); if (nullptr != v) v->ClearSavedSubdivisionPoints(bClearNeighborhood); } break; case ON_SUBD_COMPONENT_TYPE_EDGE: { const ON_SubDEdge* e = Edge(); if (nullptr != e) e->ClearSavedSubdivisionPoints(bClearNeighborhood); } break; case ON_SUBD_COMPONENT_TYPE_FACE: { const ON_SubDFace* f = Face(); if (nullptr != f) f->ClearSavedSubdivisionPoints(bClearNeighborhood); } break; } } const ON_3dPoint ON_SubDComponentPtr::ControlNetCenterPoint() const { switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr) { case ON_SUBD_COMPONENT_TYPE_VERTEX: { const ON_SubDVertex* v = Vertex(); if (nullptr != v) return v->ControlNetPoint(); } break; case ON_SUBD_COMPONENT_TYPE_EDGE: { const ON_SubDEdge* e = Edge(); if (nullptr != e) return e->ControlNetCenterPoint(); } break; case ON_SUBD_COMPONENT_TYPE_FACE: { const ON_SubDFace* f = Face(); if (nullptr != f) return f->ControlNetCenterPoint(); } break; } return ON_3dPoint::NanPoint; } const ON_BoundingBox ON_SubDComponentPtr::ControlNetBoundingBox() const { switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr) { case ON_SUBD_COMPONENT_TYPE_VERTEX: { const ON_SubDVertex* v = Vertex(); if (nullptr != v) return v->ControlNetBoundingBox(); } break; case ON_SUBD_COMPONENT_TYPE_EDGE: { const ON_SubDEdge* e = Edge(); if (nullptr != e) return e->ControlNetBoundingBox(); } break; case ON_SUBD_COMPONENT_TYPE_FACE: { const ON_SubDFace* f = Face(); if (nullptr != f) return f->ControlNetBoundingBox(); } break; } return ON_BoundingBox::NanBoundingBox; } ON__UINT16 ON_SubDComponentPtr::Hash16FromTypeAndId() const { const ON_SubDComponentBase* c = ComponentBase(); return (0 != c) ? ON_CRC16((ON__UINT16)(ON_SUBD_COMPONENT_TYPE_MASK & m_ptr), sizeof(c->m_id), &(c->m_id)) : ((ON__UINT16)0U) ; } ON__UINT32 ON_SubDComponentPtr::Hash32FromPointer() const { const ON__UINT_PTR ptr = (ON__UINT_PTR)ComponentBase(); return ON_CRC32((ON__UINT32)(ON_SUBD_COMPONENT_TYPE_MASK & m_ptr), sizeof(ptr), &ptr); } ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentType() const { switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr) { case ON_SUBD_COMPONENT_TYPE_VERTEX: return ON_SubDComponentPtr::Type::Vertex; case ON_SUBD_COMPONENT_TYPE_EDGE: return ON_SubDComponentPtr::Type::Edge; case ON_SUBD_COMPONENT_TYPE_FACE: return ON_SubDComponentPtr::Type::Face; } return ON_SubDComponentPtr::Type::Unset; } const ON_ComponentStatus ON_SubDComponentPtr::Status() const { switch (ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: { const ON_SubDVertex* vertex = Vertex(); if ( nullptr != vertex ) return vertex->m_status; } break; case ON_SubDComponentPtr::Type::Edge: { const ON_SubDEdge* edge = Edge(); if ( nullptr != edge ) return edge->m_status; } break; case ON_SubDComponentPtr::Type::Face: { const ON_SubDFace* face = Face(); if ( nullptr != face ) return face->m_status; } break; } return ON_ComponentStatus::NoneSet; } unsigned int ON_SubDComponentPtr::SetStatus( ON_ComponentStatus status ) { switch (ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: { const ON_SubDVertex* vertex = Vertex(); if (nullptr != vertex) return vertex->m_status.SetStatus(status); } break; case ON_SubDComponentPtr::Type::Edge: { const ON_SubDEdge* edge = Edge(); if (nullptr != edge) return edge->m_status.SetStatus(status); } break; case ON_SubDComponentPtr::Type::Face: { const ON_SubDFace* face = Face(); if (nullptr != face) return face->m_status.SetStatus(status); } break; } return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDComponentPtr::SetStates( ON_ComponentStatus states_to_set ) { switch (ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: { const ON_SubDVertex* vertex = Vertex(); if (nullptr != vertex) return vertex->m_status.SetStates(states_to_set); } break; case ON_SubDComponentPtr::Type::Edge: { const ON_SubDEdge* edge = Edge(); if (nullptr != edge) return edge->m_status.SetStates(states_to_set); } break; case ON_SubDComponentPtr::Type::Face: { const ON_SubDFace* face = Face(); if (nullptr != face) return face->m_status.SetStates(states_to_set); } break; } return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDComponentPtr::ClearStates( ON_ComponentStatus states_to_clear ) { switch (ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: { const ON_SubDVertex* vertex = Vertex(); if (nullptr != vertex) return vertex->m_status.ClearStates(states_to_clear); } break; case ON_SubDComponentPtr::Type::Edge: { const ON_SubDEdge* edge = Edge(); if (nullptr != edge) return edge->m_status.ClearStates(states_to_clear); } break; case ON_SubDComponentPtr::Type::Face: { const ON_SubDFace* face = Face(); if (nullptr != face) return face->m_status.ClearStates(states_to_clear); } break; } return ON_SUBD_RETURN_ERROR(0); } ON_SubDComponentTest::ON_SubDComponentTest(ON__UINT_PTR ptr) : m_ptr(ptr) {} ON_SubDComponentTest::~ON_SubDComponentTest() {} bool ON_SubDComponentTest::Passes(const ON_SubDComponentPtr cptr) const { // Default implementation of a virtual function that is typically overridden return cptr.IsNotNull() && 0 != m_ptr; } bool ON_SubDComponentTest::Passes(const class ON_SubDVertex* v) const { return Passes((nullptr != v) ? v->ComponentPtr() : ON_SubDComponentPtr::Null); } bool ON_SubDComponentTest::Passes(const class ON_SubDEdge* e) const { return Passes((nullptr != e) ? e->ComponentPtr() : ON_SubDComponentPtr::Null); } bool ON_SubDComponentTest::Passes(const ON_SubDFace* f) const { return Passes((nullptr != f) ? f->ComponentPtr() : ON_SubDComponentPtr::Null); } ON_SubDComponentId::ON_SubDComponentId(ON_SubDComponentPtr::Type component_type, unsigned int component_id) : m_id(component_id) , m_type(component_type) {} ON_SubDComponentId::ON_SubDComponentId(ON_SubDComponentPtr cptr) { const ON_SubDComponentBase* b = cptr.ComponentBase(); if (nullptr != b) { m_id = b->m_id; m_type = cptr.ComponentType(); } } ON_SubDComponentId::ON_SubDComponentId(const class ON_SubDVertex* v) { if (nullptr != v) { m_id = v->m_id; m_type = ON_SubDComponentPtr::Type::Vertex; } } ON_SubDComponentId::ON_SubDComponentId(const class ON_SubDEdge* e) { if (nullptr != e) { m_id = e->m_id; m_type = ON_SubDComponentPtr::Type::Edge; } } ON_SubDComponentId::ON_SubDComponentId(const class ON_SubDFace* f) { if (nullptr != f) { m_id = f->m_id; m_type = ON_SubDComponentPtr::Type::Face; } } int ON_SubDComponentId::CompareTypeAndId(const ON_SubDComponentId& lhs, const ON_SubDComponentId& rhs) { if (static_cast(lhs.m_type) < static_cast(rhs.m_type)) return -1; if (static_cast(lhs.m_type) > static_cast(rhs.m_type)) return 1; if (lhs.m_id < rhs.m_id) return -1; if (lhs.m_id > rhs.m_id) return 1; return 0; } int ON_SubDComponentId::CompareTypeAndIdFromPointer(const ON_SubDComponentId* lhs, const ON_SubDComponentId* rhs) { if (lhs == rhs) return 0; // nullptr sorts to end of list if (nullptr == lhs) return 1; if (nullptr == rhs) return -1; if (static_cast(lhs->m_type) < static_cast(rhs->m_type)) return -1; if (static_cast(lhs->m_type) > static_cast(rhs->m_type)) return 1; if (lhs->m_id < rhs->m_id) return -1; if (lhs->m_id > rhs->m_id) return 1; return 0; } unsigned int ON_SubDComponentId::ComponentId() const { return m_id; } ON_SubDComponentPtr::Type ON_SubDComponentId::ComponentType() const { return m_type; } bool ON_SubDComponentId::IsSet() const { return 0 != m_id && ON_SubDComponentPtr::Type::Unset != m_type; } bool ON_SubDComponentIdList::Passes(const ON_SubDComponentPtr cptr) const { return InList(cptr) ? m_bInListPassesResult : !m_bInListPassesResult; } void ON_SubDComponentIdList::AddId(ON_SubDComponentId cid) { if (cid.IsSet()) { m_bSorted = false; m_id_list.Append(cid); } } void ON_SubDComponentIdList::AddId(ON_SubDComponentPtr cptr) { AddId(ON_SubDComponentId(cptr)); } void ON_SubDComponentIdList::SetInListPassesResult(bool bInListPassesResult) { m_bInListPassesResult = bInListPassesResult ? true : false; } bool ON_SubDComponentIdList::InListPassesResult() const { return m_bInListPassesResult; } bool ON_SubDComponentIdList::InList(ON_SubDComponentId cid) const { if (false == m_bSorted) { m_bSorted = true; m_id_list.QuickSortAndRemoveDuplicates(ON_SubDComponentId::CompareTypeAndIdFromPointer); } const bool bInList = m_id_list.BinarySearch(&cid, ON_SubDComponentId::CompareTypeAndIdFromPointer) >= 0; return bInList; } bool ON_SubDComponentIdList::InList(ON_SubDComponentPtr cptr) const { return InList(ON_SubDComponentId(cptr)); } bool ON_SubDComponentPtr::Mark() const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->m_status.RuntimeMark() : false; } bool ON_SubDComponentPtr::ClearMark() const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false; } bool ON_SubDComponentPtr::SetMark() const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->m_status.SetRuntimeMark() : false; } bool ON_SubDComponentPtr::SetMark( bool bMark ) const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false; } ON__UINT8 ON_SubDComponentPtr::MarkBits() const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->m_status.MarkBits() : 0U; } ON__UINT8 ON_SubDComponentPtr::SetMarkBits(ON__UINT8 mark_bits) const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->m_status.SetMarkBits(mark_bits) : false; } ON__UINT8 ON_SubDComponentPtr::ClearMarkBits() const { const ON_SubDComponentBase* c = this->ComponentBase(); 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 { const ON_SubDVertex* c = this->Vertex(); return (nullptr != c) ? c->m_status.RuntimeMark() : false; } bool ON_SubDVertexPtr::ClearMark() const { const ON_SubDVertex* c = this->Vertex(); return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false; } bool ON_SubDVertexPtr::SetMark() const { const ON_SubDVertex* c = this->Vertex(); return (nullptr != c) ? c->m_status.SetRuntimeMark() : false; } bool ON_SubDVertexPtr::SetMark( bool bMark ) const { const ON_SubDVertex* c = this->Vertex(); return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false; } ON__UINT8 ON_SubDVertexPtr::MarkBits() const { const ON_SubDVertex* c = this->Vertex(); return (nullptr != c) ? c->m_status.MarkBits() : 0U; } ON__UINT8 ON_SubDVertexPtr::SetMarkBits(ON__UINT8 mark_bits) const { const ON_SubDVertex* c = this->Vertex(); return (nullptr != c) ? c->m_status.SetMarkBits(mark_bits) : false; } ON__UINT8 ON_SubDVertexPtr::ClearMarkBits() const { const ON_SubDVertex* c = this->Vertex(); return (nullptr != c) ? c->m_status.SetMarkBits(0) : false; } bool ON_SubDEdgePtr::Mark() const { const ON_SubDEdge* c = this->Edge(); return (nullptr != c) ? c->m_status.RuntimeMark() : false; } bool ON_SubDEdgePtr::ClearMark() const { const ON_SubDEdge* c = this->Edge(); return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false; } bool ON_SubDEdgePtr::SetMark() const { const ON_SubDEdge* c = this->Edge(); return (nullptr != c) ? c->m_status.SetRuntimeMark() : false; } bool ON_SubDEdgePtr::SetMark( bool bMark ) const { const ON_SubDEdge* c = this->Edge(); return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false; } ON__UINT8 ON_SubDEdgePtr::MarkBits() const { const ON_SubDEdge* c = this->Edge(); return (nullptr != c) ? c->m_status.MarkBits() : 0U; } ON__UINT8 ON_SubDEdgePtr::SetMarkBits(ON__UINT8 mark_bits) const { const ON_SubDEdge* c = this->Edge(); return (nullptr != c) ? c->m_status.SetMarkBits(mark_bits) : false; } ON__UINT8 ON_SubDEdgePtr::ClearMarkBits() const { const ON_SubDEdge* c = this->Edge(); return (nullptr != c) ? c->m_status.SetMarkBits(0) : false; } bool ON_SubDFacePtr::Mark() const { const ON_SubDFace* c = this->Face(); return (nullptr != c) ? c->m_status.RuntimeMark() : false; } bool ON_SubDFacePtr::ClearMark() const { const ON_SubDFace* c = this->Face(); return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false; } bool ON_SubDFacePtr::SetMark() const { const ON_SubDFace* c = this->Face(); return (nullptr != c) ? c->m_status.SetRuntimeMark() : false; } bool ON_SubDFacePtr::SetMark( bool bMark ) const { const ON_SubDFace* c = this->Face(); return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false; } ON__UINT8 ON_SubDFacePtr::MarkBits() const { const ON_SubDFace* c = this->Face(); return (nullptr != c) ? c->m_status.MarkBits() : 0U; } ON__UINT8 ON_SubDFacePtr::SetMarkBits(ON__UINT8 mark_bits) const { const ON_SubDFace* c = this->Face(); return (nullptr != c) ? c->m_status.SetMarkBits(mark_bits) : false; } ON__UINT8 ON_SubDFacePtr::ClearMarkBits() const { const ON_SubDFace* c = this->Face(); return (nullptr != c) ? c->m_status.SetMarkBits(0) : false; } ON__UINT_PTR ON_SubDComponentPtr::ComponentDirection() const { return ON_SUBD_COMPONENT_DIRECTION(m_ptr); } const ON_SubDComponentPtr ON_SubDComponentPtr::ClearComponentDirection() const { ON_SubDComponentPtr component_ptr = *this; component_ptr.m_ptr &= (ON_SUBD_COMPONENT_POINTER_MASK | ON_SUBD_COMPONENT_TYPE_MASK); return component_ptr; } const ON_SubDComponentPtr ON_SubDComponentPtr::SetComponentDirection() const { ON_SubDComponentPtr component_ptr = *this; component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK; return component_ptr; } const ON_SubDComponentPtr ON_SubDComponentPtr::SetComponentDirection(ON__UINT_PTR dir) const { ON_SubDComponentPtr component_ptr = *this; if (0 == dir) component_ptr.m_ptr &= ~((ON__UINT_PTR)ON_SUBD_COMPONENT_DIRECTION_MASK); else if (1 == dir) component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK; else ON_SUBD_ERROR("Invalid dir parameter"); return component_ptr; } const ON_SubDComponentPtr ON_SubDComponentPtr::Reversed() const { return (0 != (m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK)) ? ClearComponentDirection() : SetComponentDirection(); } const ON_SubDComponentPtr ON_SubDComponentPtr::CreateNull( ON_SubDComponentPtr::Type component_type, ON__UINT_PTR component_direction ) { ON_SubDComponentPtr component_ptr; switch (component_type) { case ON_SubDComponentPtr::Type::Unset: component_ptr.m_ptr = 0; break; case ON_SubDComponentPtr::Type::Vertex: component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_VERTEX; break; case ON_SubDComponentPtr::Type::Edge: component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_EDGE; break; case ON_SubDComponentPtr::Type::Face: component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_FACE; break; default: component_ptr.m_ptr = 0; break; } if (1 == component_direction) component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK; return component_ptr; } class ON_SubDComponentBase* ON_SubDComponentPtr::ComponentBase() const { switch ((ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)) { case ON_SUBD_COMPONENT_TYPE_VERTEX: case ON_SUBD_COMPONENT_TYPE_EDGE: case ON_SUBD_COMPONENT_TYPE_FACE: // During archive id mapping, the returned value can be an archive id and not a true pointer. // This is in a controlled setting inside functions like ON_SubDArchiveIdMap::ConvertArchiveIdToRuntimeSymmetrySetNextPtr(). // All public level SDK code can safely assume the returned value is a true pointer. // It does mean that you cannot "validate" the value returned here // using some contraint on what you feel is a reasonable true pointer value. return ((class ON_SubDComponentBase*)ON_SUBD_COMPONENT_POINTER(m_ptr)); break; } return nullptr; } class ON_SubDComponentBase* ON_SubDComponentPtr::ComponentBase(ON_SubDComponentPtr::Type type_filter) const { const ON_SubDComponentPtr::Type ptr_type = static_cast((unsigned char)(ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)); switch (ptr_type) { case ON_SubDComponentPtr::Type::Vertex: case ON_SubDComponentPtr::Type::Edge: case ON_SubDComponentPtr::Type::Face: return (ptr_type == type_filter || ON_SubDComponentPtr::Type::Unset == type_filter) ? ((class ON_SubDComponentBase*)ON_SUBD_COMPONENT_POINTER(m_ptr)) : nullptr; } return nullptr; } class ON_SubDVertex* ON_SubDComponentPtr::Vertex() const { if (ON_SUBD_COMPONENT_TYPE_VERTEX == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)) return ON_SUBD_VERTEX_POINTER(m_ptr); return nullptr; } class ON_SubDEdge* ON_SubDComponentPtr::Edge() const { if (ON_SUBD_COMPONENT_TYPE_EDGE == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)) return ON_SUBD_EDGE_POINTER(m_ptr); return nullptr; } class ON_SubDFace* ON_SubDComponentPtr::Face() const { if (ON_SUBD_COMPONENT_TYPE_FACE == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)) return ON_SUBD_FACE_POINTER(m_ptr); return nullptr; } const ON_SubDVertexPtr ON_SubDComponentPtr::VertexPtr() const { ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr); if ( ON_SUBD_COMPONENT_TYPE_VERTEX == element_type) return ON_SubDVertexPtr::Create(Vertex(), ComponentDirection()); return ON_SubDVertexPtr::Null; } const ON_SubDEdgePtr ON_SubDComponentPtr::EdgePtr() const { ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr); if ( ON_SUBD_COMPONENT_TYPE_EDGE == element_type) return ON_SubDEdgePtr::Create(Edge(), ComponentDirection()); return ON_SubDEdgePtr::Null; } const ON_SubDFacePtr ON_SubDComponentPtr::FacePtr() const { ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr); if ( ON_SUBD_COMPONENT_TYPE_FACE == element_type) return ON_SubDFacePtr::Create(Face(), ComponentDirection()); return ON_SubDFacePtr::Null; } const bool ON_SubDComponentPtr::IsVertex() const { return (ON_SUBD_COMPONENT_TYPE_VERTEX == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)); } const bool ON_SubDComponentPtr::IsEdge() const { return (ON_SUBD_COMPONENT_TYPE_EDGE == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)); } const bool ON_SubDComponentPtr::IsFace() const { return (ON_SUBD_COMPONENT_TYPE_FACE == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)); } const ON_SubDComponentPtr ON_SubDComponentPtr::Create( const class ON_SubDVertex* vertex ) { if (nullptr != vertex) { ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_COMPONENT_TYPE_VERTEX }; return vptr; } return ON_SubDComponentPtr::Null; } const ON_SubDComponentPtr ON_SubDComponentPtr::Create( const class ON_SubDEdge* edge ) { if (nullptr != edge) { ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | ON_SUBD_COMPONENT_TYPE_EDGE }; return eptr; } return ON_SubDComponentPtr::Null; } const ON_SubDComponentPtr ON_SubDComponentPtr::Create( const class ON_SubDFace* face ) { if (nullptr != face) { ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | ON_SUBD_COMPONENT_TYPE_FACE }; return fptr; } return ON_SubDComponentPtr::Null; } const ON_SubDComponentPtr ON_SubDComponentPtr::Create( const class ON_SubDVertex* vertex, ON__UINT_PTR vertex_direction ) { if (nullptr != vertex) { ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_COMPONENT_TYPE_VERTEX | (vertex_direction & ON_SUBD_COMPONENT_DIRECTION_MASK) }; return vptr; } return ON_SubDComponentPtr::Null; } const ON_SubDComponentPtr ON_SubDComponentPtr::Create( const class ON_SubDEdge* edge, ON__UINT_PTR edge_direction ) { if (nullptr != edge) { ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | (ON_SUBD_COMPONENT_TYPE_EDGE | (edge_direction & ON_SUBD_COMPONENT_DIRECTION_MASK)) }; return eptr; } return ON_SubDComponentPtr::Null; } const ON_SubDComponentPtr ON_SubDComponentPtr::Create( const class ON_SubDFace* face, ON__UINT_PTR face_direction ) { if (nullptr != face) { ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | (ON_SUBD_COMPONENT_TYPE_FACE | (face_direction & ON_SUBD_COMPONENT_DIRECTION_MASK)) }; return fptr; } return ON_SubDComponentPtr::Null; } const ON_SubDComponentPtr ON_SubDComponentPtr::Create( ON_SubDVertexPtr vertexptr ) { return Create(vertexptr.Vertex(), vertexptr.VertexDirection()); } const ON_SubDComponentPtr ON_SubDComponentPtr::Create( ON_SubDEdgePtr edgeptr ) { return Create(edgeptr.Edge(), edgeptr.EdgeDirection()); } const ON_SubDComponentPtr ON_SubDComponentPtr::Create( ON_SubDFacePtr faceptr ) { return Create(faceptr.Face(), faceptr.FaceDirection()); } int ON_SubDComponentPtr::CompareComponentPtrType( ON_SubDComponentPtr::Type a, ON_SubDComponentPtr::Type b ) { if ( a == b ) return 0; switch (a) { case ON_SubDComponentPtr::Type::Vertex: return -1; break; case ON_SubDComponentPtr::Type::Edge: return (ON_SubDComponentPtr::Type::Vertex == b) ? 1 : -1; break; case ON_SubDComponentPtr::Type::Face: return (ON_SubDComponentPtr::Type::Vertex == b || ON_SubDComponentPtr::Type::Edge == b) ? 1 : -1; break; default: break; } return (((unsigned char)a) < ((unsigned char)b)) ? -1 : 1; } int ON_SubDComponentPtr::CompareType( const ON_SubDComponentPtr* a, const ON_SubDComponentPtr* b ) { if ( a == b ) return 0; if ( nullptr == a ) return 1; if ( nullptr == b ) return -1; return ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType()); } int ON_SubDComponentPtr::CompareComponent( const ON_SubDComponentPtr* a, const ON_SubDComponentPtr* b ) { if (a == b) return 0; const int rc = ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType()); if (0 == rc) { const ON__UINT_PTR x = a->m_ptr; const ON__UINT_PTR y = b->m_ptr; if (x < y) return -1; if (x > y) return 1; } return rc; } int ON_SubDComponentPtr::CompareComponentAndDirection( const ON_SubDComponentPtr* a, const ON_SubDComponentPtr* b ) { if (a == b) return 0; const int rc = ON_SubDComponentPtr::CompareComponent(a, b); if (0 == rc) { const ON__UINT_PTR x = (a->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); const ON__UINT_PTR y = (b->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); if (x < y) return -1; if (x > y) return 1; } return rc; } const ON_SubDComponentAndNumber ON_SubDComponentAndNumber::Create( ON_SubDComponentPtr cptr, double x ) { ON_SubDComponentAndNumber cx; cx.m_cptr = cptr; cx.m_x = x; return cx; } const ON_SubDComponentAndNumber ON_SubDComponentAndNumber::Create( const ON_SubDVertex* v, double x ) { return ON_SubDComponentAndNumber::Create(ON_SubDComponentPtr::Create(v), x); } const ON_SubDComponentAndNumber ON_SubDComponentAndNumber::Create( const ON_SubDEdge* e, double x ) { return ON_SubDComponentAndNumber::Create(ON_SubDComponentPtr::Create(e), x); } const ON_SubDComponentAndNumber ON_SubDComponentAndNumber::Create( const ON_SubDFace* f, double x ) { return ON_SubDComponentAndNumber::Create(ON_SubDComponentPtr::Create(f), x); } const ON_SubDComponentAndNumber ON_SubDComponentAndNumber::Create( const ON_SubDVertexPtr vptr, double x ) { return ON_SubDComponentAndNumber::Create(ON_SubDComponentPtr::Create(vptr), x); } const ON_SubDComponentAndNumber ON_SubDComponentAndNumber::Create( const ON_SubDEdgePtr eptr, double x ) { return ON_SubDComponentAndNumber::Create(ON_SubDComponentPtr::Create(eptr), x); } const ON_SubDComponentAndNumber ON_SubDComponentAndNumber::Create( const ON_SubDFacePtr fptr, double x ) { return ON_SubDComponentAndNumber::Create(ON_SubDComponentPtr::Create(fptr), x); } const ON_SubDComponentPtr ON_SubDComponentAndNumber::Component() const { return m_cptr; } void ON_SubDComponentAndNumber::SetComponent(ON_SubDComponentPtr cptr) { m_cptr = cptr; } double ON_SubDComponentAndNumber::Number() const { return m_x; } void ON_SubDComponentAndNumber::SetNumber(double x) { m_x = x; } int ON_SubDComponentAndNumber::CompareComponent( const ON_SubDComponentAndNumber* a, const ON_SubDComponentAndNumber* b ) { if (a == b) return 0; if (nullptr == a) return -1; if (nullptr == b) return 1; return ON_SubDComponentPtr::CompareComponent(&a->m_cptr, &b->m_cptr); } // int ON_SubDComponentAndNumber::CompareComponentAndDirection( const ON_SubDComponentAndNumber* a, const ON_SubDComponentAndNumber* b ) { if (a == b) return 0; if (nullptr == a) return -1; if (nullptr == b) return 1; return ON_SubDComponentPtr::CompareComponentAndDirection(&a->m_cptr, &b->m_cptr); } int ON_SubDComponentAndNumber::CompareNumber( const ON_SubDComponentAndNumber* a, const ON_SubDComponentAndNumber* b ) { if (a == b) return 0; if (nullptr == a) return -1; if (nullptr == b) return 1; return ON_CompareDouble(a->m_x, b->m_x); } int ON_SubDComponentAndNumber::CompareComponentAndNumber( const ON_SubDComponentAndNumber* a, const ON_SubDComponentAndNumber* b ) { const int rc = ON_SubDComponentAndNumber::CompareComponent(a, b); return (0 != rc) ? rc : ON_SubDComponentAndNumber::CompareNumber(a, b); } int ON_SubDComponentAndNumber::CompareComponentAndDirectionAndNumber( const ON_SubDComponentAndNumber* a, const ON_SubDComponentAndNumber* b ) { const int rc = ON_SubDComponentAndNumber::CompareComponentAndDirection(a, b); return (0 != rc) ? rc : ON_SubDComponentAndNumber::CompareNumber(a, b); } const ON_SubDComponentAndPoint ON_SubDComponentAndPoint::Create( ON_SubDComponentPtr cptr, ON_3dPoint P ) { ON_SubDComponentAndPoint cx; cx.m_cptr = cptr; cx.m_P = P; return cx; } const ON_SubDComponentAndPoint ON_SubDComponentAndPoint::Create( const ON_SubDVertex* v, ON_3dPoint P ) { return ON_SubDComponentAndPoint::Create(ON_SubDComponentPtr::Create(v), P); } const ON_SubDComponentAndPoint ON_SubDComponentAndPoint::Create( const ON_SubDEdge* e, ON_3dPoint P ) { return ON_SubDComponentAndPoint::Create(ON_SubDComponentPtr::Create(e), P); } const ON_SubDComponentAndPoint ON_SubDComponentAndPoint::Create( const ON_SubDFace* f, ON_3dPoint P ) { return ON_SubDComponentAndPoint::Create(ON_SubDComponentPtr::Create(f), P); } const ON_SubDComponentAndPoint ON_SubDComponentAndPoint::Create( const ON_SubDVertexPtr vptr, ON_3dPoint P ) { return ON_SubDComponentAndPoint::Create(ON_SubDComponentPtr::Create(vptr), P); } const ON_SubDComponentAndPoint ON_SubDComponentAndPoint::Create( const ON_SubDEdgePtr eptr, ON_3dPoint P ) { return ON_SubDComponentAndPoint::Create(ON_SubDComponentPtr::Create(eptr), P); } const ON_SubDComponentAndPoint ON_SubDComponentAndPoint::Create( const ON_SubDFacePtr fptr, ON_3dPoint P ) { return ON_SubDComponentAndPoint::Create(ON_SubDComponentPtr::Create(fptr), P); } const ON_SubDComponentPtr ON_SubDComponentAndPoint::Component() const { return m_cptr; } void ON_SubDComponentAndPoint::SetComponent(ON_SubDComponentPtr cptr) { m_cptr = cptr; } const ON_3dPoint ON_SubDComponentAndPoint::Point() const { return m_P; } void ON_SubDComponentAndPoint::SetPoint(ON_3dPoint P) { m_P = P; } int ON_SubDComponentAndPoint::CompareComponent( const ON_SubDComponentAndPoint* lhs, const ON_SubDComponentAndPoint* rhs ) { if (lhs == rhs) return 0; if (nullptr == lhs) return -1; if (nullptr == rhs) return 1; return ON_SubDComponentPtr::CompareComponent(&lhs->m_cptr, &rhs->m_cptr); } // int ON_SubDComponentAndPoint::CompareComponentAndDirection( const ON_SubDComponentAndPoint* lhs, const ON_SubDComponentAndPoint* rhs ) { if (lhs == rhs) return 0; if (nullptr == lhs) return -1; if (nullptr == rhs) return 1; return ON_SubDComponentPtr::CompareComponentAndDirection(&lhs->m_cptr, &rhs->m_cptr); } int ON_SubDComponentAndPoint::ComparePoint( const ON_SubDComponentAndPoint* lhs, const ON_SubDComponentAndPoint* rhs ) { if (lhs == rhs) return 0; if (nullptr == lhs) return -1; if (nullptr == rhs) return 1; return ON_3dPoint::Compare(lhs->m_P, rhs->m_P); } int ON_SubDComponentAndPoint::CompareComponentAndPoint( const ON_SubDComponentAndPoint* lhs, const ON_SubDComponentAndPoint* rhs ) { const int rc = ON_SubDComponentAndPoint::CompareComponent(lhs, rhs); return (0 != rc) ? rc : ON_SubDComponentAndPoint::ComparePoint(lhs, rhs); } int ON_SubDComponentAndPoint::CompareComponentAndDirectionAndPoint( const ON_SubDComponentAndPoint* lhs, const ON_SubDComponentAndPoint* rhs ) { const int rc = ON_SubDComponentAndPoint::CompareComponentAndDirection(lhs, rhs); return (0 != rc) ? rc : ON_SubDComponentAndPoint::ComparePoint(lhs, rhs); } 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, const ON_SubDComponentPoint* b ) { if ( a == b) return 0; if ( nullptr == a) return 1; // nullptr > non-null pointer. if ( nullptr == b) return -1; // nullptr > non-null pointer. // 1st: compare component type // unset < vertex < edge < face ON__UINT_PTR x = (ON_SUBD_COMPONENT_TYPE_MASK & a->m_component_ptr.m_ptr); ON__UINT_PTR y = (ON_SUBD_COMPONENT_TYPE_MASK & b->m_component_ptr.m_ptr); if ( x < y ) return -1; if ( x > y ) return 1; // 2nd: compare component pointer x = (a->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); y = (b->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); if (x < y) return -1; if (x > y) return 1; // 3rd: compare component direction flag x = (a->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK); y = (b->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK); if (x < y) return -1; if (x > y) return 1; return 0; } const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::Create(ON_SubDComponentPtr first_ptr, ON_SubDComponentPtr second_ptr) { ON_SubDComponentPtrPair p; p.m_pair[0] = first_ptr; p.m_pair[1] = second_ptr; return p; } int ON_SubDComponentPtrPair::CompareComponent( const ON_SubDComponentPtrPair* lhs, const ON_SubDComponentPtrPair* rhs ) { if (lhs == rhs) return 0; // nulls sort to end. if (nullptr == rhs) return -1; if (nullptr == lhs) return 1; int rc = ON_SubDComponentPtr::CompareComponent(&lhs->m_pair[0], &rhs->m_pair[0]); if (0 == rc) rc = ON_SubDComponentPtr::CompareComponent(&lhs->m_pair[1], &rhs->m_pair[1]); return rc; } int ON_SubDComponentPtrPair::CompareComponentAndDirection( const ON_SubDComponentPtrPair* lhs, const ON_SubDComponentPtrPair* rhs ) { if (lhs == rhs) return 0; // nulls sort to end. if (nullptr == rhs) return -1; if (nullptr == lhs) return 1; int rc = ON_SubDComponentPtr::CompareComponentAndDirection(&lhs->m_pair[0], &rhs->m_pair[0]); if (0 == rc) rc = ON_SubDComponentPtr::CompareComponentAndDirection(&lhs->m_pair[1], &rhs->m_pair[1]); return rc; } int ON_SubDComponentPtrPair::CompareFirstPointer( const ON_SubDComponentPtrPair* lhs, const ON_SubDComponentPtrPair* rhs ) { if (lhs == rhs) return 0; // nulls sort to end. if (nullptr == rhs) return -1; if (nullptr == lhs) return 1; const ON__UINT_PTR lhs_ptr = (ON_SUBD_COMPONENT_POINTER_MASK & lhs->m_pair[0].m_ptr); const ON__UINT_PTR rhs_ptr = (ON_SUBD_COMPONENT_POINTER_MASK & rhs->m_pair[0].m_ptr); if (lhs_ptr < rhs_ptr) return -1; if (lhs_ptr > rhs_ptr) return 1; return 0; } ON_SubDComponentPtr::Type ON_SubDComponentPtrPair::ComponentType() const { const ON_SubDComponentPtr::Type type = m_pair[0].ComponentType(); return (type == m_pair[1].ComponentType()) ? type : ON_SubDComponentPtr::Type::Unset; } const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::SwapPair() const { return ON_SubDComponentPtrPair::Create(m_pair[1], m_pair[0]); } const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::ReversedPair() const { return ON_SubDComponentPtrPair::Create(m_pair[0].Reversed(), m_pair[1].Reversed()); } const ON_SubDComponentPtr ON_SubDComponentPtrPair::First() const { return m_pair[0]; } const ON_SubDComponentPtr ON_SubDComponentPtrPair::Second() const { return m_pair[1]; } bool ON_SubDComponentPtrPair::FirstIsNull() const { return (0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[0].m_ptr)); } bool ON_SubDComponentPtrPair::SecondIsNull() const { return (0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[1].m_ptr)); } bool ON_SubDComponentPtrPair::BothAreNull() const { return (0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[0].m_ptr)) && 0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[1].m_ptr); } bool ON_SubDComponentPtrPair::FirstIsNotNull() const { return m_pair[0].IsNotNull(); } bool ON_SubDComponentPtrPair::SecondIsNotNull() const { return m_pair[1].IsNotNull(); } bool ON_SubDComponentPtrPair::BothAreNotNull() const { return m_pair[0].IsNotNull() && m_pair[1].IsNotNull(); } ///////////////////////////////////////////////////////////////////////// // // ON_SubD_ComponentIdTypeAndTag // const ON_wString ON_SubD_ComponentIdTypeAndTag::ToString() const { switch (m_type) { case ON_SubDComponentPtr::Type::Unset: break; case ON_SubDComponentPtr::Type::Vertex: return ON_wString::FormatToString(L"Vertex id=%u tag=",m_id) + ON_SubD::VertexTagToString(VertexTag(),false); break; case ON_SubDComponentPtr::Type::Edge: return ON_wString::FormatToString(L"Edge id=%u tag=",m_id) + ON_SubD::EdgeTagToString(EdgeTag(), false); break; case ON_SubDComponentPtr::Type::Face: return ON_wString::FormatToString(L"Face id=%u tag=%u", m_id, (unsigned)FaceTag()); break; default: break; } return ON_wString(L"Unset"); } const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromVertex(const ON_SubDVertex* v) { ON_SubD_ComponentIdTypeAndTag itt = (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); return itt; } const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromVertexId(unsigned vertex_id, ON_SubDVertexTag vtag) { ON_SubD_ComponentIdTypeAndTag itt; if (vertex_id > 0) { itt.m_id = vertex_id; itt.m_type = ON_SubDComponentPtr::Type::Vertex; itt.m_tag = static_cast(vtag); } return itt; } const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromEdge(const ON_SubDEdge * e) { ON_SubD_ComponentIdTypeAndTag itt = (nullptr != e) ? ON_SubD_ComponentIdTypeAndTag::CreateFromEdgeId(e->m_id, e->m_edge_tag) : ON_SubD_ComponentIdTypeAndTag::Unset; if (itt.m_id > 0) itt.m_cptr = ON_SubDComponentPtr::Create(e); return itt; } const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromEdgeId(unsigned edge_id, ON_SubDEdgeTag etag) { ON_SubD_ComponentIdTypeAndTag itt; if (edge_id > 0) { 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); } return itt; } const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromFace(const ON_SubDFace* f, unsigned char ftag) { ON_SubD_ComponentIdTypeAndTag itt = (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); return itt; } const ON_SubD_ComponentIdTypeAndTag ON_SubD_ComponentIdTypeAndTag::CreateFromFaceId(unsigned face_id, unsigned char ftag) { ON_SubD_ComponentIdTypeAndTag itt; if (face_id > 0) { itt.m_id = face_id; itt.m_type = ON_SubDComponentPtr::Type::Vertex; itt.m_tag = ftag; } return itt; } int ON_SubD_ComponentIdTypeAndTag::CompareTypeAndId(const ON_SubD_ComponentIdTypeAndTag * lhs, const ON_SubD_ComponentIdTypeAndTag * rhs) { if (lhs == rhs) return 0; if (nullptr == lhs) return 1; if (nullptr == rhs) return -1; if (lhs->m_type < rhs->m_type) return -1; if (lhs->m_type > rhs->m_type) return 1; if (lhs->m_id < rhs->m_id) return -1; if (lhs->m_id > rhs->m_id) return 1; return 0; } int ON_SubD_ComponentIdTypeAndTag::CompareTypeAndIdAndTag(const ON_SubD_ComponentIdTypeAndTag* lhs, const ON_SubD_ComponentIdTypeAndTag* rhs) { int rc = ON_SubD_ComponentIdTypeAndTag::CompareTypeAndId(lhs, rhs); if (0 != rc) return rc; if (nullptr == lhs) return 1; if (nullptr == rhs) return -1; if (lhs->m_tag < rhs->m_tag) return -1; if (lhs->m_tag > rhs->m_tag) return 1; return 0; } 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; } 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; } 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; } 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; } 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; } 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; } ON_SubDComponentPtr::Type ON_SubD_ComponentIdTypeAndTag::ComponentType() const { return m_type; } unsigned ON_SubD_ComponentIdTypeAndTag::VertexId() const { return (ON_SubDComponentPtr::Type::Vertex == m_type) ? m_id : 0; } unsigned ON_SubD_ComponentIdTypeAndTag::EdgeId() const { return (ON_SubDComponentPtr::Type::Edge == m_type) ? m_id : 0; } unsigned ON_SubD_ComponentIdTypeAndTag::FaceId() const { return (ON_SubDComponentPtr::Type::Face == m_type) ? m_id : 0; } ON_SubDVertexTag ON_SubD_ComponentIdTypeAndTag::VertexTag() const { return (ON_SubDComponentPtr::Type::Vertex == m_type) ? ON_SubD::VertexTagFromUnsigned(m_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; } unsigned char ON_SubD_ComponentIdTypeAndTag::FaceTag() const { return (ON_SubDComponentPtr::Type::Face == m_type) ? m_tag : 0; } ////////////////////////////////////////////////////////////////////////// // // ON_SubDFromMeshParameters // // Convex corner options ON_SubDFromMeshParameters::ConvexCornerOption ON_SubDFromMeshParameters::ConvexCornerOptionFromUnsigned( unsigned int convex_corner_option_as_unsigned ) { switch (convex_corner_option_as_unsigned) { 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_SUBD_RETURN_ERROR(ON_SubDFromMeshParameters::ConvexCornerOption::Unset); } void ON_SubDFromMeshParameters::SetConvexCornerOption( ON_SubDFromMeshParameters::ConvexCornerOption convex_corner_option ) { m_convex_corner_option = ON_SubDFromMeshParameters::ConvexCornerOptionFromUnsigned((unsigned int)convex_corner_option); } ON_SubDFromMeshParameters::ConvexCornerOption ON_SubDFromMeshParameters::GetConvexCornerOption() const { switch (m_convex_corner_option) { case ON_SubDFromMeshParameters::ConvexCornerOption::Unset: case ON_SubDFromMeshParameters::ConvexCornerOption::None: return m_convex_corner_option; case ON_SubDFromMeshParameters::ConvexCornerOption::AtMeshCorner: if ( m_maximum_convex_corner_edge_count >= 2 && m_maximum_convex_corner_edge_count <= ON_SubDVertex::MaximumEdgeCount && m_maximum_convex_corner_angle_radians >= 0.0 && m_maximum_convex_corner_angle_radians < ON_PI ) return m_convex_corner_option; break; } return ON_SubDFromMeshParameters::ConvexCornerOption::Unset; } void ON_SubDFromMeshParameters::SetMaximumConvexCornerEdgeCount( unsigned int maximum_convex_corner_edge_count ) { if ( maximum_convex_corner_edge_count >= 2 && maximum_convex_corner_edge_count <= ON_SubDVertex::MaximumEdgeCount) m_maximum_convex_corner_edge_count = (unsigned short)maximum_convex_corner_edge_count; } unsigned int ON_SubDFromMeshParameters::MaximumConvexCornerEdgeCount() const { return m_maximum_convex_corner_edge_count; } void ON_SubDFromMeshParameters::SetMaximumConvexCornerAngleRadians( double maximum_convex_corner_angle_radians ) { if (maximum_convex_corner_angle_radians > 0.0 && maximum_convex_corner_angle_radians < ON_PI) m_maximum_convex_corner_angle_radians = maximum_convex_corner_angle_radians; } double ON_SubDFromMeshParameters::MaximumConvexCornerAngleRadians() const { return m_maximum_convex_corner_angle_radians; } ON_SubDFromMeshParameters::ConvexCornerOption ON_SubDFromMeshParameters::CopyConvexCornerTest( ON_SubDFromMeshParameters source_parameters ) { SetConvexCornerOption(source_parameters.GetConvexCornerOption()); SetMaximumConvexCornerEdgeCount(source_parameters.MaximumConvexCornerEdgeCount()); SetMaximumConvexCornerAngleRadians(source_parameters.MaximumConvexCornerAngleRadians()); 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; } void ON_SubDFromMeshParameters::SetInterpolateMeshVertices( bool bInterpolateMeshVertices ) { // Not supported in free opennurbs 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 return (0 == (ON_SubDFromMeshParameters::MergeColinearBoundaryEdgesMask & m_merge_edges_bits)); } void ON_SubDFromMeshParameters::SetMergeColinearBoundaryEdges( bool bAllowColinearBoundaryEdges ) { const unsigned char mask = ON_SubDFromMeshParameters::MergeColinearBoundaryEdgesMask; if (false == bAllowColinearBoundaryEdges) m_merge_edges_bits |= mask; // set bit else m_merge_edges_bits &= ~mask; // clear bit } bool ON_SubDFromMeshParameters::MergeColinearInteriorEdges() const { // clear bit means true, set bit means false return (0 == (ON_SubDFromMeshParameters::MergeColinearInteriorEdgesMask & m_merge_edges_bits)); } void ON_SubDFromMeshParameters::SetMergeColinearInteriorEdges( bool bAllowColinearInteriorEdges ) { const unsigned char mask = ON_SubDFromMeshParameters::MergeColinearInteriorEdgesMask; if (false == bAllowColinearInteriorEdges) m_merge_edges_bits |= mask; // set bit else m_merge_edges_bits &= ~mask; // clear bit } void ON_SubDFromMeshParameters::SetInteriorCreaseOption( ON_SubDFromMeshParameters::InteriorCreaseOption interior_crease_option ) { m_interior_crease_option = ON_SubDFromMeshParameters::InteriorCreaseOptionFromUnsigned((unsigned int)interior_crease_option); } ON_SubDFromMeshParameters::InteriorCreaseOption ON_SubDFromMeshParameters::GetInteriorCreaseOption() const { return m_interior_crease_option; } ON_SubDFromMeshParameters::InteriorCreaseOption ON_SubDFromMeshParameters::CopyInteriorCreaseTest( ON_SubDFromMeshParameters source_parameters ) { SetInteriorCreaseOption(source_parameters.GetInteriorCreaseOption()); return GetInteriorCreaseOption(); } ON_SubDFromMeshParameters::InteriorCreaseOption ON_SubDFromMeshParameters::InteriorCreaseOptionFromUnsigned( unsigned int interior_crease_option_as_unsigned ) { switch (interior_crease_option_as_unsigned) { 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_SUBD_RETURN_ERROR(ON_SubDFromMeshParameters::InteriorCreaseOption::Unset); } ON_SubDFromSurfaceParameters::Methods ON_SubDFromSurfaceParameters::Method() const { return m_method; } void ON_SubDFromSurfaceParameters::SetMethod( ON_SubDFromSurfaceParameters::Methods method ) { m_method = method; } bool ON_SubDFromSurfaceParameters::Corners() const { return m_bCorners; } void ON_SubDFromSurfaceParameters::SetCorners( bool bCorners ) { m_bCorners = bCorners ? true : false; } ////////////////////////////////////////////////////////////////////////// // // ON_SubDVertex // const ON_SubDEdgePtr ON_SubDVertex::EdgePtr( unsigned int i ) const { return (i < m_edge_count) ? m_edges[i] : ON_SubDEdgePtr::Null; } const class ON_SubDEdge* ON_SubDVertex::Edge( unsigned int i ) const { return (i < m_edge_count) ? ON_SUBD_EDGE_POINTER(m_edges[i].m_ptr) : nullptr; } ON__UINT_PTR ON_SubDVertex::EdgeDirection( unsigned int i ) const { return (i < m_edge_count) ? ON_SUBD_EDGE_DIRECTION(m_edges[i].m_ptr) : 0; } unsigned int ON_SubDVertex::EdgeCount() const { return m_edge_count; } unsigned int ON_SubDVertex::EdgeCount( ON_SubDEdgeTag edge_tag ) const { if (nullptr != m_edges) { unsigned int matching_edge_count = 0; const unsigned int edge_count = m_edge_count; for (unsigned int vei = 0; vei < edge_count; vei++) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); if (nullptr != e && edge_tag == e->m_edge_tag) matching_edge_count++; } return matching_edge_count; } return 0; } unsigned int ON_SubDVertex::EdgeArrayIndex( const ON_SubDEdge* edge ) const { if ( nullptr == edge ) return ON_UNSET_UINT_INDEX; const unsigned int edge_count = m_edge_count; if ( 0 == edge_count) return ON_UNSET_UINT_INDEX; const ON_SubDEdgePtr* eptr = m_edges; if ( nullptr == eptr) return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); for ( unsigned int i = 0; i < edge_count; i++, eptr++) { if (edge == ON_SUBD_EDGE_POINTER(eptr->m_ptr)) return i; } return ON_UNSET_UINT_INDEX; } unsigned int ON_SubDVertex::MarkedEdgeCount() const { unsigned int mark_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 && e->m_status.RuntimeMark()) ++mark_count; } return mark_count; } bool ON_SubDVertex::ClearEdgeMarks() const { bool rc = true; 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) rc = false; else e->m_status.ClearRuntimeMark(); } return rc; } unsigned int ON_SubDVertex::MarkedFaceCount() const { unsigned int mark_count = 0; for (unsigned short vfi = 0; vfi < m_face_count; ++vfi) { const ON_SubDFace* f = m_faces[vfi]; if (nullptr != f && f->m_status.RuntimeMark()) ++mark_count; } return mark_count; } bool ON_SubDVertex::ClearFaceMarks() const { bool rc = true; for (unsigned short vfi = 0; vfi < m_face_count; ++vfi) { const ON_SubDFace* f = m_faces[vfi]; if (nullptr == f) rc = false; else f->m_status.ClearRuntimeMark(); } return rc; } unsigned int ON_SubDVertex::MinimumFaceEdgeCount() const { unsigned short min_count = 0xFFFFU; for (unsigned short vfi = 0; vfi < m_face_count; ++vfi) { const ON_SubDFace* f = m_faces[vfi]; if (nullptr != f && f->m_edge_count < min_count) min_count = f->m_edge_count; } return min_count < 0xFFFFU ? min_count : 0; } unsigned int ON_SubDVertex::MaximumFaceEdgeCount() const { unsigned short max_count = 0; for (unsigned short vfi = 0; vfi < m_face_count; ++vfi) { const ON_SubDFace* f = m_faces[vfi]; if (nullptr != f && f->m_edge_count < max_count) max_count = f->m_edge_count; } return max_count; } unsigned int ON_SubDVertex::MinimumEdgeFaceCount() const { unsigned short min_count = 0xFFFFU; 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 && e->m_face_count < min_count) min_count = e->m_face_count; } return min_count < 0xFFFFU ? min_count : 0; } unsigned int ON_SubDVertex::MaximumEdgeFaceCount() const { unsigned short max_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 && e->m_face_count > max_count) max_count = e->m_face_count; } return max_count; } unsigned int ON_SubDEdge::MarkedVertexCount() const { unsigned int mark_count = 0; for (unsigned evi = 0; evi < 2; ++evi) { const ON_SubDVertex* v = m_vertex[evi]; if (nullptr != v && v->m_status.RuntimeMark()) ++mark_count; } return mark_count; } unsigned int ON_SubDEdge::MarkedFaceCount() const { unsigned int mark_count = 0; const ON_SubDFacePtr* fptr = m_face2; for (unsigned short efi = 0; efi < m_face_count; ++efi, ++fptr) { if (2 == efi) { fptr = m_facex; if (nullptr == fptr) break; } const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr->m_ptr); if (nullptr != f && f->m_status.RuntimeMark()) ++mark_count; } return mark_count; } unsigned int ON_SubDVertex::FaceCount() const { return m_face_count; } const class ON_SubDFace* ON_SubDVertex::Face( unsigned int i ) const { //return (i < m_face_count) ? ON_SUBD_FACE_POINTER(m_faces[i].m_ptr) : nullptr; return (i < m_face_count) ? m_faces[i] : nullptr; } unsigned int ON_SubDVertex::FaceArrayIndex( const ON_SubDFace* face ) const { if ( nullptr == face ) return ON_UNSET_UINT_INDEX; const unsigned int face_count = m_face_count; if ( 0 == face_count) return ON_UNSET_UINT_INDEX; if ( nullptr == m_faces) return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); for ( unsigned int i = 0; i < face_count; i++) { if (face == m_faces[i] ) return i; } return ON_UNSET_UINT_INDEX; } unsigned int ON_SubDVertex::ReplaceFaceInArray(const ON_SubDFace * old_face, const ON_SubDFace * new_face) { unsigned vfi = (nullptr != old_face && old_face != new_face) ? FaceArrayIndex(old_face) : ON_UNSET_UINT_INDEX; if (ON_UNSET_UINT_INDEX == vfi) return ON_UNSET_UINT_INDEX; if (nullptr != new_face) { m_faces[vfi] = new_face; } else { const unsigned c = (unsigned)(m_face_count--); while (++vfi < c) m_faces[vfi - 1] = m_faces[vfi]; } return vfi; } const ON_3dPoint ON_SubDVertex::ControlNetPoint() const { return ON_3dPoint(m_P); } bool ON_SubDVertex::IsSmooth() const { return (ON_SubDVertexTag::Smooth == m_vertex_tag); } bool ON_SubDVertex::IsCrease() const { return (ON_SubDVertexTag::Crease == m_vertex_tag); } bool ON_SubDVertex::IsCorner() const { return (ON_SubDVertexTag::Corner == m_vertex_tag); } bool ON_SubDVertex::IsDart() const { return (ON_SubDVertexTag::Dart == m_vertex_tag); } bool ON_SubDVertex::IsCreaseOrCorner() const { return (ON_SubDVertexTag::Crease == m_vertex_tag || ON_SubDVertexTag::Corner == m_vertex_tag); } bool ON_SubDVertex::IsDartOrCreaseOrCorner() const { return ( 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 ); } bool ON_SubDVertex::IsSmoothOrDart() const { return (ON_SubDVertexTag::Smooth == m_vertex_tag || ON_SubDVertexTag::Dart == m_vertex_tag); } bool ON_SubDVertex::IsSmoothOrCrease() const { return (ON_SubDVertexTag::Smooth == m_vertex_tag || ON_SubDVertexTag::Crease == m_vertex_tag); } bool ON_SubDVertex::GetBoundaryVertexEdges( ON_SubDEdgePtr* eptr0, ON_SubDEdgePtr* eptr1 ) const { unsigned int vbi[2] = {}; const bool rc = GetBoundaryVertexEdgeIndices(&vbi[0], &vbi[1]); if (rc) { if (nullptr != eptr0) *eptr0 = m_edges[vbi[0]]; if (nullptr != eptr1) *eptr1 = m_edges[vbi[1]]; } else { if (nullptr != eptr0) *eptr0 = ON_SubDEdgePtr::Null; if (nullptr != eptr1) *eptr1 = ON_SubDEdgePtr::Null; } return rc; } bool ON_SubDVertex::GetBoundaryVertexEdgeIndices( unsigned* vei0, unsigned* vei1 ) const { unsigned int vbi_count = 0; unsigned int vbi[2] = {}; for (unsigned short vei = 0; vei < m_edge_count; vei++) { const ON_SubDEdge* e = m_edges[vei].Edge(); if (1 == e->m_face_count) { if (vbi_count < 2) vbi[vbi_count++] = vei; else { vbi_count = 0; break; } } } if (2 != vbi_count) vbi[0] = vbi[1] = ON_UNSET_UINT_INDEX; if (nullptr != vei0) *vei0 = vbi[0]; if (nullptr != vei1) *vei1 = vbi[1]; return (2 == vbi_count); } const ON_SubDVertexEdgeProperties ON_SubDVertex::EdgeProperties() const { ON_SubDVertexEdgeProperties ep; ep.m_edge_count = m_edge_count; ep.m_face_count = m_face_count; bool bFirstEdge = true; for (unsigned short vei = 0; vei < ep.m_edge_count; vei++) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); if (nullptr == e) { ep.m_null_edge_count++; continue; } if (e->IsCrease()) ep.m_crease_edge_count++; else if (e->IsSmooth()) ep.m_smooth_edge_count++; else ep.m_unset_edge_count++; const unsigned short edge_face_count = e->m_face_count; if (bFirstEdge) { bFirstEdge = false; ep.m_min_edge_face_count = edge_face_count; ep.m_max_edge_face_count = edge_face_count; } else if (edge_face_count < ep.m_min_edge_face_count) ep.m_min_edge_face_count = edge_face_count; else if (edge_face_count > ep.m_max_edge_face_count) ep.m_max_edge_face_count = edge_face_count; if (0 == edge_face_count) ep.m_wire_edge_count++; else if (1 == edge_face_count) ep.m_boundary_edge_count++; else if (2 == edge_face_count) ep.m_interior_edge_count++; else ep.m_nonmanifold_edge_count++; } return ep; } bool ON_SubDEdge::IsCrease() const { return (ON_SubDEdgeTag::Crease == m_edge_tag) ? true : false; } bool ON_SubDEdge::IsHardCrease() const { return ( ON_SubDEdgeTag::Crease == m_edge_tag && nullptr != m_vertex[0] && nullptr != m_vertex[1] && m_vertex[0]->IsCreaseOrCorner() && m_vertex[1]->IsCreaseOrCorner() ) ? true : false; } bool ON_SubDEdge::IsDartCrease() const { return (ON_SubDEdgeTag::Crease == m_edge_tag && DartCount() > 0 ) ? true : false; } unsigned int ON_SubDEdge::DartCount() const { unsigned int dart_count = 0; if (nullptr != m_vertex[0] && ON_SubDVertexTag::Dart == m_vertex[0]->m_vertex_tag) dart_count++; 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_SubDEdgeTag::Smooth == m_edge_tag || ON_SubDEdgeTag::SmoothX == m_edge_tag) ? true : false; } bool ON_SubDEdge::IsSmoothNotX() const { return (ON_SubDEdgeTag::Smooth == m_edge_tag) ? true : false; } bool ON_SubDEdge::IsSmoothX() const { return (ON_SubDEdgeTag::SmoothX == m_edge_tag) ? true : false; } bool ON_SubDVertex::IsSingleSectorVertex() const { const bool bIsCreaseOrCorner = IsCreaseOrCorner(); if (bIsCreaseOrCorner) { if (m_face_count < 1 || m_face_count + 1 != m_edge_count) return false; } else if (IsSmoothOrDart()) { if (m_face_count < 2 || m_edge_count!= m_face_count) return false; } else return false; unsigned short boundary_crease_count = 0; unsigned short interior_crease_count = 0; unsigned short interior_smooth_count = 0; for (unsigned short vei = 0; vei < m_edge_count; ++vei) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(this->m_edges[vei].m_ptr); if (nullptr == e) return false; if (e->IsSmooth()) { if (2 == e->m_face_count) { ++interior_smooth_count; continue; } } else if (ON_SubDEdgeTag::Crease == e->m_edge_tag) { if (2 == e->m_face_count) { ++interior_crease_count; if (ON_SubDVertexTag::Dart == m_vertex_tag && 1 == interior_crease_count) continue; } else if (1 == e->m_face_count) { ++boundary_crease_count; if (bIsCreaseOrCorner && boundary_crease_count <= 2) continue; } } return false; } if (bIsCreaseOrCorner) { if (2 == boundary_crease_count && 2+interior_smooth_count == m_edge_count) return true; } 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_SubDVertexTag::Smooth == m_vertex_tag) { if (interior_smooth_count == m_edge_count) return true; } return false; } bool ON_SubDVertex::IsManifoldBoundaryVertex() const { return IsCreaseOrCorner() && IsSingleSectorVertex(); } bool ON_SubDVertex::HasInteriorVertexTopology() const { if (m_edge_count >= 2 && m_edge_count == m_face_count && nullptr != m_edges && nullptr != m_faces) { 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 || 2 != e->m_face_count) return false; } return true; } return false; } bool ON_SubDVertex::HasBoundaryVertexTopology() const { if (m_edge_count >= 2 && m_edge_count == m_face_count+1 && nullptr != m_edges && nullptr != m_faces) { unsigned boundary_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 || 0 == e->m_face_count || e->m_face_count > 2) return false; if (1 == e->m_face_count) ++boundary_count; } if (2 == boundary_count) return true; } return false; } const ON_SubDComponentPtrPair ON_SubDVertex::BoundaryEdgePair() const { ON_SubDComponentPtrPair epair = ON_SubDComponentPtrPair::Null; if (nullptr != m_edges && m_edge_count >= 2) { for (unsigned short vei = 0; vei < m_edge_count; ++vei) { ON_SubDEdgePtr eptr = m_edges[vei]; const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr); if (nullptr == e) continue; if (false == e->HasBoundaryEdgeTopology()) continue; const ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(eptr.m_ptr); if (this != e->m_vertex[edir]) { // m_edges[vei] is corrupt ... ON_SUBD_ERROR("m_edges[vei] has incorrect edge orientation flag."); if (this == e->m_vertex[1 - edir]) eptr = eptr.Reversed(); // we can still return the requested information. else return ON_SubDComponentPtrPair::Null; } if (epair.m_pair[0].IsNull()) epair.m_pair[0] = ON_SubDComponentPtr::Create(eptr); else if (epair.m_pair[1].IsNull()) epair.m_pair[1] = ON_SubDComponentPtr::Create(eptr); else return ON_SubDComponentPtrPair::Null; // 3 or more boundary edges } } return (epair.m_pair[1].IsNotNull()) ? epair : ON_SubDComponentPtrPair::Null; } const ON_SubDComponentPtrPair ON_SubDVertex::CreasedEdgePair(bool bInteriorEdgesOnly) const { ON_SubDComponentPtrPair creased_eptr_pair = ON_SubDComponentPtrPair::Null; if (nullptr != m_edges && m_edge_count >= 2) { 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 (bInteriorEdgesOnly && false == e->HasInteriorEdgeTopology(false)) continue; 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)) { ON_SUBD_ERROR("Duplicate entries in m_edges[] list."); continue; } if (creased_eptr_pair.FirstIsNull()) creased_eptr_pair.m_pair[0] = ON_SubDComponentPtr::Create(m_edges[vei]); else if (creased_eptr_pair.SecondIsNull()) creased_eptr_pair.m_pair[1] = ON_SubDComponentPtr::Create(m_edges[vei]); else return ON_SubDComponentPtrPair::Null; // 3 or more creases } } } return creased_eptr_pair.SecondIsNull() ? ON_SubDComponentPtrPair::Null : creased_eptr_pair; } const ON_SubDEdgePtr ON_SubDVertex::CreasedEdge(bool bInteriorEdgesOnly) const { ON_SubDEdgePtr creased_eptr = ON_SubDEdgePtr::Null; if (nullptr != m_edges) { 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 (bInteriorEdgesOnly && false == e->HasInteriorEdgeTopology(false)) continue; if (ON_SubDEdgeTag::Crease == e->m_edge_tag) { if (creased_eptr.IsNull()) creased_eptr = m_edges[vei]; else return ON_SubDEdgePtr::Null; // 2 or more creases. } } } return creased_eptr; } const unsigned int ON_SubDVertex::CreasedEdgeCount() const { return CreasedEdgeCount(true, true, true, true); } 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 m_null_edge_count == 0 && m_edge_count == m_face_count && m_boundary_edge_count == 0 && m_interior_edge_count >= 2 && m_nonmanifold_edge_count == 0 ; } bool ON_SubDVertexEdgeProperties::HasBoundaryVertexTopology() const { return m_null_edge_count == 0 && m_edge_count == m_face_count+1 && m_boundary_edge_count == 2 && m_nonmanifold_edge_count == 0 ; } bool ON_SubDVertexEdgeProperties::HasManifoldVertexTopology() const { return HasInteriorVertexTopology() || HasBoundaryVertexTopology(); } bool ON_SubDVertexEdgeProperties::HasNonmanifoldVertexTopology() const { return (m_null_edge_count == 0) && (m_wire_edge_count > 0 || m_nonmanifold_edge_count > 0) ; } bool ON_SubDVertex::IsStandard() const { if (nullptr == m_edges) return false; if (nullptr == m_faces) return false; const unsigned int edge_count = m_edge_count; if (!ON_SubD::IsValidSectorEdgeCount(m_vertex_tag,edge_count)) return false; const unsigned int face_count = m_face_count; if (face_count != ON_SubDSectorType::SectorFaceCountFromEdgeCount(m_vertex_tag, edge_count)) return false; const unsigned short f_edge_count = 4; unsigned int crease_edge_face_count = ON_UNSET_UINT_INDEX; bool bTaggedVertex = false; switch (m_vertex_tag) { case ON_SubDVertexTag::Unset: return false; break; case ON_SubDVertexTag::Smooth: if (edge_count != face_count) return false; break; case ON_SubDVertexTag::Crease: if (edge_count != face_count+1) return false; crease_edge_face_count = 1; bTaggedVertex = true; break; case ON_SubDVertexTag::Corner: if (edge_count != face_count+1) return false; crease_edge_face_count = 1; bTaggedVertex = true; break; case ON_SubDVertexTag::Dart: if (edge_count != face_count) return false; crease_edge_face_count = 2; bTaggedVertex = true; break; default: return false; break; } for (unsigned int vfi = 0; vfi < face_count; vfi++) { const ON_SubDFace* f = m_faces[vfi]; if (nullptr == f) return false; if (f_edge_count != f->m_edge_count) return false; } unsigned int creased_edge_count = 0; double sector_weight = 0.0; for (unsigned int vei = 0; vei < edge_count; vei++) { const ON_SubDEdge* e = m_edges[vei].Edge(); if (nullptr == e) return false; unsigned int evi; if (this == e->m_vertex[0]) evi = 0; else if (this == e->m_vertex[1]) evi = 1; else return false; const ON_SubDVertex* other_vertex = e->m_vertex[1-evi]; if (nullptr == other_vertex) return false; if (ON_SubDEdgeTag::Smooth == e->m_edge_tag) { if (2 != e->m_face_count) return false; if (bTaggedVertex && 0 == vei) { sector_weight = e->m_sector_coefficient[evi]; if (!(0.0 <= sector_weight && sector_weight <= 1.0)) return false; } if (!(sector_weight == e->m_sector_coefficient[evi])) return false; if (ON_SubDVertexTag::Smooth == other_vertex->m_vertex_tag) { if ( !(0.0 == e->m_sector_coefficient[1-evi]) ) return false; } else { if ( bTaggedVertex ) return false; if ( !(0.5 == e->m_sector_coefficient[1-evi]) ) return false; } } else if (ON_SubDEdgeTag::Crease == e->m_edge_tag) { if (crease_edge_face_count != e->m_face_count) return false; creased_edge_count++; if (creased_edge_count > 2) return false; if (false == other_vertex->IsDartOrCreaseOrCorner()) return false; } else { return false; } } switch (creased_edge_count) { case 0: if (false == IsSmooth()) return false; break; case 1: if (false == IsDart()) return false; break; case 2: if (false == IsCreaseOrCorner()) return false; break; default: return false; } // The standard subdivison matrix will correctly calculate // the subdivision location for this vertex. return true; } unsigned int ON_SubDEdge::EdgeAttributes() const { unsigned int edge_topology_attributes = 0U; if (nullptr == m_vertex[0] || nullptr == m_vertex[1]) { edge_topology_attributes |= ON_ComponentAttributes::Damaged; } else { const double* P[2] = { m_vertex[0]->m_P, m_vertex[1]->m_P }; if ( fabs(P[0][0]) < ON_UNSET_POSITIVE_VALUE && fabs(P[0][1]) < ON_UNSET_POSITIVE_VALUE && fabs(P[0][2]) < ON_UNSET_POSITIVE_VALUE && fabs(P[1][0]) < ON_UNSET_POSITIVE_VALUE && fabs(P[1][1]) < ON_UNSET_POSITIVE_VALUE && fabs(P[1][2]) < ON_UNSET_POSITIVE_VALUE ) { if (P[0][0] == P[1][0] && P[0][1] == P[1][1] && P[0][2] == P[1][2]) edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::ZeroLength; else edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::NonzeroLength; } if (m_vertex[0] != m_vertex[1]) edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Open; else edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Closed; } const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr),ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) }; switch (m_face_count) { case 0: edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Wire; break; case 1: edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Boundary; if ( nullptr == f[0]) edge_topology_attributes |= ON_ComponentAttributes::Damaged; break; case 2: edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Interior; if (IsSmooth()) edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSmooth; else if (IsCrease()) edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorCrease; if (nullptr != f[0] && nullptr != f[1]) { if (ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr)) edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorNotOriented; else edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorOriented; if (f[0] != f[1]) { edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorTwoFaced; } else { const unsigned int fecount = f[0]->EdgeCount(); const unsigned int fei0 = f[0]->EdgeArrayIndex(this); if (fecount > 2 && fei0 < fecount) { for (unsigned int fei1 = fei0 + 1; fei1 < fecount; ++fei1) { if (this == f[0]->Edge(fei1)) { if (f[0]->EdgeDirection(fei0) != f[0]->EdgeDirection(fei1)) { if ( fei1+1 == fei0 || (0 == fei0 && fei1+1 == fecount)) edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSlit; else edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSeam; } break; } } } } } else { edge_topology_attributes |= ON_ComponentAttributes::Damaged; } break; default: edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Nonmanifold; if ( nullptr == f[0] || nullptr == f[1] || nullptr == m_facex ) edge_topology_attributes |= ON_ComponentAttributes::Damaged; break; } return edge_topology_attributes; } static int Compare_ON__UINT_PTR(const void* a, const void* b) { ON__UINT_PTR ai = *((const unsigned int*)a); ON__UINT_PTR bi = *((const unsigned int*)b); if (ai < bi) return -1; if (ai > bi) return 1; return 0; } static int ComparePointerArrays( size_t count, const ON__UINT_PTR* a, const ON__UINT_PTR* b ) { if (count <= 0) return 0; if (nullptr == a) return ((nullptr == b) ? 0 : -1); if (nullptr == b) return 1; if (1 == count) return Compare_ON__UINT_PTR(a, b); unsigned int stack_buffer[128]; unsigned int* adex = (2 * count <= sizeof(stack_buffer) / sizeof(stack_buffer[0])) ? stack_buffer : new (std::nothrow) unsigned int[2 * count]; if (nullptr == adex) return 0; unsigned int* bdex = adex + count; ON_Sort(ON::sort_algorithm::quick_sort, adex, a, count, sizeof(a[0]), Compare_ON__UINT_PTR); ON_Sort(ON::sort_algorithm::quick_sort, bdex, b, count, sizeof(b[0]), Compare_ON__UINT_PTR); int rc = 0; for (unsigned int i = 0; 0 == rc && i < count; i++) { rc = Compare_ON__UINT_PTR(a + adex[i], b + bdex[i]); } if (adex != stack_buffer) delete[] adex; return rc; } int ON_SubDVertex::CompareUnorderedEdges( const ON_SubDVertex* a, const ON_SubDVertex* b ) { if (nullptr == a) return ((nullptr == b) ? 0 : -1); if (nullptr == b) return 1; if (a->m_edge_count < b->m_edge_count) return -1; if (a->m_edge_count > b->m_edge_count) return 1; return ComparePointerArrays(a->m_edge_count, (const ON__UINT_PTR*)a->m_edges, (const ON__UINT_PTR*)b->m_edges); } int ON_SubDVertex::CompareUnorderedFaces( const ON_SubDVertex* a, const ON_SubDVertex* b ) { if (nullptr == a) return ((nullptr == b) ? 0 : -1); if (nullptr == b) return 1; if (a->m_face_count < b->m_face_count) return -1; if (a->m_face_count > b->m_face_count) return 1; return ComparePointerArrays(a->m_face_count, (const ON__UINT_PTR*)a->m_faces, (const ON__UINT_PTR*)b->m_faces); } int ON_SubDVertex::CompareUnorderedEdgesAndFaces( const ON_SubDVertex* a, const ON_SubDVertex* b ) { int rc = CompareUnorderedEdges(a, b); if (0 == rc) rc = CompareUnorderedFaces(a, b); return rc; } ////////////////////////////////////////////////////////////////////////// // // ON_SubDEdge // void ON_SubDComponentBase::CopyBaseFrom( const ON_SubDComponentBase* src, bool bCopySymmetrySetNext ) { if ( nullptr == src ) src = &ON_SubDComponentBase::Unset; *this = *src; m_subd_point1 = nullptr; Internal_ClearSurfacePointFlag(); if (bCopySymmetrySetNext) m_symmetry_set_next = src->m_symmetry_set_next; } void ON_SubDEdge::CopyFrom( const ON_SubDEdge* src, bool bReverseEdge, bool bCopyVertexArray, bool bCopyFaceArray, bool bCopySymmetrySetNext ) { if (nullptr == src) src = &ON_SubDEdge::Empty; CopyBaseFrom(src, bCopySymmetrySetNext); m_next_edge = nullptr; m_edge_tag = src->m_edge_tag; unsigned int end0 = bReverseEdge ? 1 : 0; if (bCopyVertexArray) { m_vertex[0] = src->m_vertex[end0]; m_vertex[1] = src->m_vertex[1 - end0]; } m_sector_coefficient[0] = src->m_sector_coefficient[end0]; m_sector_coefficient[1] = src->m_sector_coefficient[1 - end0]; if (bCopyFaceArray) { if (src->m_face_count > 0 && (src->m_face_count <= 2 || (nullptr != src->m_facex && nullptr != m_facex))) { m_face2[0] = src->m_face2[0]; m_face2[1] = src->m_face2[1]; unsigned int face_count = src->m_face_count; if (face_count > 2) { face_count -= 2; for (unsigned int efi = 0; efi < face_count; efi++) m_facex[efi] = src->m_facex[efi]; } m_face_count = src->m_face_count; } else m_face_count = 0; } } unsigned int ON_SubDEdge::TaggedEndIndex() const { unsigned int tagged_end_index = 3; for (unsigned int evi = 0; evi < 2; evi++) { const ON_SubDVertex* v = m_vertex[evi]; if (nullptr == v || false == v->IsDartOrCreaseOrCorner()) continue; tagged_end_index = (3 == tagged_end_index) ? evi : 2; } return tagged_end_index; } const ON_SubDFacePtr ON_SubDEdge::FacePtr( unsigned int i ) const { return (i < 2) ? m_face2[i] : ((i < m_face_count) ? m_facex[i - 2] : ON_SubDFacePtr::Null); //return (i < m_face_count) ? ((i < 2) ? m_face2[i] : m_facex[i - 2]) : ON_SubDFacePtr::Null; } unsigned ON_SubDEdge::VertexCount() const { return (nullptr != m_vertex[0]) ? ((nullptr != m_vertex[1] && m_vertex[0] != m_vertex[1]) ? 2U : 1U) : ((nullptr != m_vertex[1] ? 1U : 0U)) ; } 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; } bool ON_SubDEdge::HasBoundaryEdgeTopology() const { for (;;) { if (1 != m_face_count) break; const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_face2[0].m_ptr); if (nullptr == f) break; const ON_SubDEdgePtr feptr = f->EdgePtrFromEdge(this); if (this != ON_SUBD_EDGE_POINTER(feptr.m_ptr)) { ON_SUBD_ERROR("m_face2[0] does not reference this edge."); break; } if (ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr) != ON_SUBD_FACE_DIRECTION(feptr.m_ptr)) { ON_SUBD_ERROR("m_face2[0] has inconsistent direction flags."); break; } if (nullptr == m_vertex[0] || nullptr == m_vertex[1] || m_vertex[0] == m_vertex[1]) { ON_SUBD_ERROR("m_vertex[] has null or invalid pointers."); break; } return true; } return false; } bool ON_SubDEdge::HasInteriorEdgeTopology(bool bRequireOppositeFaceDirections) const { for (;;) { if (2 != m_face_count) break; if (bRequireOppositeFaceDirections && ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr)) break; 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] || f[0] == f[1]) break; const ON_SubDEdgePtr feptr[2] = { f[0]->EdgePtrFromEdge(this), f[1]->EdgePtrFromEdge(this) }; if (this != ON_SUBD_EDGE_POINTER(feptr[0].m_ptr)) { ON_SUBD_ERROR("m_face2[0] does not reference this edge."); break; } if (ON_SUBD_FACE_DIRECTION(feptr[0].m_ptr) != ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr)) { ON_SUBD_ERROR("m_face2[0] has inconsistent direction flags."); break; } if (this != ON_SUBD_EDGE_POINTER(feptr[1].m_ptr)) { ON_SUBD_ERROR("m_face2[1] does not reference this edge."); break; } if (ON_SUBD_FACE_DIRECTION(feptr[1].m_ptr) != ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr)) { ON_SUBD_ERROR("m_face2[1] has inconsistent direction flags."); break; } if (nullptr == m_vertex[0] || nullptr == m_vertex[1] || m_vertex[0] == m_vertex[1]) { ON_SUBD_ERROR("m_vertex[] has null or invalid pointers."); break; } return true; } return false; } const class ON_SubDFace* ON_SubDEdge::Face( unsigned int i ) const { return (i < 2) ? ON_SUBD_FACE_POINTER(m_face2[i].m_ptr) : ((i < m_face_count) ? ON_SUBD_FACE_POINTER(m_facex[i - 2].m_ptr) : nullptr); } ON__UINT_PTR ON_SubDEdge::FaceDirection( unsigned int i ) const { return (i < 2) ? ON_SUBD_FACE_DIRECTION(m_face2[i].m_ptr) : ((i < m_face_count) ? ON_SUBD_FACE_DIRECTION(m_facex[i - 2].m_ptr) : 0); } const ON_SubDFacePtr ON_SubDEdge::FacePtrFromFace( const class ON_SubDFace* f ) const { if (nullptr != f) { const ON_SubDFacePtr* fptr = m_face2; const unsigned int edge_face_count = m_face_count; for (unsigned int efi = 0; efi < edge_face_count; efi++, fptr++) { if (2 == efi) { fptr = m_facex; if (nullptr == fptr) break; } if (fptr->Face() == f) return *fptr; } } return ON_SubDFacePtr::Null; } unsigned int ON_SubDEdge::FaceArrayIndex( const ON_SubDFace* f ) const { if (nullptr == f) return ON_UNSET_UINT_INDEX; const unsigned int face_count = m_face_count; if (face_count > 0) { if (f == ON_SUBD_FACE_POINTER(m_face2[0].m_ptr)) return 0; if (face_count > 1) { if (f == ON_SUBD_FACE_POINTER(m_face2[1].m_ptr)) return 1; if (face_count > 2 && nullptr != m_facex) { const ON_SubDFacePtr* fptr = m_facex - 2; for (unsigned int efi = 2; efi < face_count; efi++) { if (f == ON_SUBD_FACE_POINTER(fptr[efi].m_ptr)) return efi; } } } } return ON_UNSET_UINT_INDEX; } unsigned int ON_SubDEdge::ReplaceFaceInArray(const ON_SubDFace * old_face, const ON_SubDFace * new_face) { unsigned efi = (nullptr != old_face && old_face != new_face) ? FaceArrayIndex(old_face) : ON_UNSET_UINT_INDEX; if (ON_UNSET_UINT_INDEX == efi) return ON_UNSET_UINT_INDEX; ON_SubDFacePtr* fptr = (efi < 2) ? &m_face2[efi] : &m_facex[efi - 2]; if (nullptr != new_face) { *fptr = ON_SubDFacePtr::Create(new_face, fptr->FaceDirection()); } else { unsigned efi1 = efi + 1; ON_SubDFacePtr* fptr1 = (efi1 < 2) ? &m_face2[efi1] : &m_facex[efi1 - 2]; for (const unsigned c = (unsigned)(m_face_count--); efi1 < c; ++efi, ++efi1) { if (2 == efi) fptr = m_facex; else if (2 == efi1) fptr1 = m_facex; *fptr++ = *fptr1++; } } return efi; } unsigned int ON_SubDEdge::VertexArrayIndex(const ON_SubDVertex * v) const { if (nullptr == v || m_vertex[0] == m_vertex[1]) return ON_UNSET_UINT_INDEX; if (v == m_vertex[0]) return 0; if (v == m_vertex[1]) return 1; return ON_UNSET_UINT_INDEX; } const ON_SubDFace* ON_SubDEdge::NeighborFace( const ON_SubDFace* face, bool bStopAtCrease ) const { if ( nullptr == face || 2 != m_face_count ) return nullptr; // Do not stop at x tags 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] ) return ON_SUBD_RETURN_ERROR(nullptr); if (face == f[0]) { if (face == f[1] ) return ON_SUBD_RETURN_ERROR(nullptr); return f[1]; } if (face == f[1]) { return f[0]; } return ON_SUBD_RETURN_ERROR(nullptr); } const ON_SubDFacePtr ON_SubDEdge::NeighborFacePtr( const ON_SubDFace* face, bool bStopAtCrease ) const { if ( nullptr == face || 2 != m_face_count ) return ON_SubDFacePtr::Null; // Do not stop at x tags 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] ) return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null); if (face == f[0]) { if (face == f[1] ) return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null); return m_face2[1]; } if (face == f[1]) { return m_face2[0]; } return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null); } const ON_SubDEdgePtr ON_SubDEdge::AdjacentEdgePtr( unsigned int edge_vertex_index, unsigned int i ) const { if ( edge_vertex_index >= 2 ) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); const ON_SubDFacePtr fptr = FacePtr(i); const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr.m_ptr); if (nullptr == f) return ON_SubDEdgePtr::Null; const unsigned int fe_count = f->m_edge_count; if ( fe_count < 3 || fe_count > ON_SubDFace::MaximumEdgeCount) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); const unsigned int fei = f->EdgeArrayIndex(this); if( fei >= fe_count) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); const ON_SubDEdgePtr eptr = f->EdgePtr(fei); if ( this != ON_SUBD_EDGE_POINTER(eptr.m_ptr)) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); const ON__UINT_PTR dir = ON_SUBD_FACE_DIRECTION(fptr.m_ptr); if (dir != ON_SUBD_EDGE_DIRECTION(eptr.m_ptr)) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); const unsigned int fei1 = (dir == ((ON__UINT_PTR)edge_vertex_index)) ? ((fei + fe_count - 1) % fe_count) : ((fei + 1) % fe_count) ; return f->EdgePtr(fei1); } const ON_SubDEdge* ON_SubDEdge::AdjacentEdge( unsigned int edge_vertex_index, unsigned int i ) const { return ON_SUBD_EDGE_POINTER(AdjacentEdgePtr(edge_vertex_index, i).m_ptr); } const ON_SubDVertex* ON_SubDEdge::OtherEndVertex( const ON_SubDVertex* vertex ) const { if (nullptr != vertex) { if (m_vertex[0] == vertex) { if (m_vertex[1] != vertex ) return m_vertex[1]; } else if (m_vertex[1] == vertex ) return m_vertex[0]; } return nullptr; } const ON_SubDEdgePtr ON_SubDEdge::FromVertices( const ON_SubDVertex* vertex0, const ON_SubDVertex* vertex1 ) { if (nullptr != vertex0 && nullptr != vertex1 && vertex0 != vertex1 && nullptr != vertex0->m_edges) { for (unsigned short vei = 0; vei < vertex0->m_edge_count; vei++) { const ON_SubDEdgePtr eptr = vertex0->m_edges[vei]; if (vertex1 == eptr.RelativeVertex(1) && vertex0 == eptr.RelativeVertex(0)) return eptr; } } return ON_SubDEdgePtr::Null; } const ON_SubDEdge* ON_SubDEdge::FromVertices( const ON_SubDVertex* vertex0, const ON_SubDVertex* vertex1, bool bIgnoreOrientation ) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(ON_SubDEdge::FromVertices(vertex0, vertex1).m_ptr); if ( nullptr != e && false == bIgnoreOrientation && vertex0 != e->m_vertex[0] ) e = nullptr; return e; } const ON_SubDFacePtr ON_SubDFace::FromVertices(const ON_SimpleArray< const ON_SubDVertex* >& vertex_list) { return ON_SubDFace::FromVertices(vertex_list.Array(), vertex_list.UnsignedCount()); } const ON_SubDFacePtr ON_SubDFace::FromVertices(const ON_SubDVertex* const* vertex_list, size_t vertex_count) { if (nullptr == vertex_list || vertex_count < 3 || vertex_count > ON_SubDFace::MaximumEdgeCount) return ON_SubDFacePtr::Null; const ON_SubDFace* candiates4[4]; const ON_SubDFace** candidates = nullptr; unsigned candidate_count = 0; const ON_SubDFace* f = nullptr; const unsigned unsigned_vertex_count = (unsigned)vertex_count; if (unsigned_vertex_count < 3) return ON_SubDFacePtr::Null; const ON_SubDVertex* v[2] = { nullptr,vertex_list[0] }; for (unsigned fei = 0; fei < unsigned_vertex_count; ++fei) { v[0] = v[1]; v[1] = vertex_list[(fei + 1) % unsigned_vertex_count]; const ON_SubDEdge* e = ON_SubDEdge::FromVertices(v[0], v[1]).Edge(); if (nullptr == e || e->m_face_count <= 0) candidate_count = 0; else if (0 == fei) { candidates = (e->m_face_count <= 4) ? candiates4 : ((const ON_SubDFace**)onmalloc(e->m_face_count * sizeof(candidates[0]))); for (unsigned short efi = 0; efi < e->m_face_count; ++efi) { const ON_SubDFace* ef = e->Face(efi); if (nullptr != ef && unsigned_vertex_count == ef->EdgeCount()) candidates[candidate_count++] = ef; } } else { unsigned c = 0; for (unsigned i = 0; i < candidate_count; ++i) { if (e->FaceArrayIndex(candidates[i]) < ON_UNSET_UINT_INDEX) candidates[c++] = candidates[i]; } candidate_count = c; } if (0 == candidate_count) break; if (1 == candidate_count) { f = candidates[0]; break; } } if (nullptr != candidates && candidates != candiates4) onfree(candidates); if (nullptr == f) return ON_SubDFacePtr::Null; const unsigned fvi0 = f->VertexIndex(vertex_list[0]); if (fvi0 >= unsigned_vertex_count) return ON_SubDFacePtr::Null; const ON__UINT_PTR dir = (vertex_list[1] == f->Vertex((fvi0 + 1) % unsigned_vertex_count)) ? 0 : 1; if (0 == dir) { for (unsigned fvi = 2; fvi < unsigned_vertex_count; fvi++) { if (vertex_list[fvi] != f->Vertex((fvi0 + fvi) % unsigned_vertex_count)) return ON_SubDFacePtr::Null; } } else { for (unsigned fvi = 1; fvi < unsigned_vertex_count; fvi++) { if (vertex_list[fvi] != f->Vertex((fvi0 + unsigned_vertex_count - fvi) % unsigned_vertex_count)) return ON_SubDFacePtr::Null; } } return ON_SubDFacePtr::Create(f, dir); } const ON_3dPoint ON_SubDEdge::ControlNetPoint( unsigned int i) const { if (i >= 2 || nullptr == m_vertex[i]) return ON_3dPoint::NanPoint; return (ON_3dPoint(m_vertex[i]->m_P)); } const ON_Line ON_SubDEdge::ControlNetLine() const { return ON_Line(ControlNetPoint(0), ControlNetPoint(1)); } const ON_3dVector ON_SubDEdge::ControlNetDirection() const { if (nullptr == m_vertex[0] || nullptr == m_vertex[1]) return ON_3dVector::NanVector; const ON_3dPoint P[2] = { ON_3dPoint(m_vertex[0]->m_P) ,ON_3dPoint(m_vertex[1]->m_P) }; return (P[0].IsValid() && P[1].IsValid()) ? (P[1] - P[0]) : ON_3dVector::NanVector; } const ON_3dVector ON_SubDEdge::ControlNetDirectionFrom( const ON_SubDVertex* v ) const { if (nullptr != v) { if (v == m_vertex[0] && nullptr != m_vertex[1]) return ControlNetDirection(); if (v == m_vertex[1] && nullptr != m_vertex[0]) return -ControlNetDirection(); } return ON_3dVector::NanVector; } ////////////////////////////////////////////////////////////////////////// // // ON_SubDFace // //bool ON_SubDFace::IsOrdinary( // ON_SubD::SubDType subdivision_type, // bool bTestFaces // ) const //{ // unsigned int ordinary_face_edge_count = 0; // if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) // ordinary_face_edge_count = 4; // else if (ON_SubD::SubDType::TriLoopWarren == subdivision_type) // ordinary_face_edge_count = 3; // else // return false; // // if (ordinary_face_edge_count != m_edge_count) // return false; // // for (unsigned int fei = 0; fei < ordinary_face_edge_count; fei++) // { // 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_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_SubDVertexTag::Unset,bTestFaces)) // return false; // } // return true; //} void ON_SubDFace::CopyFrom( const ON_SubDFace* src, bool bCopyEdgeArray, bool bCopySymmetrySetNext ) { if (nullptr == src) src = &ON_SubDFace::Empty; CopyBaseFrom(src, bCopySymmetrySetNext); m_next_face = nullptr; m_material_channel_index = src->m_material_channel_index; m_per_face_color = src->m_per_face_color; m_level_zero_face_id = src->m_level_zero_face_id; if (bCopyEdgeArray) { if (src->m_edge_count > 0 && (src->m_edge_count <= 4 || (nullptr != src->m_edgex && nullptr != m_edgex))) { m_edge4[0] = src->m_edge4[0]; m_edge4[1] = src->m_edge4[1]; m_edge4[2] = src->m_edge4[2]; m_edge4[3] = src->m_edge4[3]; unsigned int edge_count = src->m_edge_count; if (edge_count > 4) { edge_count -= 4; for (unsigned int fei = 0; fei < edge_count; fei++) m_edgex[fei] = src->m_edgex[fei]; } m_edge_count = src->m_edge_count; } else m_edge_count = 0; } // 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( unsigned int i ) const { return (i < 4) ? m_edge4[i] : ((i < m_edge_count) ? m_edgex[i-4] : ON_SubDEdgePtr::Null); } unsigned int ON_SubDFace::EdgeCount() const { return m_edge_count; } bool ON_SubDFace::HasEdges() const { if (m_edge_count < 3 || m_edge_count > ON_SubDFace::MaximumEdgeCount) return false; if (m_edge_count > 4 + m_edgex_capacity) return false; const ON_SubDEdgePtr* eptr = m_edge4; const ON_SubDVertex* v0 = nullptr; const ON_SubDVertex* v1 = nullptr; const ON_SubDVertex* ev[2]; for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++eptr) { if (4 == fei) { eptr = m_edgex; if (nullptr == eptr) return false; if (m_edge_count > 4 + m_edgex_capacity) return false; } const ON__UINT_PTR ptr = eptr->m_ptr; const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(ptr); if (nullptr == e) return false; if (0 == e->m_face_count) return false; if (e->m_face_count > 2 + e->m_facex_capacity) return false; if (0 == ON_SUBD_EDGE_DIRECTION(ptr)) { ev[0] = e->m_vertex[0]; ev[1] = e->m_vertex[1]; } else { ev[0] = e->m_vertex[1]; ev[1] = e->m_vertex[0]; } if (nullptr == ev[0] || nullptr == ev[1] || ev[0] == ev[1]) return false; if (nullptr == v0) v0 = ev[0]; else if (v1 != ev[0]) return false; v1 = ev[1]; if (v1->m_edge_count < 2 || v1->m_edge_count > v1->m_edge_capacity) return false; if (v1->m_face_count < 1 || v1->m_face_count > v1->m_face_capacity) return false; } if ( v0 != v1) return false; return true; } unsigned int ON_SubDFace::MarkedEdgeCount() const { unsigned int marked_edge_count = 0; const ON_SubDEdgePtr* eptr = m_edge4; for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr) { if (4 == fei) { eptr = m_edgex; if (nullptr == eptr) break; } const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr != e && e->m_status.RuntimeMark()) ++marked_edge_count; } return marked_edge_count; } unsigned int ON_SubDFace::SetEdgeMarks(bool bMark) const { bMark = bMark ? true : false; // so exact compare can be used unsigned int changed_mark_count = 0; const ON_SubDEdgePtr* eptr = m_edge4; for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr) { if (4 == fei) { eptr = m_edgex; if (nullptr == eptr) break; } const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr != e && bMark != e->m_status.RuntimeMark()) { e->m_status.SetRuntimeMark(bMark); ++changed_mark_count; } } return changed_mark_count; } unsigned int ON_SubDFace::GetEdgeArray( ON_SimpleArray< ON_SubDEdgePtr >& face_edge_array ) const { face_edge_array.SetCount(0); const unsigned edge_count = m_edge_count; face_edge_array.Reserve(edge_count); face_edge_array.Append(edge_count <= 4 ? edge_count : 4U, m_edge4); if (edge_count > 4) { if ( nullptr != m_edgex ) face_edge_array.Append(edge_count-4U, m_edgex); else { for (unsigned fei = 4; fei < edge_count; ++fei) face_edge_array.Append(ON_SubDEdgePtr::Null); } } return edge_count; } unsigned int ON_SubDFace::SetVertexMarks(bool bMark) const { bMark = bMark ? true : false; // so exact compare can be used unsigned int changed_mark_count = 0; const ON_SubDEdgePtr* eptr = m_edge4; for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr) { if (4 == fei) { eptr = m_edgex; if (nullptr == eptr) break; } const ON_SubDVertex* v = eptr->RelativeVertex(0); if (nullptr != v && bMark != v->m_status.RuntimeMark()) { v->m_status.SetRuntimeMark(bMark); ++changed_mark_count; } } return changed_mark_count; } unsigned int ON_SubDFace::MarkedVertexCount() const { unsigned int marked_vertex_count = 0; const ON_SubDEdgePtr* eptr = m_edge4; for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr) { if (4 == fei) { eptr = m_edgex; if (nullptr == eptr) break; } const ON_SubDVertex* v = eptr->RelativeVertex(0); if (nullptr != v && v->m_status.RuntimeMark()) ++marked_vertex_count; } return marked_vertex_count; } bool ON_SubDFace::EdgeMark( unsigned int i, bool bMissingEdgeReturnValue ) const { const ON_SubDEdge* e = Edge(i); return (nullptr != e) ? e->Mark() : bMissingEdgeReturnValue; } ON__UINT8 ON_SubDFace::EdgeMarkBits( unsigned int i, ON__UINT8 missing_edge_markbits ) const { const ON_SubDEdge* e = Edge(i); return (nullptr != e) ? e->MarkBits() : missing_edge_markbits; } bool ON_SubDFace::VertexMark( unsigned int i, bool bMissingVertexReturnValue ) const { const ON_SubDVertex* v = Vertex(i); return (nullptr != v) ? v->Mark() : bMissingVertexReturnValue; } ON__UINT8 ON_SubDFace::VertexMarkBits( unsigned int i, ON__UINT8 missing_vertex_markbits ) const { const ON_SubDVertex* v = Vertex(i); return (nullptr != v) ? v->MarkBits() : missing_vertex_markbits; } const class ON_SubDVertex* ON_SubDFace::Vertex( unsigned int i ) const { ON_SubDEdge* e; const ON__UINT_PTR edge_ptr = (i < 4) ? m_edge4[i].m_ptr : ((i < m_edge_count) ? m_edgex[i - 4].m_ptr : 0); return (nullptr != (e = ON_SUBD_EDGE_POINTER(edge_ptr))) ? e->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)] : nullptr; } const ON_3dPoint ON_SubDFace::ControlNetPoint(unsigned int i) const { const ON_SubDEdge* e; const ON__UINT_PTR edge_ptr = (i < 4) ? m_edge4[i].m_ptr : ((i < m_edge_count) ? m_edgex[i - 4].m_ptr : 0); const ON_SubDVertex* v = (nullptr != (e = ON_SUBD_EDGE_POINTER(edge_ptr))) ? e->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)] : nullptr; return (nullptr != v) ? ON_3dPoint(v->m_P) : ON_3dPoint::NanPoint; } const ON_SubDVertex* ON_SubDFace::QuadOppositeVertex( const ON_SubDVertex* vertex ) const { if ( nullptr == vertex ) return ON_SUBD_RETURN_ERROR(nullptr); if ( 4 != m_edge_count) return nullptr; // not an error ON__UINT_PTR ptr0 = m_edge4[0].m_ptr; const ON_SubDEdge* e0 = ON_SUBD_EDGE_POINTER(ptr0); if ( nullptr == e0 ) return ON_SUBD_RETURN_ERROR(nullptr); ptr0 = ON_SUBD_EDGE_DIRECTION(ptr0); ON__UINT_PTR ptr2 = m_edge4[2].m_ptr; const ON_SubDEdge* e2 = ON_SUBD_EDGE_POINTER(ptr2); if ( nullptr == e2 ) return ON_SUBD_RETURN_ERROR(nullptr); ptr2 = ON_SUBD_EDGE_DIRECTION(ptr2); if (vertex == e0->m_vertex[ptr0]) return e2->m_vertex[ptr2]; if (vertex == e0->m_vertex[1-ptr0]) return e2->m_vertex[1-ptr2]; if (vertex == e2->m_vertex[ptr2]) return e0->m_vertex[ptr0]; if (vertex == e2->m_vertex[1-ptr2]) return e0->m_vertex[1-ptr0]; return ON_SUBD_RETURN_ERROR(nullptr); } const ON_SubDEdge* ON_SubDFace::QuadOppositeEdge( const ON_SubDEdge* edge ) const { if ( nullptr == edge ) return ON_SUBD_RETURN_ERROR(nullptr); if ( 4 != m_edge_count) return nullptr; // not an error for (unsigned int fei = 0; fei < 4; fei++) { const ON_SubDEdge* e0 = ON_SUBD_EDGE_POINTER(m_edge4[fei].m_ptr); if (nullptr == e0) return ON_SUBD_RETURN_ERROR(nullptr); if (e0 == edge) { e0 = ON_SUBD_EDGE_POINTER(m_edge4[(fei + 2) % 4].m_ptr); if (nullptr == e0) return ON_SUBD_RETURN_ERROR(nullptr); return e0; } } return ON_SUBD_RETURN_ERROR(nullptr); } const class ON_SubDEdge* ON_SubDFace::Edge( unsigned int i ) const { return (i < 4) ? ON_SUBD_EDGE_POINTER(m_edge4[i].m_ptr) : ((i < m_edge_count) ? ON_SUBD_EDGE_POINTER(m_edgex[i - 4].m_ptr) : nullptr); } ON__UINT_PTR ON_SubDFace::EdgeDirection( unsigned int i ) const { return (i < 4) ? ON_SUBD_EDGE_DIRECTION(m_edge4[i].m_ptr) : ((i < m_edge_count) ? ON_SUBD_EDGE_DIRECTION(m_edgex[i - 4].m_ptr) : 0); } const ON_SubDEdgePtr ON_SubDFace::EdgePtrFromEdge( const class ON_SubDEdge* e ) const { if (nullptr != e) { const ON_SubDEdgePtr* eptr = m_edge4; const unsigned int face_edge_count = m_edge_count; for (unsigned int fei = 0; fei < face_edge_count; fei++, eptr++) { if (4 == fei) { eptr = m_edgex; if (nullptr == eptr) break; } if (e == ON_SUBD_EDGE_POINTER(eptr->m_ptr)) return *eptr; } } return ON_SubDEdgePtr::Null; } unsigned int ON_SubDFace::EdgeArrayIndex( const ON_SubDEdge* e ) const { if (nullptr != e) { const ON_SubDEdgePtr* eptr = m_edge4; const unsigned int face_edge_count = m_edge_count; for (unsigned int fei = 0; fei < face_edge_count; fei++, eptr++) { if (4 == fei) { eptr = m_edgex; if (nullptr == eptr) break; } if (e == ON_SUBD_EDGE_POINTER(eptr->m_ptr)) return fei; } } return ON_UNSET_UINT_INDEX; } const ON_SubDEdge* ON_SubDFace::PrevEdge( const ON_SubDEdge* edge ) const { unsigned int edge_index = EdgeArrayIndex(edge); if (ON_UNSET_UINT_INDEX == edge_index) return nullptr; const unsigned int edge_count = m_edge_count; edge_index = (edge_index + (edge_count - 1)) % edge_count; return Edge(edge_index); } const ON_SubDEdge* ON_SubDFace::NextEdge( const ON_SubDEdge* edge ) const { unsigned int edge_index = EdgeArrayIndex(edge); if (ON_UNSET_UINT_INDEX == edge_index) return nullptr; edge_index = (edge_index + 1) % ((unsigned int)m_edge_count); return Edge(edge_index); } unsigned int ON_SubDFace::PrevEdgeArrayIndex( unsigned int edge_array_index ) const { const unsigned int edge_count = m_edge_count; return (edge_array_index < edge_count) ? ((edge_array_index + edge_count - 1) % edge_count) : ON_UNSET_UINT_INDEX; } unsigned int ON_SubDFace::NextEdgeArrayIndex( unsigned int edge_array_index ) const { const unsigned int edge_count = m_edge_count; return (edge_array_index < edge_count) ? ((edge_array_index + 1) % edge_count) : ON_UNSET_UINT_INDEX; } bool ON_SubDVertex::RemoveEdgeFromArray(const ON_SubDEdge * e) { if (nullptr == e || 0 == m_edge_count || nullptr == m_edges) return ON_SUBD_RETURN_ERROR(false); unsigned short new_count = 0; for (unsigned short vei = 0; vei < m_edge_count; ++vei) { const ON_SubDEdgePtr eptr = m_edges[vei]; if (e == ON_SUBD_EDGE_POINTER(eptr.m_ptr)) continue; m_edges[new_count++] = eptr; } if (new_count == m_edge_count) return false; m_edge_count = new_count; return true; } bool ON_SubDVertex::RemoveFaceFromArray(const ON_SubDFace * f) { if (nullptr == f || 0 == m_face_count || nullptr == m_faces) return ON_SUBD_RETURN_ERROR(false); unsigned short new_count = 0; for (unsigned short vfi = 0; vfi < m_face_count; ++vfi) { const ON_SubDFace* vf = m_faces[vfi]; if (f == vf) continue; m_faces[new_count++] = vf; } if (new_count == m_face_count) return false; m_face_count = new_count; return true; } bool ON_SubD::RemoveEdgeVertexConnection( ON_SubDEdge* e, ON_SubDVertex* v ) { if (nullptr == e || nullptr == v) return false; if (v == e->m_vertex[0]) e->m_vertex[0] = nullptr; if (v == e->m_vertex[1]) e->m_vertex[1] = nullptr; return v->RemoveEdgeFromArray(e); } ON_SubDVertex* ON_SubD::RemoveEdgeVertexConnection( ON_SubDEdge* e, unsigned evi ) { if (nullptr == e) return nullptr; ON_SubDVertex* v = const_cast((nullptr != e && evi >= 0 && evi <= 1) ? e->m_vertex[evi] : nullptr); return RemoveEdgeVertexConnection(e, v) ? v : nullptr; } ON_SubDVertexTag ON_SubDVertex::SuggestedVertexTag( bool bApplyInputTagBias, bool bReturnBestGuessWhenInvalid ) const { unsigned wire_count = 0; unsigned boundary_count = 0; unsigned interior_count = 0; unsigned crease_count = 0; const unsigned edge_count = (nullptr != m_edges ? m_edge_count : 0U); if ( edge_count < 2) return ON_SubDVertexTag::Corner; for (unsigned vei = 0; vei < edge_count; ++vei) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); if (nullptr == e) continue; switch(e->m_face_count) { case 0: ++wire_count; ++crease_count; break; case 1: ++boundary_count; ++crease_count; break; case 2: ++interior_count; if (ON_SubDEdgeTag::Crease == e->m_edge_tag) ++crease_count; break; default: // nonmanifold edge return ON_SubDVertexTag::Corner; break; } } if (crease_count >= 3) return ON_SubDVertexTag::Corner; if (wire_count > 0) { if (2 == wire_count && 0 == boundary_count && 0 == interior_count) return (bApplyInputTagBias && ON_SubDVertexTag::Corner == m_vertex_tag) ? ON_SubDVertexTag::Corner : ON_SubDVertexTag::Crease; return ON_SubDVertexTag::Corner; } ON_SubDVertexTag best_guess_tag = ON_SubDVertexTag::Unset; // crease_count >= 3 handled above switch (crease_count) { case 0: if (interior_count >= 2) return ON_SubDVertexTag::Smooth; if (bReturnBestGuessWhenInvalid) { // can occure when there is a nullptr edge best_guess_tag = ON_SubDVertexTag::Smooth; } break; case 1: if (0 == boundary_count && interior_count >= 2) 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_SubDVertexTag::Corner; } break; case 2: if( (0 == boundary_count && interior_count >= 2) || (2 == boundary_count) ) 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_SubDVertexTag::Corner; } break; } return best_guess_tag; } bool ON_SubDEdge::RemoveFaceFromArray( const ON_SubDFace* f ) { unsigned int i; if (nullptr == f) return false; if (m_face_count <= 2) { for (i = 0; i < m_face_count; i++) { if (f == ON_SUBD_FACE_POINTER(m_face2[i].m_ptr)) { for (i++; i < m_face_count; i++) m_face2[i - 1] = m_face2[i]; m_face_count--; return true; } } } else { for (i = 0; i < 2; i++) { if (f == ON_SUBD_FACE_POINTER(m_face2[i].m_ptr)) { for (i++; i < 2; i++) m_face2[i - 1] = m_face2[i]; m_face2[1] = m_facex[0]; for (i = 3; i < m_face_count; i++) m_facex[i - 3] = m_facex[i - 2]; m_face_count--; return true; } } for (i = 2; i < m_face_count; i++) { 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]; m_face_count--; return true; } } } return false; } bool ON_SubDEdge::AddFaceToArray( ON_SubDFacePtr face_ptr ) { if (m_face_count < 2) m_face2[m_face_count] = face_ptr; else if (nullptr != m_facex && m_face_count < 2 + m_facex_capacity) m_facex[m_face_count - 2] = face_ptr; else { // not enough room in m_facex. // If you really are trying to make a non-manifold subd, // then use ON_SubD::GrowEdgeFaceArray(). return ON_SUBD_RETURN_ERROR(false); } m_face_count++; return true; } bool ON_SubDEdge::RemoveFaceFromArray( unsigned int i, ON_SubDFacePtr& removed_face ) { removed_face = ON_SubDFacePtr::Null; unsigned int count = m_face_count; if ( i >= count ) return ON_SUBD_RETURN_ERROR(false); if (i < 2) { removed_face = m_face2[i]; } if (count > 2) { if ( nullptr == m_facex || m_facex_capacity + ((unsigned short)2) < m_face_count ) return ON_SUBD_RETURN_ERROR(false); if ( i >= 2 ) removed_face = m_facex[i-2]; } unsigned int j = i+1; while (j < 2 && j < count ) m_face2[i++] = m_face2[j++]; if (count > 2) { m_face2[1] = m_facex[0]; i = 0; j = 1; count -= 2; while (j < count ) m_facex[i++] = m_facex[j++]; } m_face_count--; return true; } bool ON_SubDFace::ReplaceEdgeInArray( unsigned int fei0, ON_SubDEdge* edge_to_remove, ON_SubDEdge* edge_to_insert ) { const unsigned int face_edge_count = m_edge_count; ON_SubDEdgePtr* eptr = m_edge4; for (unsigned int fei = 0; fei < face_edge_count; fei++, eptr++) { if (4 == fei) { eptr = m_edgex; if (nullptr == eptr) break; } if (fei >= fei0 && edge_to_remove == eptr->Edge() ) { const ON__UINT_PTR edir = eptr->EdgeDirection(); *eptr = ON_SubDEdgePtr::Create(edge_to_insert,edir); return true; } } return false; } bool ON_SubDFace::RemoveEdgeFromArray( unsigned int i, ON_SubDEdgePtr& removed_edge ) { removed_edge = ON_SubDEdgePtr::Null; unsigned int count = m_edge_count; if ( i >= count ) return ON_SUBD_RETURN_ERROR(false); if (i < 4) { removed_edge = m_edge4[i]; } if (count > 4) { if ( nullptr == m_edgex || m_edgex_capacity + ((unsigned short)4) < m_edge_count ) return ON_SUBD_RETURN_ERROR(false); if ( i >= 4 ) removed_edge = m_edgex[i-4]; } unsigned int j = i+1; while (j < count) { 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--; return true; } bool ON_SubDFace::RotateEdgeArray( unsigned int fei0 ) { if (0 == fei0) return true; const unsigned int edge_count = m_edge_count; if (edge_count < 2 || edge_count > ON_SubDFace::MaximumEdgeCount || fei0 >= edge_count) return false; ON_SubDEdgePtr stack_eptr[8]; ON_SubDEdgePtr* eptr = (edge_count*sizeof(stack_eptr[0]) > sizeof(stack_eptr)) ? ((ON_SubDEdgePtr*)onmalloc(edge_count * sizeof(eptr[0]))) : stack_eptr; if (nullptr == eptr) return false; ON_SubDEdgePtr* feptr = m_edge4; for (unsigned int fei = 0; fei < edge_count; fei++) { if (4 == fei) { feptr = m_edgex; if (nullptr == feptr) { if ( eptr != stack_eptr ) onfree(eptr); return false; } } eptr[fei] = *feptr++; } feptr = m_edge4; for (unsigned int fei = 0; fei < edge_count; fei++) { if (4 == fei) feptr = m_edgex; *feptr++ = eptr[(fei + fei0) % edge_count]; } if ( eptr != stack_eptr ) onfree(eptr); return true; } bool ON_SubDFace::RemoveEdgeFromArray( const ON_SubDEdge* e ) { unsigned int i; if (nullptr == e) return false; if (m_edge_count <= 4) { for (i = 0; i < m_edge_count; i++) { if (e == ON_SUBD_EDGE_POINTER(m_edge4[i].m_ptr)) { for (i++; i < m_edge_count; i++) m_edge4[i - 1] = m_edge4[i]; m_edge_count--; m_edge4[m_edge_count] = ON_SubDEdgePtr::Null; return true; } } } else { for (i = 0; i < 4; i++) { if (e == ON_SUBD_EDGE_POINTER(m_edge4[i].m_ptr)) { for (i++; i < 4; i++) m_edge4[i - 1] = m_edge4[i]; m_edge4[3] = m_edgex[0]; for (i = 5; i < m_edge_count; i++) m_edgex[i - 5] = m_edgex[i - 4]; m_edge_count--; m_edgex[m_edge_count-4] = ON_SubDEdgePtr::Null; return true; } } for (i = 4; i < m_edge_count; i++) { if (e == ON_SUBD_EDGE_POINTER(m_edgex[i - 4].m_ptr)) { for (i++; i < m_edge_count; i++) m_edgex[i - 5] = m_edgex[i - 4]; m_edge_count--; m_edgex[m_edge_count-4] = ON_SubDEdgePtr::Null; return true; } } } return false; } unsigned int ON_SubDFace::VertexIndex( const ON_SubDVertex* vertex ) const { if (nullptr == vertex) return ON_UNSET_UINT_INDEX; const ON_SubDEdgePtr* face_edges = m_edge4; const unsigned int edge_count = m_edge_count; for (unsigned int i = 0; i < edge_count; i += 2) { if (4 == i) { face_edges = m_edgex; if (nullptr == face_edges) break; face_edges -= 4; } ON__UINT_PTR e_ptr = face_edges[i].m_ptr; const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(e_ptr); if (nullptr != edge) { if (vertex == edge->m_vertex[0]) return (0 == ON_SUBD_EDGE_DIRECTION(e_ptr)) ? i : ((i + 1) % edge_count); if (vertex == edge->m_vertex[1]) return (0 == ON_SUBD_EDGE_DIRECTION(e_ptr)) ? ((i + 1) % edge_count) : i; } } return ON_UNSET_UINT_INDEX; } const ON_SubDComponentPtrPair ON_SubDFace::VertexEdgePair( const ON_SubDVertex* vertex ) const { return ON_SubDFace::VertexEdgePair(VertexIndex(vertex)); } const ON_SubDComponentPtrPair ON_SubDFace::VertexEdgePair( unsigned vertex_index ) const { for (;;) { const unsigned edge_count = m_edge_count; if (edge_count < 3) break; if (vertex_index >= edge_count) break; if (edge_count > 4 && nullptr == m_edgex) break; const unsigned fei0 = (vertex_index + (edge_count - 1)) % edge_count; const ON_SubDEdgePtr eptr[2] = { (fei0 < 4 ? m_edge4[fei0] : m_edgex[fei0 - 4]), (vertex_index < 4 ? m_edge4[vertex_index] : m_edgex[vertex_index - 4]), }; const ON_SubDVertex* v = eptr[0].RelativeVertex(1); if (nullptr == v || v != eptr[1].RelativeVertex(0)) break; return ON_SubDComponentPtrPair::Create( ON_SubDComponentPtr::Create(eptr[0]), ON_SubDComponentPtr::Create(eptr[1]) ); } return ON_SubDComponentPtrPair::Null; } ////////////////////////////////////////////////////////////////////////// // // ON_SubD // ON_OBJECT_IMPLEMENT(ON_SubD,ON_Geometry,"F09BA4D9-455B-42C3-BA3B-E6CCACEF853B"); ON_SubD::ON_SubD() ON_NOEXCEPT : ON_Geometry() {} ON_SubD::~ON_SubD() { this->Destroy(); } #if defined(ON_HAS_RVALUEREF) ON_SubD::ON_SubD( ON_SubD&& src ) ON_NOEXCEPT : ON_Geometry(std::move(src)) , m_subdimple_sp(std::move(src.m_subdimple_sp)) {} ON_SubD& ON_SubD::operator=( ON_SubD&& src ) { if ( this != &src ) { this->Destroy(); ON_Geometry::operator=(std::move(src)); m_subdimple_sp = std::move(src.m_subdimple_sp); } return *this; } #endif ON__UINT64 ON_SubD::RuntimeSerialNumber() const { ON_SubDimple* subdimple = m_subdimple_sp.get(); return (nullptr != subdimple) ? subdimple->RuntimeSerialNumber : 0; } ON__UINT64 ON_SubD::GeometryContentSerialNumber() const { const ON_SubDimple* subdimple = m_subdimple_sp.get(); return (nullptr != subdimple) ? subdimple->GeometryContentSerialNumber() : 0; } const ON_SubDHash ON_SubD::SubDHash( ON_SubDHashType hash_type, bool bForceUpdate ) const { ON_SubDimple* subdimple = m_subdimple_sp.get(); return (nullptr != subdimple) ? subdimple->SubDHash(hash_type, bForceUpdate) : ON_SubDHash::Create(hash_type , *this); } const ON_SHA1_Hash ON_SubD::GeometryHash() const { return this->SubDHash(ON_SubDHashType::Geometry, false).SubDHash(); } const ON_SHA1_Hash ON_SubD::TopologyHash() const { return this->SubDHash(ON_SubDHashType::Topology, false).SubDHash(); } const ON_SubDHash ON_SubDimple::SubDHash( ON_SubDHashType hash_type, bool bForceUpdate ) const { const unsigned vertex_count = this->ActiveLevel().m_vertex_count; if (0 == vertex_count) return ON_SubDHash::Empty; // m_subd_toplologyX_hash, m_subd_toplology_and_edge_crease_hash, and m_subd_geometry_hash // are mutable and use lazy evaluation to stay updated. // subd.GeometryContentSerialNumber() is used to detect stale values. ON_SubDHash* h; switch (hash_type) { case ON_SubDHashType::Topology: h = &this->m_subd_toplology_hash; break; case ON_SubDHashType::TopologyAndEdgeCreases: h = &this->m_subd_toplology_and_edge_creases_hash; break; case ON_SubDHashType::Geometry: h = &this->m_subd_geometry_hash; break; default: h = nullptr; break; } if ( nullptr == h) return ON_SubDHash::Empty; const unsigned edge_count = this->ActiveLevel().m_edge_count; const unsigned face_count = this->ActiveLevel().m_face_count; const ON__UINT64 rsn = this->RuntimeSerialNumber; const ON__UINT64 gsn = this->GeometryContentSerialNumber(); if ( false == bForceUpdate && h->IsNotEmpty() && hash_type == h->HashType() && rsn > 0 && rsn == h->SubDRuntimeSerialNumber() && gsn > 0 && gsn == h->SubDGeometryContentSerialNumber() && vertex_count == h->VertexCount() && edge_count == h->EdgeCount() && face_count == h->FaceCount() ) { // The chache hash values are up to date (or should be). // If h is out of date, something somewhere modified the SubD components and // failed to change the GeometryContentSerialNumber(). // All C++ SDK opennurbs code changes gsn after modifying SubD geometry (or it's a bug that should be fixed). // The unwashed masses can do just about anything and that's why the bForceUpdate parameter is supplied. return *h; } // update cached value *h = ON_SubDHash::Create(hash_type,this); // return updated value return *h; } 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 { ON_SubDimple* subdimple = m_subdimple_sp.get(); return (nullptr != subdimple) ? subdimple->ComponentStatusSerialNumber() : 0; } 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->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) { if (ON_SubDComponentLocation::Surface == subd_appearance) return ON_SubDComponentLocation::ControlNet; if (ON_SubDComponentLocation::ControlNet == subd_appearance) return ON_SubDComponentLocation::Surface; return subd_appearance; } ON_SubDComponentLocation ON_SubDMeshFragmentIterator::SubDAppearance() const { return (ON_SubDComponentLocation::Surface == m_subd_appearance_override || ON_SubDComponentLocation::ControlNet == m_subd_appearance_override) ? m_subd_appearance_override : SubD().SubDAppearance(); } void ON_SubDMeshFragmentIterator::SetSubDAppearanceOverride(ON_SubDComponentLocation subd_appearance_override) { if (m_subd_appearance_override != subd_appearance_override ) m_subd_appearance_override = subd_appearance_override; } ON_SubDComponentLocation ON_SubD::SubDAppearance() const { const ON_SubDimple* subdimple = this->SubDimple(); return (nullptr != subdimple) ? subdimple->SubDAppearance() : ON_SubD::DefaultSubDAppearance; } ON_SubDComponentLocation ON_SubDimple::SubDAppearance() const { return m_subd_appearance; } void ON_SubD::SetSubDAppearance(ON_SubDComponentLocation subd_appearance) const { if ( subd_appearance != SubDAppearance() && (ON_SubDComponentLocation::Surface == subd_appearance || ON_SubDComponentLocation::ControlNet == subd_appearance) ) { const ON_SubDimple* subdimple = const_cast(this)->SubDimple(true); if (nullptr != subdimple) subdimple->SetSubDAppearance(subd_appearance); } } void ON_SubDimple::SetSubDAppearance(ON_SubDComponentLocation subd_appearance) const { if ( subd_appearance != m_subd_appearance && (ON_SubDComponentLocation::Surface == subd_appearance || ON_SubDComponentLocation::ControlNet == subd_appearance) ) { m_subd_appearance = subd_appearance; } } ////////////////////////////////////////////////////////////////////////// // // ON_SubD - ON_Object overrides // //virtual void ON_SubD::MemoryRelocate() { } static bool ON_SubDIsNotValid(bool bSilentError) { ON_SubDIncrementErrorCount(); return bSilentError ? false : ON_IsNotValid(); } static bool EdgeSectorCoefficientIsSet( double edge_sector_coefficient ) { return (0.0 < edge_sector_coefficient && edge_sector_coefficient < 1.0); } static bool EdgeSectorWeightIsValid( double edge_vertex_weight, const ON_SubDEdge* edge ) { if (0.0 <= edge_vertex_weight && edge_vertex_weight < 1.0) return true; if (ON_SubDSectorType::UnsetSectorCoefficient == edge_vertex_weight && nullptr != edge && 0 == edge->SubdivisionLevel()) return true; return false; } static bool IsValidVertexEdgeLink( const ON_SubDVertex* vertex, const ON_SubDEdge* edge, ON__UINT_PTR end_index, bool bSilentError ) { if (nullptr == vertex || nullptr == edge) return ON_SubDIsNotValid(bSilentError); if (end_index > 1) return ON_SubDIsNotValid(bSilentError); if (edge->m_vertex[end_index] != vertex) return ON_SubDIsNotValid(bSilentError); if (vertex->SubdivisionLevel() != edge->SubdivisionLevel()) return ON_SubDIsNotValid(bSilentError); if (false == EdgeSectorWeightIsValid(edge->m_sector_coefficient[end_index],edge)) return ON_SubDIsNotValid(bSilentError); if ( edge->IsSmooth() ) { // 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); } else { const unsigned int tagged_end_index = edge->TaggedEndIndex(); if (ON_SubDEdgeTag::SmoothX == edge->m_edge_tag) { if (2 != tagged_end_index) return ON_SubDIsNotValid(bSilentError); } else { if (tagged_end_index != (unsigned int)end_index) return ON_SubDIsNotValid(bSilentError); } ON_SubDSectorType st = ON_SubDSectorType::Create(edge,(unsigned int)end_index); if (!st.IsValid()) return ON_SubDIsNotValid(bSilentError); const double expected_sector_coefficient = st.SectorCoefficient(); if (false == (expected_sector_coefficient == edge->m_sector_coefficient[end_index])) return ON_SubDIsNotValid(bSilentError); if (false == EdgeSectorCoefficientIsSet(expected_sector_coefficient)) return ON_SubDIsNotValid(bSilentError); } } 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_SubDVertexTag::Smooth == vertex->m_vertex_tag) return ON_SubDIsNotValid(bSilentError); if (ON_SubDVertexTag::Unset == vertex->m_vertex_tag) return ON_SubDIsNotValid(bSilentError); } else { return ON_SubDIsNotValid(bSilentError); } return true; } static bool IsValidVertexFaceLink( const ON_SubDVertex* vertex, const ON_SubDFace* face, unsigned int vertex_face_index, unsigned int face_vertex_index, bool bSilentError ) { if (nullptr == vertex || nullptr == face) return ON_SubDIsNotValid(bSilentError); if (vertex->SubdivisionLevel() != face->SubdivisionLevel()) return ON_SubDIsNotValid(bSilentError); const unsigned int vertex_face_count = vertex->m_face_count; if (vertex_face_count <= 0) return ON_SubDIsNotValid(bSilentError); if (nullptr == vertex->m_faces) return ON_SubDIsNotValid(bSilentError); if (vertex_face_index >= vertex_face_count && ON_UNSET_UINT_INDEX != vertex_face_index) return ON_SubDIsNotValid(bSilentError); const unsigned int face_vertex_count = face->m_edge_count; if (face_vertex_count <= 0) return ON_SubDIsNotValid(bSilentError); if (face_vertex_count > 4 && nullptr == face->m_edgex) return ON_SubDIsNotValid(bSilentError); if (face_vertex_index >= face_vertex_count && ON_UNSET_UINT_INDEX != face_vertex_index) return ON_SubDIsNotValid(bSilentError); for (unsigned int i = 0; i < vertex_face_count; i++) { if (face == vertex->Face(i)) { if (ON_UNSET_UINT_INDEX == vertex_face_index) vertex_face_index = i; else if (i != vertex_face_index) return ON_SubDIsNotValid(bSilentError); } else if (i == vertex_face_index) { return ON_SubDIsNotValid(bSilentError); } } for (unsigned int i = 0; i < face_vertex_count; i++) { if (vertex == face->Vertex(i)) { if (ON_UNSET_UINT_INDEX == face_vertex_index) face_vertex_index = i; else if (i != face_vertex_index) return ON_SubDIsNotValid(bSilentError); } else if (i == face_vertex_index) { return ON_SubDIsNotValid(bSilentError); } } return true; } static bool IsValidEdgeFaceLink( const ON_SubDEdge* edge, const ON_SubDFace* face, unsigned int edge_face_index, unsigned int face_edge_index, bool bSilentError ) { if (nullptr == edge || nullptr == face) return ON_SubDIsNotValid(bSilentError); if (edge->SubdivisionLevel() != face->SubdivisionLevel()) return ON_SubDIsNotValid(bSilentError); const unsigned int edge_face_count = edge->m_face_count; if (edge_face_count <= 0) return ON_SubDIsNotValid(bSilentError); if (edge_face_count > 2 && nullptr == edge->m_facex) return ON_SubDIsNotValid(bSilentError); if (edge_face_index >= edge_face_count && ON_UNSET_UINT_INDEX != edge_face_index) return ON_SubDIsNotValid(bSilentError); const unsigned int face_edge_count = face->m_edge_count; if (face_edge_count <= 0) return ON_SubDIsNotValid(bSilentError); if (face_edge_count > 4 && nullptr == face->m_edgex) return ON_SubDIsNotValid(bSilentError); if (face_edge_index >= face_edge_count && ON_UNSET_UINT_INDEX != face_edge_index) return ON_SubDIsNotValid(bSilentError); for (unsigned int i = 0; i < edge_face_count; i++) { if (face == edge->Face(i)) { if (ON_UNSET_UINT_INDEX == edge_face_index) edge_face_index = i; else if (i != edge_face_index) return ON_SubDIsNotValid(bSilentError); } else if (i == edge_face_index) { return ON_SubDIsNotValid(bSilentError); } } for (unsigned int i = 0; i < face_edge_count; i++) { if (edge == face->Edge(i)) { if (ON_UNSET_UINT_INDEX == face_edge_index) face_edge_index = i; else if (i != face_edge_index) return ON_SubDIsNotValid(bSilentError); } else if (i == face_edge_index) { return ON_SubDIsNotValid(bSilentError); } } return true; } class ON_Internal_DamagedMarker { public: ON_Internal_DamagedMarker() = default; ~ON_Internal_DamagedMarker() { if (nullptr != m_subd_component) m_subd_component->m_status.SetDamagedState(true); } private: ON_Internal_DamagedMarker(const ON_Internal_DamagedMarker&) = delete; ON_Internal_DamagedMarker& operator=(const ON_Internal_DamagedMarker&) = delete; public: ON_Internal_DamagedMarker(const ON_SubDComponentBase* subd_component) : m_subd_component(subd_component) {} void ClearComponent() { m_subd_component = nullptr; } private: const ON_SubDComponentBase* m_subd_component = nullptr; }; static bool IsValidSubDVertexTag( const ON_SubDVertex* vertex, bool bSilentError ) { if (nullptr == vertex) return true; // this error detected elsewhere. ON_Internal_DamagedMarker dm(vertex); const unsigned short vertex_edge_count = vertex->m_edge_count; unsigned short crease_edge_count = 0; unsigned short smooth_edge_count = 0; 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) continue; if (e->IsSmooth()) ++smooth_edge_count; else if (e->IsCrease()) ++crease_edge_count; } const bool bValidEdgeTags = (vertex_edge_count == crease_edge_count + smooth_edge_count); switch (vertex->m_vertex_tag) { case ON_SubDVertexTag::Unset: return ON_SubDIsNotValid(bSilentError); break; case ON_SubDVertexTag::Smooth: if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); if ( 0 != crease_edge_count || vertex_edge_count < 2 || vertex_edge_count != smooth_edge_count || vertex_edge_count != vertex->m_face_count ) { return ON_SubDIsNotValid(bSilentError); } break; case ON_SubDVertexTag::Crease: if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); if ( 2 != crease_edge_count ) { return ON_SubDIsNotValid(bSilentError); } break; case ON_SubDVertexTag::Corner: if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); if (0 == crease_edge_count) { // currently, isolated vertices are not permitted - may change in the future return ON_SubDIsNotValid(bSilentError); } if (1 == crease_edge_count) { // must be a single wire crease edge ending at this vertex if ( 1 != vertex_edge_count || 0 != vertex->m_face_count) return ON_SubDIsNotValid(bSilentError); } break; case ON_SubDVertexTag::Dart: if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); if ( 1 != crease_edge_count || vertex_edge_count < 2 || vertex_edge_count != smooth_edge_count + crease_edge_count || vertex_edge_count != vertex->m_face_count ) { return ON_SubDIsNotValid(bSilentError); } break; default: return ON_SubDIsNotValid(bSilentError); break; } dm.ClearComponent(); return true; } static bool IsValidSubDEdgeTag( const ON_SubDEdge* edge, bool bSilentError ) { if (nullptr == edge) return true; // this error detected elsewhere. //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; unsigned int dart_vertex_count = 0; for ( unsigned int evi = 0; evi < 2; evi++) { if (nullptr == edge->m_vertex[evi]) return true; // topology errors detected elsewhere switch (edge->m_vertex[evi]->m_vertex_tag) { case ON_SubDVertexTag::Smooth: ++smooth_vertex_count; break; case ON_SubDVertexTag::Crease: ++crease_vertex_count; break; case ON_SubDVertexTag::Corner: ++corner_vertex_count; break; case ON_SubDVertexTag::Dart: ++dart_vertex_count; break; } }; if (2 != smooth_vertex_count + crease_vertex_count + corner_vertex_count + dart_vertex_count) return true; // invalid vertex tags detected in IsValidSubDVertexTag(); ON_Internal_DamagedMarker dm(edge); //const unsigned short edge_face_count = edge->m_face_count; switch(edge->m_edge_tag) { case ON_SubDEdgeTag::Unset: return ON_SubDIsNotValid(bSilentError); break; 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_SubDEdgeTag::Crease: if ( 0 != smooth_vertex_count ) return ON_SubDIsNotValid(bSilentError); break; case ON_SubDEdgeTag::SmoothX: if ( 2 != edge->m_face_count) return ON_SubDIsNotValid(bSilentError); if ( 0 != smooth_vertex_count ) return ON_SubDIsNotValid(bSilentError); break; default: return ON_SubDIsNotValid(bSilentError); break; } dm.ClearComponent(); return true; } static bool IsValidSubDVertex( const ON_SubDVertex* vertex, unsigned short level, unsigned int* vertex_id_range, unsigned short ordinary_valence_count, bool bSilentError ) { if (nullptr == vertex) return ON_SubDIsNotValid(bSilentError); if (vertex->SubdivisionLevel() != level) return ON_SubDIsNotValid(bSilentError); if (nullptr != vertex_id_range) { if (vertex->m_id < vertex_id_range[0]) return ON_SubDIsNotValid(bSilentError); if (vertex->m_id > vertex_id_range[1]) return ON_SubDIsNotValid(bSilentError); } ON_Internal_DamagedMarker dm(vertex); if (vertex->m_edge_count < vertex->m_face_count) { if ( ON_SubDVertexTag::Corner != vertex->m_vertex_tag || vertex->m_edge_count < 3 ) return ON_SubDIsNotValid(bSilentError); } if (vertex->m_edge_count > 0 && nullptr == vertex->m_edges) return ON_SubDIsNotValid(bSilentError); if (vertex->m_face_count > 0 && nullptr == vertex->m_faces) return ON_SubDIsNotValid(bSilentError); switch (vertex->m_vertex_tag) { case ON_SubDVertexTag::Smooth: // interior vertex if (vertex->m_edge_count != vertex->m_face_count) return ON_SubDIsNotValid(bSilentError); break; case ON_SubDVertexTag::Crease: if ( vertex->m_edge_count < 2 ) return ON_SubDIsNotValid(bSilentError); break; case ON_SubDVertexTag::Corner: if ( vertex->m_edge_count < 1 ) return ON_SubDIsNotValid(bSilentError); break; 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) return ON_SubDIsNotValid(bSilentError); break; default: // invalid value for this enum return ON_SubDIsNotValid(bSilentError); break; } unsigned int count = vertex->m_edge_count; for (unsigned int i = 0; i < count; i++) { const ON_SubDEdge* edge = vertex->Edge(i); if (nullptr == edge) return ON_SubDIsNotValid(bSilentError); } count = vertex->m_face_count; for (unsigned int i = 0; i < count; i++) { const ON_SubDFace* face = vertex->Face(i); if (nullptr == face) return ON_SubDIsNotValid(bSilentError); } dm.ClearComponent(); return true; } static bool IsValidSubDEdge( const ON_SubDEdge* edge, unsigned short level, unsigned int* edge_id_range, bool bSilentError ) { if (nullptr == edge) return ON_SubDIsNotValid(bSilentError); if (edge->SubdivisionLevel() != level) return ON_SubDIsNotValid(bSilentError); if (nullptr != edge_id_range) { if (edge->m_id < edge_id_range[0]) return ON_SubDIsNotValid(bSilentError); if (edge->m_id > edge_id_range[1]) return ON_SubDIsNotValid(bSilentError); } ON_Internal_DamagedMarker dm(edge); const ON_SubDVertex* edge_vertex[2] = { 0 }; for (unsigned int i = 0; i < 2; i++) { const ON_SubDVertex* vertex = edge->Vertex(i); if (nullptr == vertex) return ON_SubDIsNotValid(bSilentError); edge_vertex[i] = vertex; } if (edge_vertex[0] == edge_vertex[1]) return ON_SubDIsNotValid(bSilentError); if (edge->IsSmooth()) { // m_edge_tag is ON_SubDEdgeTag::Smooth or ON_SubDEdgeTag::SmoothX if ( 2 != edge->m_face_count) return ON_SubDIsNotValid(bSilentError); } else if (ON_SubDEdgeTag::Crease != edge->m_edge_tag) { return ON_SubDIsNotValid(bSilentError); } if (edge->m_face_count > 2 && nullptr == edge->m_facex) return ON_SubDIsNotValid(bSilentError); dm.ClearComponent(); return true; } static bool IsValidSubDFace( const ON_SubDFace* face, unsigned short level, unsigned int* face_id_range, unsigned short ordinary_face_edge_count, bool bSilentError ) { if (nullptr == face) return ON_SubDIsNotValid(bSilentError); if (face->SubdivisionLevel() != level) return ON_SubDIsNotValid(bSilentError); if (nullptr != face_id_range) { if (face->m_id < face_id_range[0]) return ON_SubDIsNotValid(bSilentError); if (face->m_id > face_id_range[1]) return ON_SubDIsNotValid(bSilentError); } ON_Internal_DamagedMarker dm(face); if (face->m_edge_count < 3) return ON_SubDIsNotValid(bSilentError); if (face->m_edge_count > 4 && nullptr == face->m_edgex) return ON_SubDIsNotValid(bSilentError); if (level > 0 && ordinary_face_edge_count != face->m_edge_count) return ON_SubDIsNotValid(bSilentError); dm.ClearComponent(); return true; } bool ON_SubDimple::IsValidLevel( const ON_SubD& subd, unsigned int level_index, bool bSilentError, ON_TextLog* text_log ) const { const unsigned int level_count = m_levels.UnsignedCount(); if (level_index >= level_count || level_index >= 0xFFFF) return ON_SubDIsNotValid(bSilentError); const ON_SubDLevel* level = m_levels[level_index]; if ( nullptr == level) return ON_SubDIsNotValid(bSilentError); level->ClearComponentDamagedState(); if ( level->m_level_index != level_index) return ON_SubDIsNotValid(bSilentError); if (level_index <= 0) { if (level->m_vertex_count < 3) return ON_SubDIsNotValid(bSilentError); if (level->m_edge_count < 3) return ON_SubDIsNotValid(bSilentError); if (level->m_face_count < 1) return ON_SubDIsNotValid(bSilentError); } else { const ON_SubDLevel* previous_level = m_levels[level_index - 1]; if (nullptr == previous_level) return ON_SubDIsNotValid(bSilentError); if (level->m_vertex_count <= previous_level->m_vertex_count) return ON_SubDIsNotValid(bSilentError); if (level->m_edge_count <= previous_level->m_edge_count) return ON_SubDIsNotValid(bSilentError); if (level->m_face_count <= previous_level->m_face_count) return ON_SubDIsNotValid(bSilentError); } if (nullptr == level->m_vertex[0]) return ON_SubDIsNotValid(bSilentError); if (nullptr == level->m_edge[0]) return ON_SubDIsNotValid(bSilentError); if (nullptr == level->m_face[0]) return ON_SubDIsNotValid(bSilentError); if (nullptr == level->m_vertex[1]) return ON_SubDIsNotValid(bSilentError); if (nullptr == level->m_edge[1]) return ON_SubDIsNotValid(bSilentError); if (nullptr == level->m_face[1]) return ON_SubDIsNotValid(bSilentError); const unsigned short expected_level = (unsigned short)level_index; unsigned int i; const ON_SubDVertex* vertex; const ON_SubDEdge* edge; const ON_SubDFace* face; unsigned int v_id_range[2] = { ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX }; unsigned int e_id_range[2] = { ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX }; unsigned int f_id_range[2] = { ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX }; unsigned int point_vertex_count = 0; unsigned int wire_edge_count = 0; // simple vertex validation if (level_index == subd.ActiveLevelIndex()) { if (subd.FirstVertex() != level->m_vertex[0]) return ON_SubDIsNotValid(bSilentError); ON_SubDVertexIterator vit = subd.VertexIterator(); if (vit.FirstVertex() != level->m_vertex[0]) return ON_SubDIsNotValid(bSilentError); ON_SubDVertexArray va = subd.VertexArray(); if (va.VertexCount() != level->m_vertex_count) return ON_SubDIsNotValid(bSilentError); if (va[0] != level->m_vertex[0]) return ON_SubDIsNotValid(bSilentError); if (va[level->m_vertex_count-1] != level->m_vertex[1]) return ON_SubDIsNotValid(bSilentError); } const ON_SubDVertex* last_vertex = nullptr; for (i = 0, vertex = level->m_vertex[0]; i < level->m_vertex_count && nullptr != vertex; i++, vertex = vertex->m_next_vertex) { if (false == IsValidSubDVertex(vertex, expected_level, nullptr, level->m_ordinary_vertex_valence, bSilentError)) return false; if (0 == i) { v_id_range[0] = v_id_range[1] = vertex->m_id; } else if (vertex->m_id < v_id_range[0]) v_id_range[0] = vertex->m_id; else if (vertex->m_id > v_id_range[1]) v_id_range[1] = vertex->m_id; if (0 == vertex->m_edge_count) { point_vertex_count++; } last_vertex = vertex; } if (level->m_vertex[1] != last_vertex) return ON_SubDIsNotValid(bSilentError); if (i != level->m_vertex_count || nullptr != vertex) return ON_SubDIsNotValid(bSilentError); if (1 + v_id_range[1] - v_id_range[0] < level->m_vertex_count) return ON_SubDIsNotValid(bSilentError); if ( v_id_range[1] > MaximumVertexId() ) return ON_SubDIsNotValid(bSilentError); // currently, point vertices are not permitted if (point_vertex_count > 0) return ON_SubDIsNotValid(bSilentError); // simple edge validation if (level_index == subd.ActiveLevelIndex()) { if (subd.FirstEdge() != level->m_edge[0]) return ON_SubDIsNotValid(bSilentError); ON_SubDEdgeIterator eit = subd.EdgeIterator(); if (eit.FirstEdge() != level->m_edge[0]) return ON_SubDIsNotValid(bSilentError); ON_SubDEdgeArray ea = subd.EdgeArray(); if (ea.EdgeCount() != level->m_edge_count) return ON_SubDIsNotValid(bSilentError); if (ea[0] != level->m_edge[0]) return ON_SubDIsNotValid(bSilentError); if (ea[level->m_edge_count-1] != level->m_edge[1]) return ON_SubDIsNotValid(bSilentError); } const ON_SubDEdge* last_edge = nullptr; for (i = 0, edge = level->m_edge[0]; i < level->m_edge_count && nullptr != edge; i++, edge = edge->m_next_edge) { if (false == IsValidSubDEdge(edge, expected_level, nullptr, bSilentError)) return false; if (0 == edge->m_face_count) { wire_edge_count++; } if (0 == i) { e_id_range[0] = e_id_range[1] = edge->m_id; } else if (edge->m_id < e_id_range[0]) e_id_range[0] = edge->m_id; else if (edge->m_id > e_id_range[1]) e_id_range[1] = edge->m_id; last_edge = edge; } if (i != level->m_edge_count || nullptr != edge) return ON_SubDIsNotValid(bSilentError); if (1 + e_id_range[1] - e_id_range[0] < level->m_edge_count) return ON_SubDIsNotValid(bSilentError); if (level->m_edge[1] != last_edge) return ON_SubDIsNotValid(bSilentError); if ( e_id_range[1] > MaximumEdgeId() ) return ON_SubDIsNotValid(bSilentError); // As of NOvember 12, 2019 // Wire edges are permitted. THey exist in subds being edited. ////// currently, wire edges are not permitted ////if (wire_edge_count > 0) //// return ON_SubDIsNotValid(bSilentError); // simple face validation if (level_index == subd.ActiveLevelIndex()) { if (subd.FirstFace() != level->m_face[0]) return ON_SubDIsNotValid(bSilentError); ON_SubDFaceIterator fit = subd.FaceIterator(); if (fit.FirstFace() != level->m_face[0]) return ON_SubDIsNotValid(bSilentError); ON_SubDFaceArray fa = subd.FaceArray(); if (fa.FaceCount() != level->m_face_count) return ON_SubDIsNotValid(bSilentError); if (fa[0] != level->m_face[0]) return ON_SubDIsNotValid(bSilentError); if (fa[0] != level->m_face[0]) return ON_SubDIsNotValid(bSilentError); } const ON_SubDFace* last_face = nullptr; for (i = 0, face = level->m_face[0]; i < level->m_face_count && nullptr != face; i++, face = face->m_next_face) { if (false == IsValidSubDFace(face, expected_level, nullptr, level->m_ordinary_face_edge_count, bSilentError)) return false; if (0 == i) { f_id_range[0] = f_id_range[1] = face->m_id; } else if (face->m_id < f_id_range[0]) f_id_range[0] = face->m_id; else if (face->m_id > f_id_range[1]) f_id_range[1] = face->m_id; last_face = face; } if (i != level->m_face_count || nullptr != face) return ON_SubDIsNotValid(bSilentError); if (1 + f_id_range[1] - f_id_range[0] < level->m_face_count) return ON_SubDIsNotValid(bSilentError); if (level->m_face[1] != last_face) return ON_SubDIsNotValid(bSilentError); if ( f_id_range[1] > MaximumFaceId() ) return ON_SubDIsNotValid(bSilentError); // vertex topology validation for (vertex = level->m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) { for (i = 0; i < vertex->m_edge_count; i++) { edge = vertex->Edge(i); if (false == IsValidSubDEdge(edge, expected_level, e_id_range, bSilentError)) return false; if (false == IsValidVertexEdgeLink(vertex, edge, vertex->EdgeDirection(i), bSilentError)) return false; } for (i = 0; i < vertex->m_face_count; i++) { face = vertex->Face(i); if (false == IsValidSubDFace(face, expected_level, f_id_range, level->m_ordinary_face_edge_count, bSilentError)) return false; if (false == IsValidVertexFaceLink(vertex, face, i, ON_UNSET_UINT_INDEX, bSilentError)) return false; } } // edge topology validation for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge) { for (i = 0; i < 2; i++) { vertex = edge->m_vertex[i]; if (false == IsValidSubDVertex(vertex, expected_level, v_id_range, level->m_ordinary_vertex_valence, bSilentError)) return false; if (false == IsValidVertexEdgeLink(vertex, edge, i, bSilentError)) return false; } for (i = 0; i < edge->m_face_count; i++) { face = edge->Face(i); if (false == IsValidSubDFace(face, expected_level, f_id_range, level->m_ordinary_face_edge_count, bSilentError)) return false; if (false == IsValidEdgeFaceLink(edge, face, i, ON_UNSET_UINT_INDEX, bSilentError)) return false; } } // face topology validation for (face = level->m_face[0]; nullptr != face; face = face->m_next_face) { for (i = 0; i < face->m_edge_count; i++) { edge = face->Edge(i); if (false == IsValidSubDEdge(edge, expected_level, e_id_range, bSilentError)) return false; if (false == IsValidEdgeFaceLink(edge, face, ON_UNSET_UINT_INDEX, i, bSilentError)) return false; } for (i = 0; i < face->m_edge_count; i++) { vertex = face->Vertex(i); if (false == IsValidSubDVertex(vertex, expected_level, v_id_range, level->m_ordinary_vertex_valence, bSilentError)) return false; if (false == IsValidVertexFaceLink(vertex, face, ON_UNSET_UINT_INDEX, i, bSilentError)) return false; } } // edge tag validation for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge) { if (false == IsValidSubDEdgeTag(edge, bSilentError)) return false; } // vertex tag validation for (vertex = level->m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) { if (false == IsValidSubDVertexTag(vertex, bSilentError)) return false; } // edge length validation for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge) { const ON_3dPoint P[2] = { edge->m_vertex[0]->ControlNetPoint(), edge->m_vertex[1]->ControlNetPoint() }; if (false == (P[0] != P[1])) { edge->m_status.SetDamagedState(true); return ON_SubDIsNotValid(bSilentError); } } return true; } bool ON_SubDimple::IsValid( const ON_SubD& subd, bool bSilentError, ON_TextLog* text_log ) const { if (false == m_heap.IsValid(bSilentError, text_log)) { if (nullptr != text_log) text_log->Print("Component ids are not set correctly. m_heap.ResetId() will fix this but may break externally stored component references.\n"); return ON_SubDIsNotValid(bSilentError); } const unsigned int level_count = m_levels.UnsignedCount(); if (level_count < 1) { return ON_SubDIsNotValid(bSilentError); } for (unsigned int level_index = 0; level_index < level_count; level_index++) { if (false == IsValidLevel(subd, level_index, bSilentError, text_log)) return false; } return true; } //virtual bool ON_SubD::IsValid(ON_TextLog* text_logx) const { // If low bit of text_log pointer is 1, then ON_Error is not called when the // knot vector is invalid. const ON__INT_PTR lowbit = 1; const ON__INT_PTR hightbits = ~lowbit; const bool bSilentError = (0 != (lowbit & ((ON__INT_PTR)text_logx))); ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits); const ON_SubDimple* subdimple = SubDimple(); if (nullptr == subdimple) return ON_SubDIsNotValid(bSilentError); return subdimple->IsValid(*this, bSilentError, text_log); } //virtual 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; ON_2udex id_range; id_range.i = component_sample_count; id_range.j = 0; DumpTopology(id_range,id_range,id_range,text_log); } unsigned int ON_SubD::DumpTopology(ON_TextLog & text_log) const { return DumpTopology(ON_2udex::Zero,ON_2udex::Zero,ON_2udex::Zero,text_log); } static const ON_wString Internal_DescribeWaste(size_t waste, size_t total) { if (waste <= 0 || total <= 0) return ON_wString::ToMemorySize(0); double p = 100.0 * ((double)waste) / ((double)total); if (p != p) return ON_wString::EmptyString; const double i = (p - floor(p) <= 0.5) ? floor(p) : ceil(p); double e = fabs(i - p); ON_wString description = ON_wString::EmptyString; const double negligible = 0.1; if (e < negligible) { if (0.0 == i) description = L" negligible"; p = i; } if (description.IsEmpty()) description = (i >= 10.0) ? ON_wString::FormatToString(L"%g%% of total", i) : ON_wString::FormatToString(L"%0.1f%% of total)", p); if (waste > 0) description += ON_wString(L" (") + ON_wString::ToMemorySize(waste) + ON_wString(L")"); return description; } unsigned int ON_SubD::DumpTopology( ON_2udex vertex_id_range, ON_2udex edge_id_range, ON_2udex face_id_range, ON_TextLog& text_log ) const { const class ON_SubDimple* subdimple = SubDimple(); if (nullptr == subdimple) { text_log.Print(L"SubD: Empty\n"); return 0; } const unsigned int level_count = LevelCount(); const unsigned int active_level_index = ActiveLevel().m_level_index; 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 unsigned subd_vertex_count = VertexCount(); const unsigned subd_edge_count = EdgeCount(); const unsigned subd_face_count = FaceCount(); if (level_count > 1) text_log.Print(L"SubD[%" PRIu64 "]: %u levels. Level %u is active (%u vertices, %u edges, %u faces).\n", runtime_sn, level_count, active_level_index, subd_vertex_count, subd_edge_count, subd_face_count ); else { text_log.Print( L"SubD[%" PRIu64 "]: %u vertices, %u edges, %u faces\n", runtime_sn, subd_vertex_count, subd_edge_count, subd_face_count ); } const ON_SubDHashType htype[] = { ON_SubDHashType::Topology, ON_SubDHashType::TopologyAndEdgeCreases, ON_SubDHashType::Geometry }; for (size_t i = 0; i < sizeof(htype) / sizeof(htype[0]); ++i) { const ON_SubDHash h = this->SubDHash(htype[i], false); h.Dump(text_log); } 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 primitive"); break; case ON_TextureMapping::TYPE::srf_mapping_primitive: text_log.Print("srf primitive"); break; case ON_TextureMapping::TYPE::brep_mapping_primitive: text_log.Print("brep primitive"); 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(); } } bool bIncludeSymmetrySet = false; 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("Heap use:\n"); { ON_TextLogIndent indent1(text_log); size_t sizeof_subd = this->SizeOfAllElements(); text_log.PrintString(ON_wString(L"Total = ") + ON_wString::ToMemorySize(sizeof_subd) + ON_wString(L".\n")); const size_t sizeof_frags = this->SizeOfAllMeshFragments(); text_log.PrintString(ON_wString(L"Mesh fragments = ") + ON_wString::ToMemorySize(sizeof_frags) + ON_wString(L".\n")); const size_t sizeof_frags_waste = this->SizeOfUnusedMeshFragments(); text_log.PrintString(ON_wString(L"Reserved but ununsed = ") + Internal_DescribeWaste(sizeof_frags_waste,sizeof_subd) + ON_wString(L".\n")); } text_log.Print(L"Levels:\n"); ON_SubDLevelIterator lit(subdimple->LevelIterator()); const ON_2udex empty_id_range(ON_UNSET_UINT_INDEX, 0); ON_SubDVertexIdIterator vidit(*this); ON_SubDEdgeIdIterator eidit(*this); ON_SubDFaceIdIterator fidit(*this); unsigned int error_count = 0; for (const ON_SubDLevel* level = lit.First(); nullptr != level; level = lit.Next()) { ON_TextLogIndent indent1(text_log); if (nullptr == level) continue; ON_2udex level_vertex_id_range = (0 != vertex_id_range.j || active_level_index == level->m_level_index) ? vertex_id_range : empty_id_range; ON_2udex level_edge_id_range = (0 != edge_id_range.j || active_level_index == level->m_level_index) ? edge_id_range : empty_id_range; ON_2udex level_face_id_range = (0 != face_id_range.j || active_level_index == level->m_level_index) ? face_id_range : empty_id_range; error_count += level->DumpTopology( *this, subdimple->MaximumVertexId(), subdimple->MaximumEdgeId(), subdimple->MaximumFaceId(), level_vertex_id_range, level_edge_id_range, level_face_id_range, vidit, eidit, fidit, bIncludeSymmetrySet, text_log); } return error_count; } ON_SubDHashType ON_SubDHashTypeFromUnsigned( unsigned int subd_hash_type_as_unsigned ) { switch (subd_hash_type_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDHashType::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDHashType::Topology); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDHashType::TopologyAndEdgeCreases); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDHashType::Geometry); } return ON_SUBD_RETURN_ERROR(ON_SubDHashType::Unset); } const ON_wString ON_SubDHashTypeToString( ON_SubDHashType subd_hash_type, bool bVerbose ) { const wchar_t* name; switch (subd_hash_type) { case ON_SubDHashType::Unset: name = L"Unset"; break; case ON_SubDHashType::Topology: name = L"Topology"; break; case ON_SubDHashType::TopologyAndEdgeCreases: name = L"TopologyAndEdgeCreases"; break; case ON_SubDHashType::Geometry: name = L"Geometry"; break; default: name = L"invalid"; break; } return bVerbose ? (ON_wString(L"ON_SubDHashType::") + ON_wString(name)) : ON_wString(name); } ON_SubDEndCapStyle ON_SubDEndCapStyleFromUnsigned( unsigned int subd_cap_style_as_unsigned ) { switch (subd_cap_style_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEndCapStyle::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEndCapStyle::None); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEndCapStyle::Triangles); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEndCapStyle::Quads); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEndCapStyle::Ngon); } return ON_SUBD_RETURN_ERROR(ON_SubDEndCapStyle::Unset); } const ON_wString ON_SubDEndCapStyleToString( ON_SubDEndCapStyle subd_cap_style, bool bVerbose ) { const wchar_t* s; switch (subd_cap_style) { case ON_SubDEndCapStyle::Unset: s = L"Unset"; break; case ON_SubDEndCapStyle::None: s = L"None"; break; case ON_SubDEndCapStyle::Triangles: s = L"Triangles"; break; case ON_SubDEndCapStyle::Quads: s = L"Quads"; break; case ON_SubDEndCapStyle::Ngon: s = L"Ngon"; break; default: s = L"invalid"; break; } return ON_wString(s); } static void Internal_AccumulateVertexHash( ON_SHA1& sha1, ON_SubDHashType hash_type, const ON_SubDVertex* v ) { sha1.AccumulateInteger32(v->m_id); if (ON_SubDHashType::Geometry == hash_type) { sha1.AccumulateBytes(&v->m_vertex_tag, sizeof(v->m_vertex_tag)); sha1.AccumulateDoubleArray(3, v->m_P); } } static const ON_SHA1_Hash Internal_VertexHash(ON_SubDHashType hash_type, const ON_SubDVertex* first_vertex, unsigned int level_index, ON_SubDVertexIdIterator& vidit) { ON_SHA1 sha1; if (ON_SubDHashType::Unset != hash_type) { unsigned prev_id = 0; for (const ON_SubDVertex* v = first_vertex; nullptr != v; v = v->m_next_vertex) { if (prev_id > v->m_id) { // must use slower vidit to get consistent results when complex editing juggles vertex order sha1.Reset(); for (v = vidit.FirstVertex(); nullptr != v; v = vidit.NextVertex()) { if ( level_index == v->SubdivisionLevel() && v->IsActive() ) Internal_AccumulateVertexHash(sha1, hash_type, v); } break; } Internal_AccumulateVertexHash(sha1, hash_type, v); prev_id = v->m_id; } } return sha1.Hash(); } const ON_SHA1_Hash ON_SubD::VertexHash(ON_SubDHashType hash_type) const { ON_SubDVertexIdIterator vidit(*this); return Internal_VertexHash(hash_type, FirstVertex(), this->ActiveLevelIndex(), vidit); } const ON_SHA1_Hash ON_SubDimple::VertexHash(ON_SubDHashType hash_type) const { ON_SubDVertexIdIterator vidit; this->InitializeVertexIdIterator(vidit); return Internal_VertexHash(hash_type, ActiveLevel().m_vertex[0], this->ActiveLevelIndex(), vidit); } static void Internal_AccumulateEdgeHash( ON_SHA1& sha1, ON_SubDHashType hash_type, const ON_SubDEdge* e ) { sha1.AccumulateInteger32(e->m_id); sha1.AccumulateInteger32(e->VertexId(0)); sha1.AccumulateInteger32(e->VertexId(1)); if (ON_SubDHashType::TopologyAndEdgeCreases == hash_type || ON_SubDHashType::Geometry == hash_type) { // Changing edge crease/smooth attributes often changes the regions used in face packing and exploding. sha1.AccumulateBool(e->IsCrease()); } } static const ON_SHA1_Hash Internal_EdgeHash(ON_SubDHashType hash_type, const ON_SubDEdge* first_edge, unsigned int level_index, ON_SubDEdgeIdIterator& eidit) { ON_SHA1 sha1; if (ON_SubDHashType::Unset != hash_type) { unsigned prev_id = 0; for (const ON_SubDEdge* e = first_edge; nullptr != e; e = e->m_next_edge) { if (prev_id > e->m_id) { // must use slower eidit to get consistent results when complex editing juggles vertex order sha1.Reset(); for (e = eidit.FirstEdge(); nullptr != e; e = eidit.NextEdge()) { if (level_index == e->SubdivisionLevel() && e->IsActive()) Internal_AccumulateEdgeHash(sha1, hash_type, e); } break; } Internal_AccumulateEdgeHash(sha1, hash_type, e); prev_id = e->m_id; } } return sha1.Hash(); } const ON_SHA1_Hash ON_SubD::EdgeHash(ON_SubDHashType hash_type) const { ON_SubDEdgeIdIterator eidit(*this); return Internal_EdgeHash(hash_type,FirstEdge(), this->ActiveLevelIndex(), eidit); } const ON_SHA1_Hash ON_SubDimple::EdgeHash(ON_SubDHashType hash_type) const { ON_SubDEdgeIdIterator eidit; this->InitializeEdgeIdIterator(eidit); return Internal_EdgeHash(hash_type, ActiveLevel().m_edge[0], this->ActiveLevelIndex(), eidit); } static void Internal_AccumulateFaceHash( ON_SHA1& sha1, ON_SubDHashType hash_type, const ON_SubDFace* f ) { 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; } sha1.AccumulateInteger32(eptr->EdgeId()); if (0 != ON_SUBD_EDGE_DIRECTION(eptr->m_ptr)) sha1.AccumulateBool(true); } } static const ON_SHA1_Hash Internal_FaceHash(ON_SubDHashType hash_type, const ON_SubDFace* first_face, unsigned int level_index, ON_SubDFaceIdIterator& fidit) { ON_SHA1 sha1; if (ON_SubDHashType::Unset != hash_type) { unsigned prev_id = 0; for (const ON_SubDFace* f = first_face; nullptr != f; f = f->m_next_face) { if (prev_id > f->m_id) { // must use slower fidit to get consistent results when complex editing juggles vertex order sha1.Reset(); for (f = fidit.FirstFace(); nullptr != f; f = fidit.NextFace()) { if (level_index == f->SubdivisionLevel() && f->IsActive()) Internal_AccumulateFaceHash(sha1, hash_type, f); } break; } Internal_AccumulateFaceHash(sha1, hash_type, f); prev_id = f->m_id; } } return sha1.Hash(); } const ON_SHA1_Hash ON_SubD::FaceHash(ON_SubDHashType hash_type) const { ON_SubDFaceIdIterator fidit(*this); return Internal_FaceHash(hash_type,FirstFace(), this->ActiveLevelIndex(), fidit); } const ON_SHA1_Hash ON_SubDimple::FaceHash(ON_SubDHashType hash_type) const { ON_SubDFaceIdIterator fidit; this->InitializeFaceIdIterator(fidit); return Internal_FaceHash(hash_type, ActiveLevel().m_face[0], this->ActiveLevelIndex(), fidit); } 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; } } } const ON_SubDHash ON_SubDHash::Create(ON_SubDHashType hash_type, const class ON_SubD& subd) { return ON_SubDHash::Create(hash_type, subd.SubDimple()); } const ON_SubDHash ON_SubDHash::Create(ON_SubDHashType hash_type, const class ON_SubDimple* subdimple) { if (nullptr == subdimple) return ON_SubDHash::Empty; const ON_SubDLevel& active_level = subdimple->ActiveLevel(); ON_SubDHash h; h.m_hash_type = hash_type; h.m_vertex_count = active_level.m_vertex_count; h.m_edge_count = active_level.m_edge_count; h.m_face_count = active_level.m_face_count; h.m_subd_runtime_serial_number = subdimple->RuntimeSerialNumber; if (h.m_vertex_count > 0) { h.m_subd_geometry_content_serial_number = subdimple->GeometryContentSerialNumber(); if (ON_SubDHashType::Unset != hash_type) { h.m_vertex_hash = subdimple->VertexHash(hash_type); h.m_edge_hash = subdimple->EdgeHash(hash_type); h.m_face_hash = subdimple->FaceHash(hash_type); } } return h; } int ON_SubDHash::Compare(const ON_SubDHash& lhs, const ON_SubDHash& rhs) { if (lhs.m_vertex_count < rhs.m_vertex_count) return -1; if (lhs.m_vertex_count > rhs.m_vertex_count) return 1; if (lhs.m_edge_count < rhs.m_edge_count) return -1; if (lhs.m_edge_count > rhs.m_edge_count) return 1; if (lhs.m_face_count < rhs.m_face_count) return -1; if (lhs.m_face_count > rhs.m_face_count) return 1; int rc; rc = ON_SHA1_Hash::Compare(lhs.m_vertex_hash, rhs.m_vertex_hash); if (0 != rc) return rc; rc = ON_SHA1_Hash::Compare(lhs.m_edge_hash, rhs.m_edge_hash); if (0 != rc) return rc; return ON_SHA1_Hash::Compare(lhs.m_face_hash, rhs.m_face_hash); } void ON_SubDHash::Dump(ON_TextLog& text_log) const { if (text_log.IsTextHash()) return; bool bIsNotEmpty = IsNotEmpty(); if (bIsNotEmpty) { switch (this->HashType()) { case ON_SubDHashType::Topology: text_log.Print("SubD toplogy hash:\n"); break; case ON_SubDHashType::TopologyAndEdgeCreases: text_log.Print("SubD toplogy and edge creases hash:\n"); break; case ON_SubDHashType::Geometry: text_log.Print("SubD geometry hash:\n"); break; default: bIsNotEmpty = false; break; } } if (bIsNotEmpty) { const ON_TextLogIndent indent1(text_log); const unsigned vcount = this->VertexCount(); const unsigned ecount = this->EdgeCount(); const unsigned fcount = this->FaceCount(); const ON_wString subdsha1 = this->SubDHash().ToStringEx(true); text_log.Print(L"SubD SHA1 = %ls\n", static_cast(subdsha1)); const ON_TextLogIndent indent2(text_log); if (vcount > 0) { const ON_wString vsha1 = this->VertexHash().ToStringEx(true); text_log.Print(L"%u vertices. SHA1 = %ls\n", vcount, static_cast(vsha1)); } else text_log.Print("No vertices.\n"); if (ecount > 0) { const ON_wString esha1 = this->EdgeHash().ToStringEx(true); text_log.Print(L"%u edges. SHA1 = %ls\n", ecount, static_cast(esha1)); } else text_log.Print("No edges.\n"); if (fcount > 0) { const ON_wString fsha1 = this->FaceHash().ToStringEx(true); text_log.Print(L"%u faces. SHA1 = %ls\n", fcount, static_cast(fsha1)); } else text_log.Print("No faces.\n"); } else text_log.Print("SubD hash: Empty\n"); } bool ON_SubDHash::Write(class ON_BinaryArchive& archive) const { if (false == archive.BeginWrite3dmAnonymousChunk(1)) return false; bool rc = false; for (;;) { const bool bIsEmpty = IsEmpty(); if (false == archive.WriteBool(bIsEmpty)) break; if (bIsEmpty) { rc = true; break; } // The SubD runtime serial number and geometry content serial numbers are runtime values. // When appropriate, calling contexts need to take appropriate steps when writing and // set these after reading. const unsigned char hash_type_as_unsigned = static_cast(m_hash_type); if (false == archive.WriteChar(hash_type_as_unsigned)) break; if (false == archive.WriteInt(m_vertex_count)) break; if (false == this->m_vertex_hash.Write(archive)) break; if (false == archive.WriteInt(m_edge_count)) break; if (false == this->m_edge_hash.Write(archive)) break; if (false == archive.WriteInt(m_face_count)) break; if (false == this->m_face_hash.Write(archive)) break; rc = true; break; } if (false == archive.EndWrite3dmChunk()) rc = false; return rc; } bool ON_SubDHash::Read(class ON_BinaryArchive& archive) { *this = ON_SubDHash::Empty; int chunk_version = 0; if (false == archive.BeginRead3dmAnonymousChunk(&chunk_version)) return false; bool rc = false; for (;;) { bool bIsEmpty = true; if (false == archive.ReadBool(&bIsEmpty)) break; if (bIsEmpty) { rc = true; break; } // The SubD runtime serial number and geometry content serial numbers are runtime values. // When appropriate, calling contexts need to set these after reading. unsigned char hash_type_as_unsigned = 0; if (false == archive.ReadChar(&hash_type_as_unsigned)) break; m_hash_type = ON_SubDHashTypeFromUnsigned(hash_type_as_unsigned); if (false == archive.ReadInt(&m_vertex_count)) break; if (false == this->m_vertex_hash.Read(archive)) break; if (false == archive.ReadInt(&m_edge_count)) break; if (false == this->m_edge_hash.Read(archive)) break; if (false == archive.ReadInt(&m_face_count)) break; if (false == this->m_face_hash.Read(archive)) break; rc = true; break; } if (false == archive.EndRead3dmChunk()) rc = false; return rc; } ON__UINT64 ON_SubDHash::SubDRuntimeSerialNumber() const { return this->m_subd_runtime_serial_number; } ON__UINT64 ON_SubDHash::SubDGeometryContentSerialNumber() const { return this->m_subd_geometry_content_serial_number; } bool ON_SubDHash::IsEmpty() const { return ON_SubDHashType::Unset == m_hash_type || 0 == m_vertex_count; } bool ON_SubDHash::IsNotEmpty() const { return ON_SubDHashType::Unset != m_hash_type && m_vertex_count > 0; } ON_SubDHashType ON_SubDHash::HashType() const { return m_hash_type; } unsigned int ON_SubDHash::VertexCount() const { return m_vertex_count; } unsigned int ON_SubDHash::EdgeCount() const { return m_edge_count; } unsigned int ON_SubDHash::FaceCount() const { return m_face_count; } const ON_SHA1_Hash ON_SubDHash::VertexHash() const { return m_vertex_hash; } const ON_SHA1_Hash ON_SubDHash::EdgeHash() const { return m_edge_hash; } const ON_SHA1_Hash ON_SubDHash::FaceHash() const { return m_face_hash; } const ON_SHA1_Hash ON_SubDHash::SubDHash() const { ON_SHA1 sha1; if (m_vertex_count > 0) sha1.AccumulateInteger32(m_vertex_count); if (m_edge_count > 0) sha1.AccumulateInteger32(m_edge_count); if (m_face_count > 0) sha1.AccumulateInteger32(m_face_count); if (ON_SubDHashType::Unset != m_hash_type) { if (m_vertex_count > 0) sha1.AccumulateSubHash(m_vertex_hash); if (m_edge_count > 0) sha1.AccumulateSubHash(m_edge_hash); if (m_face_count > 0) sha1.AccumulateSubHash(m_face_hash); } return sha1.Hash(); } bool operator==(const ON_SubDHash& lhs, const ON_SubDHash& rhs) { return 0 == ON_SubDHash::Compare(lhs, rhs); } bool operator!=(const ON_SubDHash& lhs, const ON_SubDHash& rhs) { return 0 != ON_SubDHash::Compare(lhs, rhs); } 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 ON_SubD& parent_subd, const unsigned int validate_max_vertex_id, const unsigned int validate_max_edge_id, const unsigned int validate_max_face_id, ON_2udex vertex_id_range, ON_2udex edge_id_range, ON_2udex face_id_range, ON_SubDVertexIdIterator& vidit, ON_SubDEdgeIdIterator& eidit, ON_SubDFaceIdIterator& fidit, bool bIncludeSymmetrySet, ON_TextLog& text_log ) const { // NOTE WELL: // This is a debugging tool. // The code in this function needs to work when the topology information is corrupt. const unsigned short level_index = (unsigned short)m_level_index; const unsigned int vertex_max_dump_count = (vertex_id_range.i > 0 && vertex_id_range.i != ON_UNSET_UINT_INDEX && 0 == vertex_id_range.j) ? vertex_id_range.i : 0; const unsigned int edge_max_dump_count = (edge_id_range.i > 0 && edge_id_range.i != ON_UNSET_UINT_INDEX && 0 == edge_id_range.j) ? edge_id_range.i : 0; const unsigned int face_max_dump_count = (face_id_range.i > 0 && face_id_range.i != ON_UNSET_UINT_INDEX && 0 == face_id_range.j) ? face_id_range.i : 0; const bool bVertexIdTest = (vertex_max_dump_count > 0) || (vertex_id_range.i < vertex_id_range.j) || (ON_UNSET_UINT_INDEX == (vertex_id_range.i)); const bool bEdgeIdTest = (edge_max_dump_count > 0) || (edge_id_range.i < edge_id_range.j) || (ON_UNSET_UINT_INDEX == (edge_id_range.i)); const bool bFaceIdTest = (face_max_dump_count > 0) || (face_id_range.i < face_id_range.j) || (ON_UNSET_UINT_INDEX == (face_id_range.i)); const char error_code_point = '!'; char prefix[16] = {}; unsigned int vertex_error_count = 0; unsigned int edge_error_count = 0; unsigned int face_error_count = 0; text_log.Print(L"SubD level %u topology: %u vertices, %u edges", m_level_index, m_vertex_count, m_edge_count); unsigned int wire_edge_count = 0U; unsigned int boundary_edge_count = 0U; unsigned int interior_edge_count = 0U; unsigned int nonmanifold_edge_count = 0U; for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) { if (0 == e->m_face_count) ++wire_edge_count; else if (1 == e->m_face_count) ++boundary_edge_count; else if (2 == e->m_face_count) ++interior_edge_count; else if (e->m_face_count >= 3) ++nonmanifold_edge_count; } if (wire_edge_count > 0U) { if (nonmanifold_edge_count > 0U) { if (boundary_edge_count > 0U && interior_edge_count > 0U) text_log.Print(L" (%u boundary, %u interior, %u wire, %u nonmanifold)", boundary_edge_count, interior_edge_count, wire_edge_count, nonmanifold_edge_count); else if (boundary_edge_count > 0U) text_log.Print(L" (%u boundary, %u wire, %u nonmanifold)", boundary_edge_count, wire_edge_count, nonmanifold_edge_count); else if (interior_edge_count > 0U) text_log.Print(L" (%u interior, %u wire, %u nonmanifold)", interior_edge_count, wire_edge_count, nonmanifold_edge_count); else text_log.Print(L" (%u wire, %u nonmanifold)", wire_edge_count, nonmanifold_edge_count); } else { if (boundary_edge_count > 0U && interior_edge_count > 0U) text_log.Print(L" (%u boundary, %u interior, %u wire)", boundary_edge_count, interior_edge_count, wire_edge_count); else if (boundary_edge_count > 0U) text_log.Print(L" (%u boundary, %u wire)", boundary_edge_count, wire_edge_count); else if (interior_edge_count > 0U) text_log.Print(L" (%u interior, %u wire)", interior_edge_count, wire_edge_count); else text_log.Print(L" (%u wire)", wire_edge_count); } } else if (nonmanifold_edge_count > 0U) { if (boundary_edge_count > 0U && interior_edge_count > 0U) text_log.Print(L" (%u boundary, %u interior, %u nonmanifold)", boundary_edge_count, interior_edge_count, nonmanifold_edge_count); else if (boundary_edge_count > 0U) text_log.Print(L" (%u boundary, %u nonmanifold)", boundary_edge_count, nonmanifold_edge_count); else if (interior_edge_count > 0U) text_log.Print(L" (%u interior, %u nonmanifold)", interior_edge_count, nonmanifold_edge_count); else text_log.Print(L" (%u nonmanifold)", nonmanifold_edge_count); } else if (boundary_edge_count > 0U && interior_edge_count > 0U) { text_log.Print(L" (%u boundary, %u interior)", boundary_edge_count, interior_edge_count); } text_log.Print(L", "); unsigned int ngon_count[65] = {}; unsigned int maxN = (unsigned int)(sizeof(ngon_count) / sizeof(ngon_count[0])) - 1; unsigned int face_count = 0; unsigned int uniformN = 0; for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) { if (face_count >= m_face_count && f->SubdivisionLevel() != level_index) break; face_count++; unsigned int N = f->EdgeCount(); if (1 == face_count) uniformN = N; else if (N != uniformN) uniformN = 0; unsigned int j = (N < maxN) ? N : maxN; if (N < maxN) ngon_count[j]++; } if (face_count != m_face_count) face_error_count++; if (uniformN > 0 && face_count == m_face_count) { if (3 == uniformN) text_log.Print(L"%u triangles\n", m_face_count); else if (4 == uniformN) text_log.Print(L"%u quads\n", m_face_count); else text_log.Print(L"%u %u-gons\n", m_face_count, uniformN); } else { text_log.Print(L"%u faces\n", m_face_count ); text_log.PushIndent(); for (unsigned int N = 0; N <= maxN; N++) { if (0 == ngon_count[N]) continue; if (3 == N) text_log.Print(L"%u triangles\n", ngon_count[N]); else if (4 == N) text_log.Print(L"%u quads\n", ngon_count[N]); else if (N < maxN) text_log.Print(L"%u %u-gons\n", ngon_count[N], N); else text_log.Print(L"%u N-gons\n", ngon_count[N]); } text_log.PopIndent(); } if (IsEmpty()) return 0; // The hash uniquely identifies the subd level topology and geometry. const ON_SubDHashType hash_type = ON_SubDHashType::Geometry; const ON_SHA1_Hash vhash = Internal_VertexHash(hash_type, m_vertex[0], this->m_level_index, vidit); const ON_SHA1_Hash ehash = Internal_EdgeHash(hash_type, m_edge[0], this->m_level_index, eidit); const ON_SHA1_Hash fhash = Internal_FaceHash(hash_type, m_face[0], this->m_level_index, fidit); 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; enum : unsigned int { damaged_id_list_capacity = 8 }; for (;;) { unsigned int damaged_vertex_id[damaged_id_list_capacity] = {}; unsigned int damaged_edge_id[damaged_id_list_capacity] = {}; unsigned int damaged_face_id[damaged_id_list_capacity] = {}; for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) { if (false == v->m_status.IsDamaged()) continue; if (damaged_vertex_count < damaged_id_list_capacity) damaged_vertex_id[damaged_vertex_count] = v->m_id; ++damaged_vertex_count; } for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) { if (false == e->m_status.IsDamaged()) continue; if (damaged_edge_count < damaged_id_list_capacity) damaged_edge_id[damaged_edge_count] = e->m_id; ++damaged_edge_count; } for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) { if (false == f->m_status.IsDamaged()) continue; if (damaged_face_count < damaged_id_list_capacity) damaged_face_id[damaged_face_count] = f->m_id; ++damaged_face_count; } if (0U == damaged_vertex_count && 0U == damaged_edge_count && 0U == damaged_face_count) break; text_log.Print("DAMAGED SubD level:\n"); ON_TextLogIndent indent1(text_log); if (damaged_vertex_count > 0) { text_log.Print(L"%u DAMAGED vertices: ", damaged_vertex_count); text_log.Print("v%u", damaged_vertex_id[0]); for (unsigned i = 1U; i < damaged_vertex_count; ++i) text_log.Print(", v%u", damaged_vertex_id[i]); if (damaged_vertex_count > damaged_id_list_capacity) text_log.Print(", ..."); text_log.PrintNewLine(); } if (damaged_edge_count > 0) { text_log.Print(L"%u DAMAGED edges: ", damaged_edge_count); text_log.Print("e%u", damaged_edge_id[0]); for (unsigned i = 1U; i < damaged_edge_count; ++i) text_log.Print(", e%u", damaged_edge_id[i]); if (damaged_edge_count > damaged_id_list_capacity) text_log.Print(", ..."); text_log.PrintNewLine(); } if (damaged_face_count > 0) { text_log.Print(L"%u DAMAGED faces: ", damaged_face_count); text_log.Print("f%u", damaged_face_id[0]); for (unsigned i = 1U; i < damaged_face_count; ++i) text_log.Print(", f%u", damaged_face_id[i]); if (damaged_face_count > damaged_id_list_capacity) text_log.Print(", ..."); text_log.PrintNewLine(); } break; } /////////////////////////////////////////////////////////////////// // // Vertex Topology // vN (x, y, z) // vEdges[vertex_edge_count] = { +eA, -eB, ... } // vFaces[vertex_edge_count] = { fP, fQ, fR, ... } // damaged_vertex_count = 0U; unsigned int vertex_count = 0; unsigned int vertex_dump_count = 0; ON_2udex skipped_vertex_id = ON_2udex::Zero; unsigned int max_vertex_id = 0; bool bSkippedPreviousComponent = false; for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) { if (vertex_count >= m_vertex_count && v->SubdivisionLevel() != level_index) break; if (v->m_id > max_vertex_id) max_vertex_id = v->m_id; vertex_count++; const bool bIsDamaged = v->m_status.IsDamaged(); if (bIsDamaged) ++damaged_vertex_count; if (bVertexIdTest && (false == bIsDamaged || damaged_vertex_count > damaged_id_list_capacity)) { bool bSkip = true; for (;;) { if (ON_UNSET_UINT_INDEX == vertex_id_range.i) break; if (vertex_max_dump_count > 0) { if (vertex_count > vertex_max_dump_count) break; } else { if (v->m_id < vertex_id_range.i || v->m_id >= vertex_id_range.j) break; } bSkip = false; break; } if (bSkip) { if (0 == skipped_vertex_id.j) { skipped_vertex_id.i = v->m_id; skipped_vertex_id.j = v->m_id; } else if (v->m_id < skipped_vertex_id.i) skipped_vertex_id.i = v->m_id; else if (v->m_id > skipped_vertex_id.j) skipped_vertex_id.j = v->m_id; bSkippedPreviousComponent = true; continue; } } if (0 == vertex_dump_count) text_log.Print(L"Vertices:\n"); vertex_dump_count++; ON_TextLogIndent vindent(text_log); const ON_3dPoint P0(v->ControlNetPoint()); const ON_wString vtag = ON_SubD::VertexTagToString(v->m_vertex_tag,false); if (bSkippedPreviousComponent) { text_log.Print(L"...\n"); bSkippedPreviousComponent = false; } { ON_String s = ON_String::FormatToString("v%u: ",v->m_id); if (bIsDamaged) s += "(DAMAGED) "; s += ON_String::FormatToString( "%ls (%g, %g, %g)", static_cast(vtag), P0.x, P0.y, P0.z ); if (v->m_group_id > 0) s += ON_String::FormatToString(" group_id=%u", v->m_group_id); text_log.PrintString(s); text_log.PrintNewLine(); } text_log.PushIndent(); 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 ); ON_3dPoint S(ON_3dPoint::NanPoint); if (v->GetSavedSurfacePoint(&S.x) && S.IsValid()) text_log.Print( "v.SurfacePoint: (%g, %g, %g)\n", S.x, S.y, S.z ); const unsigned int vertex_edge_count = v->m_edge_count; text_log.Print("v.Edges[%u] = ", vertex_edge_count); prefix[0] = '{'; prefix[1] = ON_String::Space; prefix[2] = error_code_point; prefix[3] = 'e'; prefix[4] = '%'; prefix[5] = 'u'; prefix[6] = 0; for (unsigned int vei = 0; vei < vertex_edge_count; vei++) { if (1 == vei) prefix[0] = ','; prefix[2] = error_code_point; const ON_SubDEdge* e = v->Edge(vei); if (nullptr != e) { if (v == e->m_vertex[0] && v != e->m_vertex[1]) prefix[2] = '+'; else if (v != e->m_vertex[0] && v == e->m_vertex[1]) prefix[2] = '-'; else vertex_error_count++; text_log.Print(prefix, e->m_id); } else { text_log.Print("%c %c", prefix[0], error_code_point); vertex_error_count++; } } text_log.Print(" }\n"); const unsigned int vertex_face_count = v->m_face_count; text_log.Print("v.Faces[%u] = ", vertex_face_count); prefix[0] = '{'; prefix[1] = ON_String::Space; prefix[2] = 'f'; prefix[3] = '%'; prefix[4] = 'u'; prefix[5] = 0; for (unsigned int vfi = 0; vfi < vertex_face_count; vfi++) { if (1 == vfi) prefix[0] = ','; const ON_SubDFace* f = v->Face(vfi); if (nullptr != f) text_log.Print(prefix, f->m_id); else { text_log.Print("%c %c", prefix[0], error_code_point); vertex_error_count++; } } text_log.Print(" }\n"); text_log.PopIndent(); } text_log.PushIndent(); if (vertex_dump_count > 0 && vertex_dump_count < vertex_count) { text_log.Print( L"... %u additional vertices (v%u to v%u).\n", vertex_count-vertex_dump_count, skipped_vertex_id.i, skipped_vertex_id.j ); } text_log.Print("Maximum vertex id = %u. ",max_vertex_id); if (validate_max_vertex_id >= max_vertex_id) text_log.Print("Next id = %u.\n", validate_max_vertex_id + 1); else text_log.Print("ERROR Next id = %u.\n", validate_max_vertex_id + 1); text_log.PopIndent(); /////////////////////////////////////////////////////////////////// // // Edge Topology // eN (+vA, -vB) // eFaces[edge_face_count] = { fP, fQ, fR, ... } // damaged_edge_count = 0U; wire_edge_count = 0U; nonmanifold_edge_count = 0U; unsigned int edge_count = 0; unsigned int edge_dump_count = 0; ON_2udex skipped_edge_id = ON_2udex::Zero; unsigned int max_edge_id = 0; bSkippedPreviousComponent = false; for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) { if (edge_count >= m_edge_count && e->SubdivisionLevel() != level_index) break; if (e->m_id > max_edge_id) max_edge_id = e->m_id; edge_count++; const bool bIsDamaged = e->m_status.IsDamaged(); if (bIsDamaged) ++damaged_edge_count; const bool bIsWireEdge = (0U == e->m_face_count); if (bIsWireEdge) ++wire_edge_count; const bool bIsNonmanifoldEdge = (e->m_face_count >= 3U); if (bIsNonmanifoldEdge) ++nonmanifold_edge_count; if ( bEdgeIdTest && (false == bIsDamaged || damaged_edge_count > damaged_id_list_capacity) && (false == bIsWireEdge || wire_edge_count > damaged_id_list_capacity) && (false == bIsNonmanifoldEdge || nonmanifold_edge_count > damaged_id_list_capacity) ) { bool bSkip = true; for (;;) { if (ON_UNSET_UINT_INDEX == edge_id_range.i) break; if (edge_max_dump_count > 0) { if (edge_count > edge_max_dump_count) break; } else { if (e->m_id < edge_id_range.i || e->m_id >= edge_id_range.j) break; } bSkip = false; break; } if (bSkip) { if (0 == skipped_edge_id.j) { skipped_edge_id.i = e->m_id; skipped_edge_id.j = e->m_id; } else if (e->m_id < skipped_edge_id.i) skipped_edge_id.i = e->m_id; else if (e->m_id > skipped_edge_id.j) skipped_edge_id.j = e->m_id; bSkippedPreviousComponent = true; continue; } } if (0 == edge_dump_count) text_log.Print(L"Edges:\n"); edge_dump_count++; ON_TextLogIndent eindent(text_log); const ON_wString etag = ON_SubD::EdgeTagToString(e->m_edge_tag,false); if (bSkippedPreviousComponent) { text_log.Print(L"...\n"); bSkippedPreviousComponent = false; } { ON_String s = ON_String::FormatToString("e%u: ", e->m_id); if (bIsDamaged) s += "(DAMAGED) "; if (bIsWireEdge) { s += ON_String::FormatToString( "wire %ls ", static_cast(etag) ); } else if (bIsNonmanifoldEdge) { s += ON_String::FormatToString( "nonmanifold %ls ", static_cast(etag) ); } else { s += ON_String::FormatToString( "%ls ", static_cast(etag) ); } s += "( "; prefix[0] = 'v'; prefix[1] = '%'; prefix[2] = 'u'; prefix[3] = 0; for (unsigned int evi = 0; evi < 2; evi++) { if (1 == evi) s += " to "; const ON_SubDVertex* v = e->m_vertex[evi]; if (nullptr != v) s += ON_String::FormatToString(prefix, v->m_id); else { s += error_code_point; edge_error_count++; } } s += " )"; if (e->m_group_id > 0) s += ON_String::FormatToString(" group_id=%u", e->m_group_id); text_log.PrintString(s); text_log.PrintNewLine(); } text_log.PushIndent(); 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 ); const unsigned int edge_face_count = e->m_face_count; text_log.Print("e.Faces[%u] = ", edge_face_count); prefix[0] = '{'; prefix[1] = ON_String::Space; prefix[2] = error_code_point; prefix[3] = 'f'; prefix[4] = '%'; prefix[5] = 'u'; prefix[6] = 0; for (unsigned int efi = 0; efi < edge_face_count; efi++) { if (1 == efi) prefix[0] = ','; prefix[2] = error_code_point; ON_SubDFacePtr fptr = e->FacePtr(efi); const ON_SubDFace* f = fptr.Face(); const ON__UINT_PTR edge_fdir = fptr.FaceDirection(); if (nullptr != f) { ON_SubDEdgePtr eptr = f->EdgePtrFromEdge(e); if (eptr.Edge() == e && eptr.EdgeDirection() == edge_fdir) prefix[2] = ((0 == edge_fdir) ? '+' : '-'); else edge_error_count++; text_log.Print(prefix, f->m_id); } else { text_log.Print("%c %c", prefix[0], error_code_point); edge_error_count++; } } text_log.Print(" }\n"); text_log.PopIndent(); } text_log.PushIndent(); if (edge_dump_count > 0 && edge_dump_count < edge_count) { text_log.Print(L"... %u additional edges (e%u to e%u).\n", edge_count - edge_dump_count, skipped_edge_id.i, skipped_edge_id.j ); } text_log.Print("Maximum edge id = %u. ",max_edge_id); if (validate_max_edge_id >= max_edge_id) text_log.Print("Next id = %u.\n", validate_max_edge_id + 1); else text_log.Print("ERROR Next id = %u.\n", validate_max_edge_id + 1); text_log.PopIndent(); /////////////////////////////////////////////////////////////////// // // Face Topology // fN // fEdges[face_edge_count] = { +eA, -eB, +eC, ...} // fVertices[face_edge_count] = { vP, vQ, vR, ... } // damaged_face_count = 0U; face_count = 0; unsigned int face_dump_count = 0; ON_2udex skipped_face_id = ON_2udex::Zero; unsigned int max_face_id = 0; bSkippedPreviousComponent = false; for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) { if (face_count >= m_face_count && f->SubdivisionLevel() != level_index) break; if (f->m_id > max_face_id) max_face_id = f->m_id; face_count++; const bool bIsDamaged = f->m_status.IsDamaged(); if (bIsDamaged) ++damaged_face_count; if (bFaceIdTest && (false == bIsDamaged || damaged_face_count > damaged_id_list_capacity)) { bool bSkip = true; for (;;) { if (ON_UNSET_UINT_INDEX == face_id_range.i) break; if (face_max_dump_count > 0) { if (face_count > face_max_dump_count) break; } else { if (f->m_id < face_id_range.i || f->m_id >= face_id_range.j) break; } bSkip = false; break; } if (bSkip) { if (0 == skipped_face_id.j) { skipped_face_id.i = f->m_id; skipped_face_id.j = f->m_id; } else if (f->m_id < skipped_face_id.i) skipped_face_id.i = f->m_id; else if (f->m_id > skipped_face_id.j) skipped_face_id.j = f->m_id; bSkippedPreviousComponent = true; continue; } } if (0 == face_dump_count) text_log.Print(L"Faces:\n"); face_dump_count++; ON_TextLogIndent eindent(text_log); if (bSkippedPreviousComponent) { text_log.Print(L"...\n"); bSkippedPreviousComponent = false; } if (bIsDamaged) { text_log.Print( "f%u (DAMAGED):\n", f->m_id ); } else { ON_String s = ON_String::FormatToString("f%u:", f->m_id); if (f->m_group_id > 0) s += ON_String::FormatToString(" group_id=%u", f->m_group_id); text_log.PrintString(s); text_log.PrintNewLine(); } text_log.PushIndent(); ON_3dPoint P1(ON_3dPoint::NanPoint); if (f->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid()) text_log.Print( "f.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z ); const unsigned int face_edge_count = f->m_edge_count; text_log.Print("f.Edges[%u] = ", face_edge_count); prefix[0] = '{'; prefix[1] = ON_String::Space; prefix[2] = error_code_point; prefix[3] = 'e'; prefix[4] = '%'; prefix[5] = 'u'; prefix[6] = 0; for (unsigned int fei = 0; fei < face_edge_count; fei++) { if (1 == fei) prefix[0] = ','; prefix[2] = error_code_point; const ON_SubDEdgePtr eptr = f->EdgePtr(fei); const ON_SubDEdge* e = eptr.Edge(); const ON__UINT_PTR face_edir = eptr.EdgeDirection(); if (nullptr != e) { ON_SubDFacePtr fptr = e->FacePtrFromFace(f); if (fptr.Face() == f && fptr.FaceDirection() == face_edir) prefix[2] = ((0 == face_edir) ? '+' : '-'); else face_error_count++; text_log.Print(prefix, e->m_id); } else { text_log.Print("%c %c", prefix[0], error_code_point); face_error_count++; } } text_log.Print(" }\n"); text_log.Print("f.Vertices[%u] = ", face_edge_count); prefix[0] = '{'; prefix[1] = ON_String::Space; prefix[2] = 'v'; prefix[3] = '%'; prefix[4] = 'u'; prefix[5] = 0; for (unsigned int fvi = 0; fvi < face_edge_count; fvi++) { if (1 == fvi) prefix[0] = ','; const ON_SubDVertex* v = f->Vertex(fvi); if (nullptr != v) text_log.Print(prefix, v->m_id); else { text_log.Print("%c %c", prefix[0], error_code_point); face_error_count++; } } 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; const ON_Color per_face_color = f->PerFaceColor(); if (ON_Color::UnsetColor != per_face_color) { if (bNeedComma) text_log.Print(", "); else text_log.Print("Per face"); bNeedComma = true; text_log.Print("face color=("); text_log.PrintColor(per_face_color); text_log.Print(")"); } const int per_face_material_channel_index = f->MaterialChannelIndex(); if (0 != per_face_material_channel_index) { if (bNeedComma) text_log.Print(", "); else text_log.Print("Per face"); bNeedComma = true; text_log.Print(" material channel index=%d", per_face_material_channel_index); } if (bNeedComma) text_log.PrintNewLine(); if (f->PackRectIsSet()) { bNeedComma = true; const bool bGridOrder = true; ON_2dPoint corners[4] = { f->PackRectCorner(bGridOrder,0), f->PackRectCorner(bGridOrder,1), f->PackRectCorner(bGridOrder,2), f->PackRectCorner(bGridOrder,3) }; text_log.Print("f.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 ); } 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(); } text_log.PushIndent(); if (face_dump_count > 0 && face_dump_count < face_count) { text_log.Print( L"... %u additional faces (f%u to f%u).\n", face_count - face_dump_count, skipped_face_id.i, skipped_face_id.j ); } text_log.Print("Maximum face id = %u. ",max_face_id); if (validate_max_face_id >= max_face_id) text_log.Print("Next id = %u.\n", validate_max_face_id + 1); else text_log.Print("ERROR Next id = %u.\n", validate_max_face_id + 1); text_log.PopIndent(); const unsigned int topology_error_count = vertex_error_count + edge_error_count + face_error_count; text_log.PushIndent(); if (0 == topology_error_count) { text_log.Print("No topology inconsistencies.\n"); } else { text_log.Print( "ERRORS: %u vertex, %u edge, %u face topology inconsistencies marked with \"%c\".\n", vertex_error_count, edge_error_count, face_error_count, error_code_point ); } text_log.PopIndent(); 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 { size_t sz = ON_Geometry::SizeOf(); sz += sizeof(*this) - sizeof(ON_Geometry); const ON_SubDimple* subdimple = SubDimple(); if ( subdimple ) sz += subdimple->SizeOf(); return (unsigned int)sz; } size_t ON_SubD::SizeOfAllElements() const { const ON_SubDimple* subdimple = SubDimple(); return (nullptr != subdimple) ? subdimple->SizeOfAllElements() : 0; } size_t ON_SubD::SizeOfActiveElements() const { const ON_SubDimple* subdimple = SubDimple(); return (nullptr != subdimple) ? subdimple->SizeOfActiveElements() : 0; } size_t ON_SubD::SizeOfUnusedElements() const { const ON_SubDimple* subdimple = SubDimple(); return (nullptr != subdimple) ? subdimple->SizeOfUnusedElements() : 0; } size_t ON_SubD::SizeOfAllMeshFragments() const { const ON_SubDimple* subdimple = SubDimple(); return (nullptr != subdimple) ? subdimple->SizeOfAllMeshFragments() : 0; } size_t ON_SubD::SizeOfActiveMeshFragments() const { const ON_SubDimple* subdimple = SubDimple(); return (nullptr != subdimple) ? subdimple->SizeOfActiveMeshFragments() : 0; } size_t ON_SubD::SizeOfUnusedMeshFragments() const { const ON_SubDimple* subdimple = SubDimple(); return (nullptr != subdimple) ? subdimple->SizeOfUnusedMeshFragments() : 0; } //virtual ON__UINT32 ON_SubD::DataCRC(ON__UINT32 current_remainder) const { return 0; } //virtual ON::object_type ON_SubD::ObjectType() const { return ON::subd_object; } //virtual void ON_SubD::DestroyRuntimeCache( bool bDelete ) { const ON_SubDimple* dimple = SubDimple(); if (nullptr != dimple) { unsigned int level_count = dimple->LevelCount(); for (unsigned int level_index = 0; level_index < level_count; level_index++) { const ON_SubDLevel* level = dimple->SubDLevel(level_index); if (level) { level->ClearEvaluationCache(); level->MarkAggregateComponentStatusAsNotCurrent(); } } dimple->ChangeGeometryContentSerialNumber(false); } return; } //virtual int ON_SubD::Dimension() const { return 3; } //virtual bool ON_SubD::GetBBox( double* boxmin, double* boxmax, bool bGrowBox ) const { int j; for ( j = 0; j < 3 && bGrowBox; j++ ) { if ( !ON_IsValid(boxmin[j]) || !ON_IsValid(boxmax[j]) || boxmin[j] > boxmax[j]) bGrowBox = false; } ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox; bool rc = false; // GetBBox must always returns the control net box - it contains both the surface and the control net. bbox = ActiveLevel().ControlNetBoundingBox(); rc = bbox.IsValid(); if (rc) { if (bGrowBox) { if (bbox.m_min.x < boxmin[0]) boxmin[0] = bbox.m_min.x; if (bbox.m_max.x > boxmax[0]) boxmax[0] = bbox.m_max.x; if (bbox.m_min.y < boxmin[1]) boxmin[1] = bbox.m_min.y; if (bbox.m_max.y > boxmax[1]) boxmax[1] = bbox.m_max.y; if (bbox.m_min.z < boxmin[2]) boxmin[2] = bbox.m_min.z; if (bbox.m_max.z > boxmax[2]) boxmax[2] = bbox.m_max.z; } else { boxmin[0] = bbox.m_min.x; boxmin[1] = bbox.m_min.y; boxmin[2] = bbox.m_min.z; boxmax[0] = bbox.m_max.x; boxmax[1] = bbox.m_max.y; boxmax[2] = bbox.m_max.z; } } return (rc || bGrowBox); } //virtual void ON_SubD::ClearBoundingBox() { // For ON_SubD, ON_SubD::ClearBoundingBox() and ON_SubD::DestroyRuntimeCache(true) ON_SubD::DestroyRuntimeCache(true); } //virtual bool ON_SubD::Transform( const ON_Xform& xform ) { if ( this == &ON_SubD::Empty) return true; // transform applied to empty subd is true on purpose // userdata transformation, etc. if (!this->ON_Geometry::Transform(xform)) return false; ON_SubDimple* subdimple = SubDimple(false); if ( 0 == subdimple ) return true; // transform applied to empty subd is true on purpose return subdimple->Transform(xform); } //virtual bool ON_SubD::IsDeformable() const { return true; } //virtual bool ON_SubD::MakeDeformable() { return true; } //virtual bool ON_SubD::SwapCoordinates( int i, int j ) { return false; } //virtual bool ON_SubD::HasBrepForm() const { return false; } //virtual ON_Brep* ON_SubD::BrepForm( ON_Brep* destination_brep) const { return nullptr; } //virtual ON_COMPONENT_INDEX ON_SubD::ComponentIndex() const { return ON_Geometry::ComponentIndex(); } //virtual bool ON_SubD::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) const { return false; } ////////////////////////////////////////////////////////////// // // // class ON_SubDHeap* ON_SubD::Internal_Heap() const { ON_SubDimple* subdimple = m_subdimple_sp.get(); return (nullptr != subdimple) ? &subdimple->Heap() : nullptr; } bool ON_SubD::InSubD(const class ON_SubDVertex* vertex) const { return InSubD(ON_SubDComponentPtr::Create(vertex)); } bool ON_SubD::InSubD(const class ON_SubDEdge* edge) const { return InSubD(ON_SubDComponentPtr::Create(edge)); } bool ON_SubD::InSubD(const class ON_SubDFace* face) const { return InSubD(ON_SubDComponentPtr::Create(face)); } bool ON_SubD::InSubD(ON_SubDComponentPtr cptr) const { const ON_SubDHeap* h = this->Internal_Heap(); return (nullptr != h) ? h->InHeap(cptr) : false; } const ON_SubDComponentPtr ON_SubD::InSubD(const ON_SubDComponentBase* b) const { const ON_SubDHeap* h = this->Internal_Heap(); return (nullptr != h) ? h->InHeap(b) : ON_SubDComponentPtr::Null; } bool ON_SubDHeap::InHeap(ON_SubDComponentPtr cptr) const { const ON_FixedSizePool* fsp = this->Internal_ComponentFixedSizePool(cptr.ComponentType()); return (nullptr != fsp) ? fsp->InPool(cptr.ComponentBase()) : false; } const ON_SubDComponentPtr ON_SubDHeap::InHeap(const class ON_SubDComponentBase* b) const { if (nullptr != b) { ON_SubDComponentPtr::Type t[3] = { ON_SubDComponentPtr::Type::Vertex, ON_SubDComponentPtr::Type::Edge, ON_SubDComponentPtr::Type::Face }; for (int i = 0; i < 3; ++i) { const ON_FixedSizePool* fsp = this->Internal_ComponentFixedSizePool(t[i]); if (nullptr != fsp && fsp->InPool(b)) { switch (t[i]) { case ON_SubDComponentPtr::Type::Vertex: return ON_SubDComponentPtr::Create((const ON_SubDVertex*)b); break; case ON_SubDComponentPtr::Type::Edge: return ON_SubDComponentPtr::Create((const ON_SubDEdge*)b); break; case ON_SubDComponentPtr::Type::Face: return ON_SubDComponentPtr::Create((const ON_SubDFace*)b); break; } } } } return ON_SubDComponentPtr::Null; } const class ON_SubDLevel& ON_SubD::ActiveLevel() const { ON_SubDimple* subdimple = m_subdimple_sp.get(); return (nullptr != subdimple) ? subdimple->ActiveLevel() : ON_SubDLevel::Empty; } class ON_SubDLevel const * ON_SubD::ActiveLevelConstPointer() const { ON_SubDimple* subdimple = m_subdimple_sp.get(); return (nullptr != subdimple) ? subdimple->ActiveLevelPointer() : nullptr; } class ON_SubDLevel* ON_SubD::ActiveLevelPointer() { ON_SubDimple* subdimple = m_subdimple_sp.get(); return (nullptr != subdimple) ? subdimple->ActiveLevelPointer() : nullptr; } ON_SubDimple* ON_SubD::SubDimple(bool bCreateIfNeeded) { ON_SubDimple* subdimple = m_subdimple_sp.get(); if (nullptr == subdimple && bCreateIfNeeded) { subdimple = new ON_SubDimple(); m_subdimple_sp = std::shared_ptr(subdimple); } return subdimple; } const class ON_SubDimple* ON_SubD::SubDimple() const { return m_subdimple_sp.get(); } unsigned int ON_SubD::SubDimpleUseCount() const { return (unsigned int)m_subdimple_sp.use_count(); } void ON_SubD::ShareDimple(const ON_SubD& other_subd) { if (m_subdimple_sp.get() != other_subd.m_subdimple_sp.get()) { m_subdimple_sp.reset(); m_subdimple_sp = other_subd.m_subdimple_sp; } } void ON_SubD::ShareDimple(const class ON_SubDMeshImpl& subd_limple) { std::shared_ptr limple_sp(subd_limple.m_subdimple_wp.lock()); const ON_SubDimple* subd_imple = m_subdimple_sp.get(); if (nullptr == limple_sp.get()) { // weak pointer is nullptr, meaning there are no known references to the // subd used to create this limit mesh. const_cast(subd_limple).ClearFragmentFacePointers(true); } if (subd_imple != limple_sp.get()) { m_subdimple_sp.reset(); m_subdimple_sp = limple_sp; } } void ON_SubD::SwapDimple(class ON_SubDMeshImpl& subd_limple ) { std::shared_ptr limple_sp(subd_limple.m_subdimple_wp.lock()); if (m_subdimple_sp.get() != limple_sp.get()) { m_subdimple_sp.swap(limple_sp); subd_limple.m_subdimple_wp = limple_sp; } } void ON_SubD::SwapDimple(ON_SubD& other_subd) { if (this != &other_subd) { m_subdimple_sp.swap(other_subd.m_subdimple_sp); } } void ON_SubD::Clear() { ON_SubDimple* subdimple = SubDimple(false); if ( subdimple ) subdimple->Clear(); } unsigned int ON_SubD::ClearHigherSubdivisionLevels( unsigned int max_level_index ) { ON_SubDimple* subdimple = SubDimple(false); return (nullptr != subdimple) ? subdimple->ClearHigherSubdivisionLevels(max_level_index) : 0U; } unsigned int ON_SubD::ClearLowerSubdivisionLevels( unsigned int min_level_index ) { ON_SubDimple* subdimple = SubDimple(false); return (nullptr != subdimple) ? subdimple->ClearLowerSubdivisionLevels(min_level_index) : 0U; } unsigned ON_SubD::ClearInactiveLevels() { ON_SubDimple* subdimple = SubDimple(false); return (nullptr != subdimple) ? subdimple->ClearInactiveLevels() : 0U; } void ON_SubD::Destroy() { m_subdimple_sp.reset(); } class ON_SubDVertex* ON_SubD::AddVertex( const double* P ) { return AddVertex(ON_SubDVertexTag::Unset, P); } class ON_SubDVertex* ON_SubD::AddVertex( ON_SubDVertexTag vertex_tag, const double* P ) { ON_SubDimple* subdimple = SubDimple(true); if ( 0 == subdimple ) return 0; const unsigned int level_index = subdimple->ActiveLevelIndex(); class ON_SubDVertex* v = subdimple->AllocateVertex(vertex_tag, level_index, P); // 0 = level subdimple->AddVertexToLevel(v); return v; } ON_SubDVertex* ON_SubD::AddVertexForExperts( unsigned int candidate_vertex_id, ON_SubDVertexTag vertex_tag, const double* P, unsigned int initial_edge_capacity, unsigned int initial_face_capacity ) { ON_SubDimple* subdimple = SubDimple(true); if (0 == subdimple) return 0; ON_SubDVertex* v = subdimple->AllocateVertex( candidate_vertex_id, vertex_tag, subdimple->ActiveLevelIndex(), P, initial_edge_capacity, initial_face_capacity); subdimple->AddVertexToLevel(v); return v; } bool ON_SubD::ReturnVertexForExperts( ON_SubDVertex* v ) { if (nullptr == v) return false; if (this->InSubD(v) && v->IsActive() && 0 == v->m_edge_count && 0 == v->m_face_count ) { ON_SubDimple* subdimple = SubDimple(false); if (nullptr != subdimple) { subdimple->ReturnVertex(v); return true; } } // Caller is not an expert but a crash has been prevented. return ON_SUBD_RETURN_ERROR(false); } class ON_SubDEdge* ON_SubDimple::AddEdge( ON_SubDEdgeTag edge_tag, ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, double v1_sector_weight ) { return AddEdge( 0U, edge_tag, v0, v0_sector_weight, v1, v1_sector_weight, 0U ); } class ON_SubDEdge* ON_SubDimple::AddEdge( unsigned int candidate_edge_id, ON_SubDEdgeTag edge_tag, ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, double v1_sector_weight, unsigned initial_face_capacity ) { if ( false == ON_SubDSectorType::IsValidSectorCoefficientValue(v0_sector_weight,true) ) return ON_SUBD_RETURN_ERROR(nullptr); if ( false == ON_SubDSectorType::IsValidSectorCoefficientValue(v1_sector_weight,true) ) return ON_SUBD_RETURN_ERROR(nullptr); if ( nullptr != v0 && nullptr != v1 && v0->SubdivisionLevel() != v1->SubdivisionLevel() ) return ON_SUBD_RETURN_ERROR(nullptr); const bool bEdgeTagSet = ON_SubD::EdgeTagIsSet(edge_tag); if ( bEdgeTagSet && ON_SubDSectorType::IgnoredSectorCoefficient != v0_sector_weight && ON_SubDSectorType::UnsetSectorCoefficient != v0_sector_weight && nullptr != v0 && ON_SubDVertexTag::Smooth == v0->m_vertex_tag ) { // minimizes checking when building subds because constant crease weights can be passed in v0_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient; } if ( bEdgeTagSet && ON_SubDSectorType::IgnoredSectorCoefficient != v1_sector_weight && ON_SubDSectorType::UnsetSectorCoefficient != v1_sector_weight && nullptr != v1 && ON_SubDVertexTag::Smooth == v1->m_vertex_tag ) { // minimizes checking when building subds because constant crease weights can be passed in v1_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient; } class ON_SubDEdge* e = AllocateEdge(candidate_edge_id,edge_tag, 0, 0); if ( nullptr == e) return ON_SUBD_RETURN_ERROR(nullptr); if ( nullptr != v0 ) e->SetSubdivisionLevel(v0->SubdivisionLevel()); else if ( nullptr != v1 ) e->SetSubdivisionLevel(v1->SubdivisionLevel()); else if ( ActiveLevelIndex() < ON_UNSET_UINT_INDEX ) e->SetSubdivisionLevel(ActiveLevelIndex()); for (unsigned int i = 0; i < 2; i++) { ON_SubDVertex* v = (i ? v1 : v0); double vertex_weight = (i ? v1_sector_weight : v0_sector_weight); e->m_vertex[i] = v; e->m_sector_coefficient[i] = vertex_weight; if (nullptr != v) { if (false == m_heap.GrowVertexEdgeArrayByOne(v)) { v->m_status.SetDamagedState(true); ReturnEdge(e); return ON_SUBD_RETURN_ERROR(nullptr); } v->m_edges[v->m_edge_count++] = ON_SubDEdgePtr::Create(e, i); } } if ( nullptr == AddEdgeToLevel(e) ) return ON_SUBD_RETURN_ERROR(nullptr); if (initial_face_capacity > 2) { m_heap.GrowEdgeFaceArray(e, initial_face_capacity); } return e; } ON_SubDEdgeTag ON_SubD::EdgeTagFromContext( unsigned int edge_face_count, const ON_SubDVertex* v0, const ON_SubDVertex* v1 ) { return (nullptr != v0 && nullptr != v1) ? ON_SubD::EdgeTagFromContext(edge_face_count, v0->m_vertex_tag, v1->m_vertex_tag) : ON_SubDEdgeTag::Unset; } ON_SubDEdgeTag ON_SubD::EdgeTagFromContext( unsigned int edge_face_count, const ON_SubDVertexTag v0_tag, const ON_SubDVertexTag v1_tag ) { ON_SubDEdgeTag edge_tag = ON_SubDEdgeTag::Unset; for(;;) { if (edge_face_count > 0x7FFFU) break; if (1 == edge_face_count || edge_face_count >= 3 ) { edge_tag = ON_SubDEdgeTag::Crease; break; } 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_SubDEdgeTag::Smooth; break; } if ( ON_SubD::VertexTagIsSet(v0_tag) && ON_SubD::VertexTagIsSet(v1_tag) ) { if (2 == edge_face_count) edge_tag = ON_SubDEdgeTag::SmoothX; break; } break; } return edge_tag; } const ON_SubDVertex* ON_SubD::FindVertex( const double* control_net_point, double distance_tolerance ) const { if (nullptr == control_net_point ) return nullptr; const ON_3dPoint P(control_net_point); if ( false == P.IsValid() ) return nullptr; if ( false == (0.0 <= distance_tolerance)) return nullptr; const ON_SubDVertex* best_v = nullptr; ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { const double d = P.DistanceTo(v->ControlNetPoint()); if (0.0 == d) return v; if (d < distance_tolerance && (nullptr == best_v || d <= distance_tolerance) ) { distance_tolerance = d; best_v = v; } } return best_v; } const ON_SubDVertex* ON_SubD::FindOrAddVertex( const double* control_net_point, double distance_tolerance ) { if (nullptr == control_net_point) return nullptr; const ON_3dPoint P(control_net_point); if (false == P.IsValid()) return nullptr; if (false == (0.0 <= distance_tolerance)) return nullptr; const ON_SubDVertex* v = FindVertex(&P.x, distance_tolerance); return (nullptr != v) ? v : AddVertex(&P.x); } const ON_SubDEdgePtr ON_SubD::FindEdge( const class ON_SubDVertex* v0, const class ON_SubDVertex* v1 ) const { return ON_SubDEdge::FromVertices(v0, v1); } const ON_SubDEdgePtr ON_SubD::FindOrAddEdge( class ON_SubDVertex* v0, class ON_SubDVertex* v1 ) { ON_SubDEdgePtr eptr = ON_SubDEdge::FromVertices(v0, v1); if (nullptr == eptr.Edge()) eptr = ON_SubDEdgePtr::Create(AddEdge(v0, v1), 0); return eptr; } class ON_SubDEdge* ON_SubD::AddEdge( ON_SubDVertex* v0, ON_SubDVertex* v1 ) { return ON_SubD::AddEdge(ON_SubDEdgeTag::Unset, v0, v1); } class ON_SubDEdge* ON_SubD::AddEdge( ON_SubDEdgeTag edge_tag, ON_SubDVertex* v0, ON_SubDVertex* v1 ) { // NO automatic edge tag setting - causes more problems than it helps. // Users can call ON_SubD::EdgeTagFromContext() if they want to // preset the edge tag based on context. return AddEdgeWithSectorCoefficients( edge_tag, v0, ON_SubDSectorType::UnsetSectorCoefficient, v1, ON_SubDSectorType::UnsetSectorCoefficient ); } 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_SubDimple* subdimple = SubDimple(true); if (nullptr != subdimple) return subdimple->AddEdge(edge_tag, v0, v0_sector_coefficient, v1, v1_sector_coefficient); return ON_SUBD_RETURN_ERROR(nullptr); } class 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, unsigned int initial_face_capacity ) { ON_SubDimple* subdimple = SubDimple(true); if (nullptr != subdimple) return subdimple->AddEdge(candidate_edge_id, edge_tag, v0, v0_sector_coefficient, v1, v1_sector_coefficient, initial_face_capacity); return ON_SUBD_RETURN_ERROR(nullptr); } bool ON_SubD::ReturnEdgeForExperts( ON_SubDEdge* e ) { if (nullptr == e) return false; if (this->InSubD(e) && e->IsActive() && 0 == e->m_face_count && nullptr == e->m_vertex[0] && nullptr == e->m_vertex[1]) { ON_SubDimple* subdimple = SubDimple(false); if (nullptr != subdimple) { subdimple->ReturnEdge(e); return true; } } // Caller is not an expert but a crash has been prevented. return ON_SUBD_RETURN_ERROR(false); } class ON_SubDFace* ON_SubDimple::AddFace( unsigned int edge_count, const ON_SubDEdgePtr* edge ) { return AddFace(0U, edge_count, edge); } class ON_SubDFace* ON_SubDimple::AddFace( unsigned int candidate_face_id, unsigned int edge_count, const ON_SubDEdgePtr* edge ) { if ( edge_count > 0 && nullptr == edge) return ON_SUBD_RETURN_ERROR(nullptr); unsigned f_level = 0; bool bHaveLevel = false; for (unsigned int i = 0; i < edge_count; i++) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(edge[i].m_ptr); if (nullptr == e) continue; if (bHaveLevel) { if (e->SubdivisionLevel() != f_level) return ON_SUBD_RETURN_ERROR(nullptr); } else { f_level = e->SubdivisionLevel(); bHaveLevel = true; } } ON_SubDFace* f = AllocateFace( candidate_face_id, 0, 0); if ( nullptr == f) return ON_SUBD_RETURN_ERROR(nullptr); f->SetSubdivisionLevel(f_level); if (edge_count > 0) { if (edge_count > 4) { if (false == m_heap.GrowFaceEdgeArray(f,edge_count)) { ReturnFace(f); return ON_SUBD_RETURN_ERROR(nullptr); } } ON_SubDEdgePtr* f_edge = f->m_edge4; for (unsigned int i = 0; i < edge_count; i++) { if (4 == i) f_edge = f->m_edgex - 4; f_edge[i] = edge[i]; ON__UINT_PTR eptr = edge[i].m_ptr; ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr); if ( nullptr == e) continue; eptr = ON_SUBD_EDGE_DIRECTION(eptr); ON_SubDVertex* v = const_cast(e->m_vertex[eptr]); if (false == m_heap.GrowVertexFaceArrayByOne(v)) { v->m_status.SetDamagedState(true); ReturnFace(f); return ON_SUBD_RETURN_ERROR(nullptr); } v->m_faces[v->m_face_count++] = f; //if (1 == v->m_face_count) //{ // if (4 == f->m_edge_count) // v->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad; // else if (3 == f->m_edge_count) // v->m_vertex_facet_type = ON_SubD::VertexFacetType::Tri; // else if ( f->m_edge_count > 4) // v->m_vertex_facet_type = ON_SubD::VertexFacetType::Ngon; //} //else //{ // const ON_SubDFace* f0 = v->m_faces[0]; // if (nullptr == f0 || f0->m_edge_count != f->m_edge_count) // v->m_vertex_facet_type = ON_SubD::VertexFacetType::Mixed; //} //v->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; ON_SubDFacePtr* e_faces; if (e->m_face_count < 2) { e_faces = e->m_face2; } else { // Dale Lear, April 3, 2019 RH-49843 - we support non-manifold SubD objects now. //if (2 == e->m_face_count) //{ // // Getting this error in a valid situation means it is time // // to support non-manifold subdivision objects. // ON_SubDIncrementErrorCount(); // ON_WARNING("creating non-manifold subdivision object"); //} if (false == m_heap.GrowEdgeFaceArrayByOne(e)) { e->m_status.SetDamagedState(true); continue; } e_faces = e->m_facex - 2; } e_faces[e->m_face_count++] = ON_SubDFacePtr::Create(f, eptr); } f->m_edge_count = (unsigned short)edge_count; } if ( nullptr == AddFaceToLevel(f)) return ON_SUBD_RETURN_ERROR(nullptr); return f; } 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) { if (input_sector_coefficient[0] >= 0.0 && input_sector_coefficient[0] <= 1.0 && input_sector_coefficient[1] >= 0.0 && input_sector_coefficient[1] <= 1.0 ) return false; } m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; 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) { m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::Create( this, tagged_end_index).SectorCoefficient(); } else if (2 == tagged_end_index) { if (ON_SubDEdgeTag::Smooth == m_edge_tag && 2 == m_face_count ) const_cast(this)->m_edge_tag = ON_SubDEdgeTag::SmoothX; 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(); } } } const bool bNoChanges = input_sector_coefficient[0] == m_sector_coefficient[0] && input_sector_coefficient[1] == m_sector_coefficient[1]; return (false == bNoChanges); } 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) { if (const_cast(edge)->UpdateEdgeSectorCoefficientsForExperts(bUnsetEdgeSectorCoefficientsOnly)) ++changed_edge_count; } return changed_edge_count; } class ON_SubDFace* ON_SubD::AddTriangleFace( class ON_SubDEdge* edge0, class ON_SubDEdge* edge1, class ON_SubDEdge* edge2 ) { ON_SubDEdge* edges[3] = { edge0,edge1,edge2 }; return AddFace(edges, 3); } class ON_SubDFace* ON_SubD::AddQuadFace( class ON_SubDEdge* edge0, class ON_SubDEdge* edge1, class ON_SubDEdge* edge2, class ON_SubDEdge* edge3 ) { ON_SubDEdge* edges[4] = { edge0,edge1,edge2,edge3 }; return AddFace(edges, 4); } class ON_SubDFace* ON_SubD::AddTriangleFace( ON_SubDEdgePtr edge0, ON_SubDEdgePtr edge1, ON_SubDEdgePtr edge2 ) { ON_SubDEdgePtr eptr3[3] = { edge0,edge1,edge2 }; return AddFace(eptr3, 3); } class ON_SubDFace* ON_SubD::AddQuadFace( ON_SubDEdgePtr edge0, ON_SubDEdgePtr edge1, ON_SubDEdgePtr edge2, ON_SubDEdgePtr edge3 ) { ON_SubDEdgePtr eptr4[4] = { edge0,edge1,edge2,edge3 }; return AddFace(eptr4, 4); } class ON_SubDFace* ON_SubD::AddFace( const ON_SimpleArray& edges ) { return AddFace(edges.Array(), edges.UnsignedCount()); } class ON_SubDFace* ON_SubD::AddFace( const ON_SimpleArray& edges ) { return AddFace(edges.Array(), edges.UnsignedCount()); } class ON_SubDFace* ON_SubD::AddFace( ON_SubDEdge *const* edge, unsigned int edge_count ) { if (edge_count < 3 || nullptr == edge) return ON_SUBD_RETURN_ERROR(nullptr); if (nullptr == edge[0] || nullptr == edge[0]->m_vertex[0] || nullptr == edge[0]->m_vertex[1] || edge[0]->m_vertex[0] == edge[0]->m_vertex[1]) return ON_SUBD_RETURN_ERROR(nullptr); if ( edge[0] == edge[edge_count-1] ) return ON_SUBD_RETURN_ERROR(nullptr); ON_SubDEdgePtr* eptr = (ON_SubDEdgePtr*)onmalloc(edge_count * sizeof(*eptr)); if (nullptr == eptr) return ON_SUBD_RETURN_ERROR(nullptr); eptr[0] = ON_SubDEdgePtr::Create( edge[0], (edge[0]->m_vertex[0] == edge[1]->m_vertex[0] || edge[0]->m_vertex[0] == edge[1]->m_vertex[1]) ? 1 : 0 ); eptr[edge_count - 1] = ON_SubDEdgePtr::Null; for (unsigned int fei = 1; fei < edge_count; ++fei) { if (nullptr == edge[fei] || nullptr == edge[fei]->m_vertex[0] || nullptr == edge[fei]->m_vertex[1] || edge[fei]->m_vertex[0] == edge[fei]->m_vertex[1]) break; if (edge[fei - 1] == edge[fei]) break; const ON_SubDVertex* v = eptr[fei - 1].RelativeVertex(1); if (nullptr == v) break; eptr[fei] = ON_SubDEdgePtr::Create(edge[fei], v == edge[fei]->m_vertex[0] ? 0 : 1); if (v != eptr[fei].RelativeVertex(0)) break; } ON_SubDFace* face = (eptr[edge_count - 1].IsNotNull() && eptr[0].RelativeVertex(0) == eptr[edge_count - 1].RelativeVertex(1)) ? AddFace(eptr, edge_count) : nullptr; onfree(eptr); if (nullptr == face) { ON_SUBD_ERROR("Invalid input edge[] array"); } return face; } class ON_SubDFace* ON_SubD::AddFace( const ON_SubDEdgePtr* edge, unsigned int edge_count ) { ON_SubDimple* subdimple = SubDimple(true); return (nullptr != subdimple) ? subdimple->AddFace(edge_count, edge) : nullptr; } class ON_SubDFace* ON_SubD::AddFaceForExperts( unsigned int candiate_face_id, const ON_SubDEdgePtr* edge, unsigned int edge_count ) { ON_SubDimple* subdimple = SubDimple(true); return (nullptr != subdimple) ? subdimple->AddFace(candiate_face_id, edge_count, edge) : nullptr; } bool ON_SubD::ReturnFaceForExperts( ON_SubDFace* f ) { if (nullptr == f) return false; if (this->InSubD(f) && f->IsActive() && 0 == f->m_edge_count) { ON_SubDimple* subdimple = SubDimple(false); if (nullptr != subdimple) { subdimple->ReturnFace(f); return true; } } // Caller is not an expert but a crash has been prevented. return ON_SUBD_RETURN_ERROR(false); } 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, ON_SubDEdge* edge, ON__UINT_PTR edge_direction ) { return AddFaceEdgeConnection(face, i, ON_SubDEdgePtr::Create(edge, edge_direction)); } bool ON_SubD::AddFaceEdgeConnection( ON_SubDFace* face, unsigned int i, ON_SubDEdgePtr eptr ) { return AddFaceEdgeConnection(face, i, eptr, false, false); } bool ON_SubD::AddFaceEdgeConnection(ON_SubDFace* face, unsigned int i, ON_SubDEdgePtr eptr, bool bAddbAddFaceToRelativeVertex0, bool bAddbAddFaceToRelativeVertex1) { if (nullptr == face && i >= ON_SubDFace::MaximumEdgeCount) { return ON_SUBD_RETURN_ERROR(false); } unsigned int face_edge_count = (unsigned int)face->m_edge_count + 1U; if ( face_edge_count <= i ) face_edge_count = i+1; ON_SubDEdge* edge = eptr.Edge(); if (nullptr != edge) { if (edge->m_face_count >= edge->m_facex_capacity + (unsigned short)2) { if (false == GrowEdgeFaceArray(edge, 0)) return ON_SUBD_RETURN_ERROR(false); } ON_SubDFacePtr fptr = ON_SubDFacePtr::Create(face,eptr.EdgeDirection()); unsigned short efi = edge->m_face_count; if ( efi < 2 ) edge->m_face2[efi] = fptr; else { if ( nullptr == edge->m_facex ) return ON_SUBD_RETURN_ERROR(false); edge->m_facex[efi - 2] = fptr; } edge->m_face_count++; } if (face_edge_count > ((unsigned int)face->m_edgex_capacity) + 4U) { if (false == GrowFaceEdgeArray(face,face_edge_count)) return ON_SUBD_RETURN_ERROR(false); } if (i >= ((unsigned int)face->m_edge_count)) { unsigned int j = face->m_edge_count; for (/*empty init*/;j < 4; j++) face->m_edge4[j] = ON_SubDEdgePtr::Null; for (/*empty init*/;j < i; j++) face->m_edgex[j-4] = ON_SubDEdgePtr::Null; } else { for (unsigned int j = face_edge_count - 1; j > i; j--) { if (j > 4) face->m_edgex[j - 4] = face->m_edgex[j - 5]; else if (4 == j) face->m_edgex[0] = face->m_edge4[3]; else face->m_edge4[j] = face->m_edge4[j - 1]; } } if ( i < 4 ) face->m_edge4[i] = eptr; else face->m_edgex[i-4] = eptr; face->m_edge_count = (unsigned short)face_edge_count; for (unsigned evi = 0; evi < 2; ++evi) { ON_SubDVertex* v = const_cast((0 == evi ? bAddbAddFaceToRelativeVertex0 : bAddbAddFaceToRelativeVertex1) ? eptr.RelativeVertex(evi) : nullptr); if (nullptr != v) { if ( false == this->GrowVertexFaceArray(v, v->m_face_count + 1)) return ON_SUBD_RETURN_ERROR(false); v->m_faces[v->m_face_count++] = face; } } return true; } bool ON_SubD::SetFaceBoundary( ON_SubDFace* face, const ON_SimpleArray& edges ) { return SetFaceBoundary(face, edges.Array(), edges.UnsignedCount()); } bool ON_SubD::SetFaceBoundary( ON_SubDFace* face, const ON_SubDEdgePtr* edges, size_t edge_count ) { // Do a little validation to prevent disasters. if (nullptr == face) return ON_SUBD_RETURN_ERROR(false); if (0 != face->m_edge_count) return ON_SUBD_RETURN_ERROR(false); if (nullptr == edges || edge_count < 3 || edge_count > ((size_t)ON_SubDFace::MaximumEdgeCount)) return ON_SUBD_RETURN_ERROR(false); const ON_SubDVertex* v1 = edges[edge_count - 1].RelativeVertex(1); if ( nullptr == v1) return ON_SUBD_RETURN_ERROR(false); for (size_t fei = 0; fei < edge_count; ++fei) { const ON_SubDVertex* v0 = edges[fei].RelativeVertex(0); if ( v0 != v1) return ON_SUBD_RETURN_ERROR(false); v1 = edges[fei].RelativeVertex(1); if ( nullptr == v1 || v0 == v1) return ON_SUBD_RETURN_ERROR(false); } // set face-edge pointers and add face to vertex face arrays if (false == this->GrowFaceEdgeArray(face, edge_count)) return ON_SUBD_RETURN_ERROR(false); for (size_t fei = 0; fei < edge_count; ++fei) { ON_SubDEdgePtr eptr = edges[fei]; if (false == this->AddFaceEdgeConnection(face, (unsigned)fei, eptr, true, false)) return ON_SUBD_RETURN_ERROR(false); } return true; } bool ON_SubD::RemoveFaceEdgeConnection( ON_SubDFace* face, ON_SubDEdge* edge ) { ON_SubDEdgePtr removed_edge; return RemoveFaceEdgeConnection(face, face->EdgeArrayIndex(edge), removed_edge); } bool ON_SubD::RemoveFaceEdgeConnection( ON_SubDFace* face, unsigned int i ) { ON_SubDEdgePtr removed_edge; return RemoveFaceEdgeConnection(face, i, removed_edge); } bool ON_SubD::RemoveFaceEdgeConnection( ON_SubDFace* face, unsigned int i, ON_SubDEdgePtr& removed_edge ) { removed_edge = ON_SubDEdgePtr::Null; if ( nullptr == face && i >= (unsigned int)face->m_edge_count ) { return ON_SUBD_RETURN_ERROR(false); } if ( false == face->RemoveEdgeFromArray(i,removed_edge) ) return ON_SUBD_RETURN_ERROR(false); ON_SubDEdge* edge = removed_edge.Edge(); if (nullptr != edge) { if (false == edge->RemoveFaceFromArray(face)) return ON_SUBD_RETURN_ERROR(false); } return true; } bool ON_SubD::RemoveFaceConnections( ON_SubDFace* face ) { if ( nullptr == face ) { return ON_SUBD_RETURN_ERROR(false); } if (face->m_edge_count > 0) { ON_SubDEdgePtr removed_edge; for (unsigned short fei = face->m_edge_count; fei > 0; --fei) { removed_edge = ON_SubDEdgePtr::Null; if (false == face->RemoveEdgeFromArray(fei - 1, removed_edge)) return ON_SUBD_RETURN_ERROR(false); ON_SubDEdge* edge = removed_edge.Edge(); if (nullptr != edge) { if (false == edge->RemoveFaceFromArray(face)) return ON_SUBD_RETURN_ERROR(false); for (int evi = 0; evi < 2; ++evi) { ON_SubDVertex* v = const_cast(edge->m_vertex[evi]); if (nullptr != v) { for (unsigned short vfi = 0; vfi < v->m_face_count; ++vfi) { if (face == v->m_faces[vfi]) { for (++vfi; vfi < v->m_face_count; ++vfi) v->m_faces[vfi - 1] = v->m_faces[vfi]; v->m_face_count--; break; } } } } } } face->m_edge_count = 0; } return true; } static bool ON_SubDFace_GetSubdivisionPointError( const class ON_SubDFace* face, double subdivision_point[3], bool bDamagedState ) { if (nullptr == subdivision_point) return ON_SUBD_RETURN_ERROR(false); // caller passed a null pointer - face is not necessarily damaged // make sure returned point is not used by a caller who doesn't bother to check return codes. subdivision_point[0] = ON_DBL_QNAN; subdivision_point[1] = ON_DBL_QNAN; subdivision_point[2] = ON_DBL_QNAN; if (nullptr == face) return ON_SUBD_RETURN_ERROR(false); // face is damaged in some way - mark it face->m_status.SetDamagedState(bDamagedState); return ON_SUBD_RETURN_ERROR(false); } const ON_3dPoint ON_SubDFace::SubdivisionPoint() const { ON_3dPoint S; return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint; } bool ON_SubDFace::GetSubdivisionPoint( double subdivision_point[3] ) const { if (nullptr == subdivision_point) return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, false); if (GetSavedSubdivisionPoint(subdivision_point)) return true; if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point)) { SetSavedSubdivisionPoint(subdivision_point); return true; } return false; } bool ON_SubDFace::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const { if (nullptr == subdivision_point) return ON_SubDFace_GetSubdivisionPointError(this,subdivision_point,false); const unsigned int count = m_edge_count; if (count < 3) return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); const class ON_SubDEdgePtr* edge_ptr = m_edge4; ON__UINT_PTR e_ptr; const ON_SubDEdge* e; ON__UINT_PTR edir; const double* vertexP[4]; // Use faster code for the case when the face is a quad. // Since this is a Catmull-Clark subdivision scheme, this // case is the most common by far and code that gives quads // special treatment will run noticably faster. e_ptr = edge_ptr[0].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); if ( nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] ) return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); edir = ON_SUBD_EDGE_DIRECTION(e_ptr); vertexP[0] = e->m_vertex[edir]->m_P; vertexP[1] = e->m_vertex[1 - edir]->m_P; e_ptr = edge_ptr[2].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); edir = ON_SUBD_EDGE_DIRECTION(e_ptr); vertexP[2] = e->m_vertex[edir]->m_P; vertexP[3] = e->m_vertex[1 - edir]->m_P; if (4 == count) { // most common case in quad subdivision schemes subdivision_point[0] = (vertexP[0][0] + vertexP[1][0] + vertexP[2][0] + vertexP[3][0])*0.25; subdivision_point[1] = (vertexP[0][1] + vertexP[1][1] + vertexP[2][1] + vertexP[3][1])*0.25; subdivision_point[2] = (vertexP[0][2] + vertexP[1][2] + vertexP[2][2] + vertexP[3][2])*0.25; return true; } if (3 == count) { // 2nd most common case in quad subdivision schemes subdivision_point[0] = (vertexP[0][0] + vertexP[1][0] + vertexP[2][0]) / 3.0; subdivision_point[1] = (vertexP[0][1] + vertexP[1][1] + vertexP[2][1]) / 3.0; subdivision_point[2] = (vertexP[0][2] + vertexP[1][2] + vertexP[2][2]) / 3.0; return true; } // count > 4 double faceP[3] = { (vertexP[0][0] + vertexP[1][0] + vertexP[2][0] + vertexP[3][0]), (vertexP[0][1] + vertexP[1][1] + vertexP[2][1] + vertexP[3][1]), (vertexP[0][2] + vertexP[1][2] + vertexP[2][2] + vertexP[3][2]) }; if (nullptr == m_edgex) { return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); } edge_ptr = m_edgex - 4; // -4 because index i begins at 4 unsigned int i; for (i = 4; i + 1 < count; i += 2) { e_ptr = edge_ptr[i].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); edir = ON_SUBD_EDGE_DIRECTION(e_ptr); vertexP[0] = e->m_vertex[edir]->m_P; vertexP[1] = e->m_vertex[1 - edir]->m_P; faceP[0] += vertexP[0][0]; faceP[1] += vertexP[0][1]; faceP[2] += vertexP[0][2]; faceP[0] += vertexP[1][0]; faceP[1] += vertexP[1][1]; faceP[2] += vertexP[1][2]; } if (i < count) { // odd number of edges and vertices e_ptr = edge_ptr[count - 1].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); edir = ON_SUBD_EDGE_DIRECTION(e_ptr); vertexP[0] = e->m_vertex[edir]->m_P; faceP[0] += vertexP[0][0]; faceP[1] += vertexP[0][1]; faceP[2] += vertexP[0][2]; } const double n = count; subdivision_point[0] = faceP[0] / n; subdivision_point[1] = faceP[1] / n; subdivision_point[2] = faceP[2] / n; return true; } int ON_SubDComponentBase::CompareId( const ON_SubDComponentBase* lhs, const ON_SubDComponentBase* rhs ) { if (lhs == rhs) return 0; // nulls at end of list if (nullptr == rhs) return -1; if (nullptr == lhs) return 1; if (lhs->m_id < rhs->m_id) return -1; if (lhs->m_id > rhs->m_id) return 1; return 0; } void ON_SubDComponentBase::ClearSavedSubdivisionPoint() const { Internal_ClearSubdivisionPointAndSurfacePointFlags(); } bool ON_SubDComponentBase::SetSavedSubdivisionPoint( const double subdivision_point[3] ) const { if (nullptr == subdivision_point) { Internal_ClearSubdivisionPointAndSurfacePointFlags(); return true; } if ( ON_IsValid(subdivision_point[0]) && ON_IsValid(subdivision_point[1]) && ON_IsValid(subdivision_point[2]) ) { m_saved_subd_point1[0] = subdivision_point[0]; m_saved_subd_point1[1] = subdivision_point[1]; m_saved_subd_point1[2] = subdivision_point[2]; m_saved_points_flags |= ON_SUBD_CACHE_POINT_FLAG_BIT; return true; } Internal_ClearSubdivisionPointAndSurfacePointFlags(); return ON_SUBD_RETURN_ERROR(false); } bool ON_SubDComponentBase::GetSavedSubdivisionPoint( double subdivision_point[3] ) const { if ( 0 == (ON_SUBD_CACHE_POINT_FLAG_BIT & m_saved_points_flags) ) return false; if (nullptr != subdivision_point) { subdivision_point[0] = m_saved_subd_point1[0]; subdivision_point[1] = m_saved_subd_point1[1]; subdivision_point[2] = m_saved_subd_point1[2]; } return true; } const ON_3dPoint ON_SubDComponentBase::SavedSubdivisionPoint() const { ON_3dPoint p(ON_3dPoint::NanPoint); return GetSavedSubdivisionPoint(&p.x) ? p : ON_3dPoint::NanPoint; } unsigned const ON_SubDComponentBase::SubdivisionLevel() const { return (unsigned)m_level; } void ON_SubDComponentBase::SetSubdivisionLevel(unsigned level) { if (level <= 255U) m_level = ((unsigned char)level); } const ON_ComponentStatus ON_SubDComponentBase::Status() const { return m_status; } bool ON_SubDComponentBase::IsActive() const { return (m_id > 0 && m_archive_id != ON_UNSET_UINT_INDEX); } bool ON_SubDComponentBase::IsSymmetrySetPrimaryMotif() const { return 1 == this->m_symmetry_set_next.ComponentDirection(); } bool ON_SubDComponentBase::InSymmetrySet() const { return this->m_symmetry_set_next.IsNotNull(); } bool ON_SubDComponentPtr::IsSymmetrySetPrimaryMotif() const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->IsSymmetrySetPrimaryMotif() : false; } bool ON_SubDComponentPtr::InSymmetrySet() const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->InSymmetrySet() : false; } bool ON_SubDComponentBase::Mark() const { return m_status.RuntimeMark(); } bool ON_SubDComponentBase::ClearMark() const { return m_status.ClearRuntimeMark(); } bool ON_SubDComponentBase::SetMark() const { return m_status.SetRuntimeMark(); } bool ON_SubDComponentBase::SetMark( bool bMark ) const { return m_status.SetRuntimeMark(bMark); } ON__UINT8 ON_SubDComponentBase::MarkBits() const { return m_status.MarkBits(); } ON__UINT8 ON_SubDComponentBase::SetMarkBits( ON__UINT8 mark_bits ) const { return m_status.SetMarkBits(mark_bits); } ON__UINT8 ON_SubDComponentBase::ClearMarkBits() const { return m_status.SetMarkBits(0); } bool ON_SubDComponentPtr::IsActive() const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->IsActive() : false; } bool ON_SubDVertexPtr::IsActive() const { const ON_SubDVertex* v = ON_SUBD_VERTEX_POINTER(m_ptr); return (nullptr != v) ? v->IsActive() : false; } bool ON_SubDEdgePtr::IsActive() const { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr != e) ? e->IsActive() : false; } bool ON_SubDFacePtr::IsActive() const { const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr); return (nullptr != f) ? f->IsActive() : false; } const ON_ComponentStatus ON_SubDVertex::NeighborhoodStatusLogicalOr( bool bIncludeEdges, bool bIncludeFaces ) const { ON_ComponentStatus s(m_status); if (bIncludeEdges && nullptr != m_edges) { for (unsigned short vei = 0; vei < m_edge_count; vei++) { const ON_SubDEdge* e = m_edges[vei].Edge(); if (nullptr != e) s = ON_ComponentStatus::LogicalOr(s, e->m_status); } } if (bIncludeFaces && nullptr != m_faces) { for (unsigned short vfi = 0; vfi < m_face_count; vfi++) { const ON_SubDFace* f = m_faces[vfi]; if (nullptr != f) s = ON_ComponentStatus::LogicalOr(s, f->m_status); } } return s; } const ON_ComponentStatus ON_SubDEdge::NeighborhoodStatusLogicalOr( bool bIncludeVertices, bool bIncludeFaces ) const { ON_ComponentStatus s(m_status); if (bIncludeVertices) { for (unsigned int evi = 0; evi < 2; evi++) { const ON_SubDVertex* v = m_vertex[evi]; if (nullptr != v) s = ON_ComponentStatus::LogicalOr(s, v->m_status); } } if (bIncludeFaces) { const ON_SubDFacePtr* fptr = m_face2; for (unsigned short vfi = 0; vfi < m_face_count; vfi++) { const ON_SubDFace* f = fptr->Face(); if (nullptr != f) s = ON_ComponentStatus::LogicalOr(s, f->m_status); if (1 == vfi) { fptr = m_facex; if (nullptr == fptr) break; } else fptr++; } } return s; } const ON_ComponentStatus ON_SubDFace::NeighborhoodStatusLogicalOr(bool bIncludeVertices, bool bIncludeEdges) const { ON_ComponentStatus s(m_status); if (bIncludeVertices || bIncludeEdges) { const ON_SubDEdgePtr* eptr = m_edge4; for (unsigned int fei = 0; fei < m_edge_count; fei++) { if (4 == fei) { eptr = m_edgex; if (nullptr == eptr) break; } const ON_SubDEdge* e = eptr->Edge(); if (nullptr != e) { if (bIncludeEdges) { s = ON_ComponentStatus::LogicalOr(s, e->m_status); } if (bIncludeVertices) { const ON_SubDVertex* v = e->m_vertex[(0!=eptr->EdgeDirection())?1:0]; if (nullptr != v) s = ON_ComponentStatus::LogicalOr(s, v->m_status); } } eptr++; } } 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. face->ClearSavedSubdivisionPoints(); const ON_SubDEdgePtr* eptr = face->m_edge4; for (unsigned int efi = 0; efi < face->m_edge_count; ++efi, ++eptr) { if (4 == efi) { eptr = face->m_edgex; if ( nullptr == eptr || face->m_edgex_capacity < face->m_edge_count - 4) { ON_SUBD_ERROR("Invalid face edge count or edgex information."); break; } } const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr != edge) { edge->ClearSavedSubdivisionPoints(); edge->UnsetSectorCoefficientsForExperts(); for (unsigned int evi = 0; evi < 2; evi++) { const ON_SubDVertex* vertex = edge->m_vertex[evi]; if (nullptr == vertex) continue; Internal_ClearVertexNeighborhoodCache(vertex); } } } } 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++) { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); if (nullptr == edge) continue; edge->ClearSavedSubdivisionPoints(); edge->UnsetSectorCoefficientsForExperts(); const ON_SubDVertex* v1 = edge->m_vertex[1-ON_SUBD_EDGE_DIRECTION(m_edges[vei].m_ptr)]; if (nullptr != v1) v1->ClearSavedSubdivisionPoints(); } if (nullptr != m_faces) { // 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++) { if (nullptr != m_vertex[evi]) m_vertex[evi]->VertexModifiedNofification(); } } void ON_SubDEdge::UnsetSectorCoefficientsForExperts() const { const_cast(this)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; const_cast(this)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; } void ON_SubDVertex::UnsetSectorCoefficientsForExperts(unsigned int relative_edge_end_dex) const { for (unsigned short vei = 0; vei < m_edge_count; ++vei) { ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); if (nullptr == e) continue; ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(m_edges[vei].m_ptr); const unsigned evi = (relative_edge_end_dex < 2) ? ((0 == edir ? false : true) == (0 == relative_edge_end_dex ? false : true) ? 0U : 1U) : 2U; if ( evi < 2) e->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorCoefficient; else { e->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; e->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; } } } void ON_SubDFace::FaceModifiedNofification() const { ClearSavedSubdivisionPoints(); const ON_SubDEdgePtr* eptr = m_edge4; for (unsigned int efi = 0; efi < m_edge_count; efi++) { if (4 == efi) { eptr = m_edgex; if ( nullptr == eptr) break; } const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr != edge) 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); ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(m_saved_points_flags); } bool ON_SubDComponentBase::Internal_SubdivisionPointFlag() const { return (0 != ON_SUBD_CACHE_POINT_FLAG(m_saved_points_flags)); } void ON_SubDComponentBase::Internal_ClearSubdivisionPointFlag() const { ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags); } bool ON_SubDComponentBase::Internal_SurfacePointFlag() const { return (0 != ON_SUBD_CACHE_LIMITLOC_FLAG(m_saved_points_flags)); } void ON_SubDComponentBase::Internal_ClearSurfacePointFlag() const { ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(m_saved_points_flags); } bool ON_SubDComponentBase::SavedSubdivisionPointIsSet() const { return (0 != ON_SUBD_CACHE_POINT_FLAG(m_saved_points_flags)) ? (ON_IS_VALID(m_saved_subd_point1[0]) && ON_IS_VALID(m_saved_subd_point1[1]) && ON_IS_VALID(m_saved_subd_point1[2])) : false; } bool ON_SubDEdge::EdgeSurfaceCurveIsSet() const { return false; } bool ON_SubDComponentBase::SubdivisionDisplacementIsNonzero() const { // deprecated - never used. return false; } bool ON_SubDComponentBase::SetSubdivisionDisplacement(const double*) { // deprecated - never used. return false; } void ON_SubDComponentBase::ClearSubdivisionDisplacement() const { // deprecated - never used. } bool ON_SubDComponentBase::GetSubdivisionDisplacement(double*) const { // deprecated - never used. return false; } const ON_3dVector ON_SubDComponentBase::SubdivisionDisplacement() const { // deprecated - never used. return ON_3dVector::ZeroVector; } void ON_SubDComponentBase::Internal_SetSavedSurfacePointFlag(bool bSavedSurfacePointFlag) const { if (bSavedSurfacePointFlag) m_saved_points_flags |= ON_SUBD_CACHE_LIMITLOC_FLAG_BIT; else Internal_ClearSurfacePointFlag(); } void ON_SubDComponentBase::Internal_SetModified1Flag() const { m_saved_points_flags |= ON_SubDComponentBase::ModifiedFlags::Modified1Bit; } void ON_SubDComponentBase::Internal_SetModified2Flag() const { m_saved_points_flags |= ON_SubDComponentBase::ModifiedFlags::Modified1Bit; } void ON_SubDComponentBase::Internal_ClearModifiedFlags() const { m_saved_points_flags &= ~ON_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask; } bool ON_SubDComponentBase::Internal_Modified1IsSet() const { return (0 != (m_saved_points_flags & ON_SubDComponentBase::ModifiedFlags::Modified1Bit)); } bool ON_SubDComponentBase::Internal_Modified1or2IsSet() const { return (0 != (m_saved_points_flags & ON_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask)); } bool ON_SubDFace::ReverseEdgeList() { const unsigned int edge_count = m_edge_count; if ( 0 == edge_count) return true; if (edge_count > 4 && nullptr == m_edgex) { return ON_SUBD_RETURN_ERROR(false); } ON_SubDEdgePtr buffer[16]; ON_SubDEdgePtr* reversed_eptrs; if ( edge_count <= sizeof(buffer)/sizeof(buffer[0]) ) reversed_eptrs = buffer; else { reversed_eptrs = new(std::nothrow) ON_SubDEdgePtr[edge_count]; if ( nullptr == reversed_eptrs) return ON_SUBD_RETURN_ERROR(false); } ON_SubDEdgePtr* face_eptrs = m_edge4; for (unsigned int fei = 0; fei < edge_count; fei++, face_eptrs++) { if (4 == fei) face_eptrs = m_edgex; ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(face_eptrs->m_ptr); if ( nullptr == e) continue; ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(face_eptrs->m_ptr); reversed_eptrs[edge_count-1-fei] = ON_SubDEdgePtr::Create(e,1-edir); ON_SubDFacePtr* edges_fptrs = e->m_face2; const unsigned int face_count = e->m_face_count; for (unsigned int efi = 0; efi < face_count; efi++, edges_fptrs++) { if (2 == efi) { edges_fptrs = e->m_facex; if ( nullptr == edges_fptrs) break; } if ( this != ON_SUBD_FACE_POINTER(edges_fptrs->m_ptr) ) continue; *edges_fptrs = ON_SubDFacePtr::Create(this,1-ON_SUBD_FACE_DIRECTION(edges_fptrs->m_ptr)); break; } } face_eptrs = m_edge4; for (unsigned int fei = 0; fei < edge_count; fei++) { if (4 == fei) face_eptrs = m_edgex; *face_eptrs++ = reversed_eptrs[fei]; } if ( reversed_eptrs != buffer ) delete[] reversed_eptrs; return true; } static bool ON_SubDEdge_GetSubdivisionPointError( const class ON_SubDEdge* edge, double edge_point[3], const double* edgeP[2], bool bDamagedState ) { if (nullptr == edge || nullptr == edge_point) return false; // caller passed a null pointer - edge is not necessarily damaged ON_SubDIncrementErrorCount(); edge->m_status.SetDamagedState(bDamagedState); if (nullptr != edgeP && nullptr != edgeP[0] && nullptr != edgeP[1]) { const double edgePsum[3] = { edgeP[0][0] + edgeP[1][0], edgeP[0][1] + edgeP[1][1], edgeP[0][2] + edgeP[1][2] }; edge_point[0] = 0.5*edgePsum[0]; edge_point[1] = 0.5*edgePsum[1]; edge_point[2] = 0.5*edgePsum[2]; } return true; } unsigned int ON_SubDEdge::GetFacePointSum( const ON_SubDFace* face, const ON_SubDEdge* edge, double* facePsum ) { const ON_SubDEdge* e; ON__UINT_PTR e_ptr, edir; const double* vertexP[2]; if (nullptr == face) return 0; const unsigned int n = face->m_edge_count; if (3 == n) { if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[0].m_ptr)) e_ptr = face->m_edge4[1].m_ptr; else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[1].m_ptr)) e_ptr = face->m_edge4[2].m_ptr; else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[2].m_ptr)) e_ptr = face->m_edge4[0].m_ptr; else return 0; e = ON_SUBD_EDGE_POINTER(e_ptr); if (nullptr == e) return 0; if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) return 0; edir = ON_SUBD_EDGE_DIRECTION(e_ptr); if (edge->m_vertex[0] != e->m_vertex[edir] && edge->m_vertex[1] != e->m_vertex[edir]) return 0; vertexP[0] = e->m_vertex[1 - edir]->m_P; facePsum[0] = vertexP[0][0]; facePsum[1] = vertexP[0][1]; facePsum[2] = vertexP[0][2]; return n; } if (4 == n) { if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[0].m_ptr)) e_ptr = face->m_edge4[2].m_ptr; else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[1].m_ptr)) e_ptr = face->m_edge4[3].m_ptr; else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[2].m_ptr)) e_ptr = face->m_edge4[0].m_ptr; else if (edge == ON_SUBD_EDGE_POINTER(face->m_edge4[3].m_ptr)) e_ptr = face->m_edge4[1].m_ptr; else return 0; e = ON_SUBD_EDGE_POINTER(e_ptr); if (nullptr == e) return 0; if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) return 0; edir = ON_SUBD_EDGE_DIRECTION(e_ptr); vertexP[0] = e->m_vertex[edir]->m_P; vertexP[1] = e->m_vertex[1 - edir]->m_P; facePsum[0] = vertexP[0][0] + vertexP[1][0]; facePsum[1] = vertexP[0][1] + vertexP[1][1]; facePsum[2] = vertexP[0][2] + vertexP[1][2]; return n; } if (n < 3) return 0; const ON_SubDEdgePtr* edgeptr = face->m_edge4; const ON_SubDVertex* edge_vertex[2] = { edge->m_vertex[0], edge->m_vertex[1] }; facePsum[0] = 0.0; facePsum[1] = 0.0; facePsum[2] = 0.0; for (unsigned i = 0; i < n; i++) { if (4 == i) edgeptr = face->m_edgex - 4; e = ON_SUBD_EDGE_POINTER(edgeptr[i].m_ptr); if (nullptr == e) return 0; if (edge == e) continue; edir = ON_SUBD_EDGE_DIRECTION(edgeptr[i].m_ptr); const ON_SubDVertex* e_vertex[2] = { e->m_vertex[edir], e->m_vertex[1 - edir] }; if (nullptr == e_vertex[0] || nullptr == e_vertex[1]) return 0; if (edge_vertex[0] != e_vertex[0] && edge_vertex[1] != e_vertex[0]) { vertexP[0] = e_vertex[0]->m_P; facePsum[0] += vertexP[0][0]; facePsum[1] += vertexP[0][1]; facePsum[2] += vertexP[0][2]; } if (i + 1 < n) { // start of next edge = end of this edge if (edge_vertex[0] != e_vertex[1] && edge_vertex[1] != e_vertex[1]) { vertexP[0] = e_vertex[1]->m_P; facePsum[0] += vertexP[0][0]; facePsum[1] += vertexP[0][1]; facePsum[2] += vertexP[0][2]; } i++; if (4 == i && n > 4) edgeptr = face->m_edgex - 4; } } return n; } const ON_3dPoint ON_SubDEdge::SubdivisionPoint() const { ON_3dPoint S; return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint; } bool ON_SubDEdge::GetSubdivisionPoint( double subdivision_point[3] ) const { if (nullptr == subdivision_point) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, false); if (GetSavedSubdivisionPoint(subdivision_point)) return true; if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point)) { SetSavedSubdivisionPoint(subdivision_point); return true; } return false; } const ON_3dPoint ON_SubDEdge::ControlNetCenterPoint() const { return 0.5*(ControlNetPoint(0) + ControlNetPoint(1)); } const ON_3dVector ON_SubDEdge::ControlNetCenterNormal( unsigned int edge_face_index ) const { const ON_SubDFace* face = Face(edge_face_index); return (nullptr != face) ? face->ControlNetCenterNormal() : ON_3dVector::NanVector; } bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const { if (nullptr == subdivision_point) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, false); const ON_SubDVertex* edge_vertex[2] = { m_vertex[0], m_vertex[1] }; if (nullptr == edge_vertex[0] || nullptr == edge_vertex[1]) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, true); const double* edgeP[2] = { edge_vertex[0]->m_P, edge_vertex[1]->m_P }; const double edgePsum[3] = { edgeP[0][0] + edgeP[1][0], edgeP[0][1] + edgeP[1][1], edgeP[0][2] + edgeP[1][2] }; if ( IsSmooth() ) { // A smooth edge must have exactly two neighboring faces and // at most one end vertex can be tagged. if (2 != m_face_count) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); const ON_SubDFace* faces[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) }; if (nullptr == faces[0] || nullptr == faces[1]) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); // for each neighbor face, sum the vertex locations that are not on this edge double facePsum[2][3]; const unsigned int face_edge_count[2] = { ON_SubDEdge::GetFacePointSum(faces[0], this, facePsum[0]), ON_SubDEdge::GetFacePointSum(faces[1], this, facePsum[1]) }; if (0 == face_edge_count[0] || 0 == face_edge_count[1]) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); const unsigned int tagged_end = (ON_SubDVertexTag::Smooth != edge_vertex[0]->m_vertex_tag) ? 0 : ((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_SubDEdgeTag::SmoothX == m_edge_tag) ) { // ignore edge weights EP[0] = 0.375*edgePsum[0]; EP[1] = 0.375*edgePsum[1]; EP[2] = 0.375*edgePsum[2]; } 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 ) { double w[2]; w[tagged_end] = m_sector_coefficient[tagged_end]; w[1 - tagged_end] = 1.0 - w[tagged_end]; EP[0] = 0.75*(w[0] * edgeP[0][0] + w[1] * edgeP[1][0]); EP[1] = 0.75*(w[0] * edgeP[0][1] + w[1] * edgeP[1][1]); EP[2] = 0.75*(w[0] * edgeP[0][2] + w[1] * edgeP[1][2]); } else { // error: // Both ends of a smooth vertex are tagged // or weights are incorrectly set // or ... return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); } if (4 == face_edge_count[0] && 4 == face_edge_count[1]) { // common case when both neighboring faces are quads subdivision_point[0] = EP[0] + 0.0625*(facePsum[0][0] + facePsum[1][0]); subdivision_point[1] = EP[1] + 0.0625*(facePsum[0][1] + facePsum[1][1]); subdivision_point[2] = EP[2] + 0.0625*(facePsum[0][2] + facePsum[1][2]); return true; } if (3 == face_edge_count[0] && 3 == face_edge_count[1]) { // common case when both neighboring faces are triangles //// bug in evaluation prior to Nov 11, 2019 ////subdivision_point[0] = EP[0] + 0.125*(facePsum[0][0] + facePsum[1][0]); ////subdivision_point[1] = EP[1] + 0.125*(facePsum[0][1] + facePsum[1][1]); ////subdivision_point[2] = EP[2] + 0.125*(facePsum[0][2] + facePsum[1][2]); subdivision_point[0] = EP[0] + (0.5*edgePsum[0] + facePsum[0][0] + facePsum[1][0]) / 12.0; subdivision_point[1] = EP[1] + (0.5*edgePsum[1] + facePsum[0][1] + facePsum[1][1]) / 12.0; subdivision_point[2] = EP[2] + (0.5*edgePsum[2] + facePsum[0][2] + facePsum[1][2]) / 12.0; return true; } // general formula works for all cases including face_edge_count[0] != face_count[2] //// bug in evaluation prior to Nov 11, 2019 ////const double f0 = 0.125 / ((double)(face_edge_count[0] - 2)); ////const double f1 = 0.125 / ((double)(face_edge_count[1] - 2)); ////subdivision_point[0] = EP[0] + f0 * facePsum[0][0] + f1 * facePsum[1][0]; ////subdivision_point[1] = EP[1] + f0 * facePsum[0][1] + f1 * facePsum[1][1]; ////subdivision_point[2] = EP[2] + f0 * facePsum[0][2] + f1 * facePsum[1][2]; const double f0 = (double)(face_edge_count[0] * 4U); const double f1 = (double)(face_edge_count[1] * 4U); const double x = 1.0 / f0 + 1.0 / f1 - 0.125; subdivision_point[0] = EP[0] + x * edgePsum[0] + facePsum[0][0] / f0 + facePsum[1][0] / f1; subdivision_point[1] = EP[1] + x * edgePsum[1] + facePsum[0][1] / f0 + facePsum[1][1] / f1; subdivision_point[2] = EP[2] + x * edgePsum[2] + facePsum[0][2] / f0 + facePsum[1][2] / f1; return true; } if ( IsCrease() ) { subdivision_point[0] = 0.5*edgePsum[0]; subdivision_point[1] = 0.5*edgePsum[1]; subdivision_point[2] = 0.5*edgePsum[2]; return true; } // invalid edge->m_edge_tag return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); } static unsigned int GetSectorBoundaryEdgesError() { return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDEdge::GetSectorBoundaryEdges( unsigned int edge_vertex_index, ON_SubDEdgePtr* edge_ptr0, ON_SubDEdgePtr* edge_ptr1 ) const { if (nullptr != edge_ptr0) *edge_ptr0 = ON_SubDEdgePtr::Null; if (nullptr != edge_ptr1) *edge_ptr1 = ON_SubDEdgePtr::Null; const unsigned int edge_face_count = m_face_count; if (edge_face_count <= 0 || edge_face_count > 2) return GetSectorBoundaryEdgesError(); if (2 == edge_face_count && ON_SubDEdgeTag::Crease == m_edge_tag) return GetSectorBoundaryEdgesError(); if (0 != edge_vertex_index && 1 != edge_vertex_index) return GetSectorBoundaryEdgesError(); const ON_SubDVertex* vertex = m_vertex[edge_vertex_index]; if (nullptr == vertex || vertex->m_face_count <= 0) return GetSectorBoundaryEdgesError(); const unsigned int vertex_face_count = vertex->m_face_count; unsigned int sector_face_count = 0; ON_SubDEdgePtr sector_boundary[2] = {}; for (unsigned int edge_face_index = 0; edge_face_index < edge_face_count; edge_face_index++) { const ON_SubDEdge* edge0 = this; unsigned int edge0_end_index = edge_vertex_index; unsigned int edge0_face_index = edge_face_index; ON_SubDFacePtr face_ptr = edge0->m_face2[edge0_face_index]; while (sector_face_count < vertex_face_count) { const ON_SubDFace* face = ON_SUBD_FACE_POINTER(face_ptr.m_ptr); if (0 == face) return GetSectorBoundaryEdgesError(); ON__UINT_PTR face_dir = ON_SUBD_FACE_DIRECTION(face_ptr.m_ptr); sector_face_count++; unsigned int face_edge0_index = face->EdgeArrayIndex(edge0); if (ON_UNSET_UINT_INDEX == face_edge0_index) return 0; unsigned int face_edge1_index = face_edge0_index; face_edge1_index += (1 == (edge0_end_index + face_dir)) ? 1 : (face->m_edge_count - 1); face_edge1_index %= face->m_edge_count; ON_SubDEdgePtr edge1_ptr = face->EdgePtr(face_edge1_index); const ON_SubDEdge* edge1 = ON_SUBD_EDGE_POINTER(edge1_ptr.m_ptr); if (nullptr == edge1) return GetSectorBoundaryEdgesError(); unsigned int edge1_end_index = (0 == face_dir) ? (1 - edge0_end_index) : edge0_end_index; if (1 == ON_SUBD_EDGE_DIRECTION(edge1_ptr.m_ptr)) edge1_end_index = 1 - edge1_end_index; if (vertex != edge1->m_vertex[edge1_end_index]) return GetSectorBoundaryEdgesError(); if ( edge1->IsSmooth() && 2 == edge1->m_face_count ) { const ON_SubDFace* edge1_faces[2] = { ON_SUBD_FACE_POINTER(edge1->m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(edge1->m_face2[1].m_ptr) }; unsigned int edge1_face_index = (face == edge1_faces[0] ? 1 : 0); if (nullptr == edge1_faces[edge1_face_index] || face == edge1_faces[edge1_face_index]) return GetSectorBoundaryEdgesError(); face_ptr = edge1->m_face2[edge1_face_index]; edge0 = edge1; edge0_face_index = edge1_face_index; edge0_end_index = edge1_end_index; continue; } sector_boundary[edge_face_index] = ON_SubDEdgePtr::Create(edge1, edge1_end_index); break; } } if (sector_face_count <= 0 || sector_boundary[0].IsNull()) return GetSectorBoundaryEdgesError(); if (1 == edge_face_count) sector_boundary[1] = ON_SubDEdgePtr::Create(this, edge_vertex_index); else if (sector_boundary[1].IsNull()) return GetSectorBoundaryEdgesError(); if (nullptr != edge_ptr0) *edge_ptr0 = sector_boundary[0]; if (nullptr != edge_ptr1) *edge_ptr1 = sector_boundary[1]; return sector_face_count; } class ON_ScratchBuffer { public: ON_ScratchBuffer( size_t sizeof_buffer, void* stack_buffer, size_t sizeof_stack_buffer ) : m_buffer(nullptr) , m_heap_buffer(nullptr) { m_buffer = (sizeof_buffer > sizeof_stack_buffer || nullptr == stack_buffer) ? stack_buffer : (m_heap_buffer = new (std::nothrow) double[1 + sizeof_buffer / sizeof(double)]); } void* Buffer() { return m_buffer; } ~ON_ScratchBuffer() { if (nullptr != m_heap_buffer) { double* p = m_heap_buffer; m_heap_buffer = nullptr; delete[] p; } } private: void* m_buffer; double* m_heap_buffer; private: // prohibit use - no implementation ON_ScratchBuffer(const ON_ScratchBuffer&); ON_ScratchBuffer& operator-(const ON_ScratchBuffer&); }; class FACE_AND_FACE_POINT { public: const ON_SubDFace* m_face; double m_faceP[3]; static int CompareFacePointer(const void* a, const void* b); }; int FACE_AND_FACE_POINT::CompareFacePointer(const void* a, const void* b) { ON__UINT_PTR fa = (ON__UINT_PTR)(((const FACE_AND_FACE_POINT*)a)->m_face); ON__UINT_PTR fb = (ON__UINT_PTR)(((const FACE_AND_FACE_POINT*)b)->m_face); if (fa < fb) return -1; if (fa > fb) return 1; return 0; } bool ON_SubDSectorSurfacePoint::IsUnset() const { return (m_limitP[0] == ON_UNSET_VALUE); } bool ON_SubDSectorSurfacePoint::IsNan() const { return !(m_limitP[0] == m_limitP[0]); } bool ON_SubDSectorSurfacePoint::IsZero() const { const double* p = m_limitP; const double* p1 = p+12; while (p < p1) { if (!(0.0 == *p++)) return false; } return true; } bool ON_SubDSectorSurfacePoint::IsSet( bool bUndefinedNormalIsPossible ) const { double x, y; const double* p = m_limitP; const double* p1 = p+3; while (p < p1) { x = *p++; if (ON_UNSET_VALUE == x || !(x == x)) return false; } p = m_limitT1; p1 = p + 6; while (p < p1) { const double* p2 = p + 3; y = 0.0; while (p < p2) { x = *p++; if (ON_UNSET_VALUE == x || !(x == x)) return false; if (0.0 != x) y = x; } if (false == bUndefinedNormalIsPossible) { if (!(y != 0.0)) return false; } p = m_limitN; p1 = p + 3; y = 0.0; while (p < p1) { x = *p++; if (ON_UNSET_VALUE == x || !(x == x)) return false; y += x * x; } if (false == bUndefinedNormalIsPossible) { if (!(fabs(y - 1.0) <= 1e-4)) return false; } } return true; } void ON_SubDVertex::CopyFrom( const ON_SubDVertex* src, bool bCopyEdgeArray, bool bCopyFaceArray, bool bCopyLimitPointList, bool bCopySymmetrySetNext ) { if (nullptr == src) src = &ON_SubDVertex::Empty; ClearSavedSubdivisionPoints(); CopyBaseFrom(src, bCopySymmetrySetNext); m_vertex_tag = src->m_vertex_tag; m_P[0] = src->m_P[0]; m_P[1] = src->m_P[1]; m_P[2] = src->m_P[2]; if (bCopyLimitPointList) { if ( src->SurfacePointIsSet() ) { for (const ON_SubDSectorSurfacePoint* p = &src->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point) { ON_SubDSectorSurfacePoint limit_point = *p; limit_point.m_next_sector_limit_point = (ON_SubDSectorSurfacePoint*)1; // disable checks SetSavedSurfacePoint( true, limit_point); } } } if (bCopyEdgeArray) { if (src->m_edge_count > 0 && nullptr != src->m_edges && nullptr != m_edges && src->m_edge_count <= m_edge_capacity) { m_edge_count = src->m_edge_count; const unsigned int edge_count = src->m_edge_count; for (unsigned int vei = 0; vei < edge_count; vei++) m_edges[vei] = src->m_edges[vei]; } else m_edge_count = 0; } if (bCopyFaceArray) { if (src->m_face_count > 0 && nullptr != src->m_faces && nullptr != m_faces && src->m_face_count <= m_face_capacity) { m_face_count = src->m_face_count; const unsigned int face_count = src->m_face_count; for (unsigned int vfi = 0; vfi < face_count; vfi++) m_faces[vfi] = src->m_faces[vfi]; } else m_face_count = 0; } } static bool ON_SubDVertex_GetSubdivisionPointError( const class ON_SubDVertex* vertex, double vertex_point[3], const double* vertexP, bool bDamagedState ) { if (nullptr == vertex || nullptr == vertex_point) return false; // caller passed a null pointer - vertex is not necessarily damaged ON_SubDIncrementErrorCount(); vertex->m_status.SetDamagedState(bDamagedState); vertex->ClearSavedSubdivisionPoints(); if (nullptr != vertexP) { vertex_point[0] = vertexP[0]; vertex_point[1] = vertexP[1]; vertex_point[2] = vertexP[2]; } return true; } bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( const class ON_SubDVertex* vertex, double vertex_point[3] ) { if (nullptr != vertex_point) { vertex_point[0] = ON_DBL_QNAN; vertex_point[1] = ON_DBL_QNAN; vertex_point[2] = ON_DBL_QNAN; } if (nullptr == vertex) { ON_SUBD_ERROR("input vertex is nullptr."); return false; } const unsigned int n = vertex->m_face_count; if (nullptr == vertex || nullptr == vertex->m_faces || nullptr == vertex->m_edges || vertex->m_face_count != vertex->m_edge_count || n < ON_SubDSectorType::MinimumSectorFaceCount(ON_SubDVertexTag::Smooth) ) { ON_SUBD_ERROR("input vertex is not valid."); return false; } const double* vertexP = vertex->m_P; // It is critical to use the centroids of the neighboring faces // in this step because the number of edges in each face's // boundary may not be constant. double facePsum[3] = { 0 }; const ON_SubDFace*const* vertex_faces = vertex->m_faces; for (unsigned int i = 0; i < n; i++) { const ON_SubDFace* face = vertex_faces[i]; if (nullptr != face) { double faceC[3]; if (face->GetSubdivisionPoint( faceC)) { facePsum[0] += faceC[0]; facePsum[1] += faceC[1]; facePsum[2] += faceC[2]; continue; } } // treat missing or damaged face as infinitesimally small facePsum[0] += vertexP[0]; facePsum[1] += vertexP[1]; facePsum[2] += vertexP[2]; } double edgePsum[3] = { 0 }; class ON_SubDEdgePtr* edges = vertex->m_edges; for (unsigned int i = 0; i < n; i++) { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges[i].m_ptr); if (nullptr != edge) { const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(vertex); if (nullptr != edge_vertex) { const double* edgeP = edge_vertex->m_P; edgePsum[0] += edgeP[0]; edgePsum[1] += edgeP[1]; edgePsum[2] += edgeP[2]; continue; } } // treat missing or damaged edge as infinitesimally small edgePsum[0] += vertexP[0]; edgePsum[1] += vertexP[1]; edgePsum[2] += vertexP[2]; } const double v_weight = 1.0 - 2.0 / ((double)n); const double ef_weight = 1.0 / ((double)(n*n)); vertex_point[0] = v_weight*vertexP[0] + ef_weight*(edgePsum[0] + facePsum[0]); vertex_point[1] = v_weight*vertexP[1] + ef_weight*(edgePsum[1] + facePsum[1]); vertex_point[2] = v_weight*vertexP[2] + ef_weight*(edgePsum[2] + facePsum[2]); return true; } bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( const class ON_SubDVertex* vertex, double vertex_point[3] ) { // This function is used to convert an arbitrary control polygon into the // "level 1" quad subD. It cannot use the faster sub-D formulas because // a face can have an arbitrary number of edges. if (nullptr == vertex || nullptr == vertex_point) return ON_SubDVertex_GetSubdivisionPointError(vertex,vertex_point,nullptr,false); const double* vertexP = vertex->m_P; const unsigned int n = (nullptr != vertex->m_edges ? vertex->m_edge_count : 0); 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) return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); double facePsum[3] = { 0 }; const ON_SubDFace*const* vertex_faces = vertex->m_faces; const ON_SubDFace* face = vertex_faces[0]; if (nullptr == face) return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); ////// for debugging code below, uncomment this line ////// and look for differences in results. ////return GetGeneralQuadSubdivisionPoint(vertex, vertex_point); const unsigned int k = (nullptr == face) ? 0U : face->m_edge_count; if (4 == k) { // possibly (probably?) every face is a quad double sum[3]; for (unsigned int i = 0; i < n; i++) { const ON_SubDFace* vface = vertex_faces[i]; const unsigned int face_n = ON_SubDVertex::Internal_GetFacePointSum(vface, vertex, sum); if (4 != face_n) { // The first face is a quadrangle and this face is not a quadrangle. // // It is critical to use the centroids of the neighboring faces // for this vertex subdivision point because the number of edges // in each face's boundary is not constant. return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point); } facePsum[0] += sum[0]; facePsum[1] += sum[1]; facePsum[2] += sum[2]; } } else if (3 == k) { // possibly (probably?) every face is a triangle for (unsigned int i = 0; i < n; i++) { const ON_SubDFace* vface = vertex_faces[i]; if (k != ((nullptr == vface) ? 0U : vface->m_edge_count)) { // The first face is a triangle and this face is not a triangle. // // It is critical to use the centroids of the neighboring faces // for this vertex subdivision point because the number of edges // in each face's boundary is not constant. return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point); } } } else { // The first face has 5 or more edges. // It is likely this is the initial subdivision being applied // to the level zero SubD control polygon. // // It may be critical to use the centroids of the neighboring faces // for this vertex subdivision point because the number of edges // in each face's boundary may not constant. In any case, this // situation is not common and typically happens only on the // first subdivision step. return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point); } double edgePsum[3] = { 0 }; class ON_SubDEdgePtr* edges = vertex->m_edges; for (unsigned int i = 0; i < n; i++) { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges[i].m_ptr); if (nullptr != edge) { const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(vertex); if (nullptr != edge_vertex) { const double* edgeP = edge_vertex->m_P; edgePsum[0] += edgeP[0]; edgePsum[1] += edgeP[1]; edgePsum[2] += edgeP[2]; continue; } } // treat missing or damaged edge as infinitesimally small edgePsum[0] += vertexP[0]; edgePsum[1] += vertexP[1]; edgePsum[2] += vertexP[2]; } if (4 == k) { // all faces were quads const double v_weight = 1.0 - 1.75 / ((double)n); const double e_weight = 1.5 / ((double)(n*n)); const double f_weight = 0.25 / ((double)(n*n)); vertex_point[0] = v_weight*vertexP[0] + e_weight*edgePsum[0] + f_weight*facePsum[0]; vertex_point[1] = v_weight*vertexP[1] + e_weight*edgePsum[1] + f_weight*facePsum[1]; vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2] + f_weight*facePsum[2]; } else { // all faces were triangles const double v_weight = 1.0 - 5.0 / ((double)(3 * n)); const double e_weight = 5.0 / ((double)(3*n*n)); vertex_point[0] = v_weight*vertexP[0] + e_weight*edgePsum[0]; vertex_point[1] = v_weight*vertexP[1] + e_weight*edgePsum[1]; vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2]; } return true; } // vertex->m_vertex_tag is damaged return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); } const ON_3dPoint ON_SubDVertex::SubdivisionPoint() const { ON_3dPoint S; return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint; } bool ON_SubDVertex::GetSubdivisionPoint( double subdivision_point[3] ) const { if (nullptr == subdivision_point) return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, nullptr, false); if (GetSavedSubdivisionPoint(subdivision_point)) return true; if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point)) { SetSavedSubdivisionPoint(subdivision_point); return true; } return false; } bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const { // This function is used to convert an arbitrary control polygon into the // "level 1" subD. It cannot use the faster sub-D formulas because // a face can have an arbitrary number of edges. if (nullptr == subdivision_point ) return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, nullptr, false); const double* vertexP = m_P; const unsigned int n = (nullptr != m_edges ? m_edge_count : 0); if (n < 2) return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); if (ON_SubDVertexTag::Smooth == m_vertex_tag || ON_SubDVertexTag::Dart == m_vertex_tag) { return ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint(this, subdivision_point); } if (ON_SubDVertexTag::Crease == m_vertex_tag) { class ON_SubDEdgePtr* edges = m_edges; const ON_SubDVertex* edge0_vertex = nullptr; for (unsigned int i = 0; i < n; i++) { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(edges[i].m_ptr); if (nullptr == edge) { ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); continue; } if (ON_SubDEdgeTag::Crease != edge->m_edge_tag) continue; const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(this); if (nullptr == edge_vertex) { ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); continue; } if (nullptr == edge0_vertex) { edge0_vertex = edge_vertex; continue; } if (edge0_vertex == edge_vertex) { ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); continue; } // We found the two crease edges that share this crease vertex. // (The parenthesis around the edgeP sum is to insure this result // is independent of the order of the edges.) vertexP = m_P; const double* edgeP[2] = { edge0_vertex->m_P, edge_vertex->m_P }; subdivision_point[0] = (vertexP[0] * 6.0 + (edgeP[0][0] + edgeP[1][0]))*0.125; subdivision_point[1] = (vertexP[1] * 6.0 + (edgeP[0][1] + edgeP[1][1]))*0.125; subdivision_point[2] = (vertexP[2] * 6.0 + (edgeP[0][2] + edgeP[1][2]))*0.125; return true; } return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); } if (ON_SubDVertexTag::Corner == m_vertex_tag) { vertexP = m_P; subdivision_point[0] = vertexP[0]; subdivision_point[1] = vertexP[1]; subdivision_point[2] = vertexP[2]; return true; } // vertex is damaged return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); } unsigned int ON_SubDVertex::Internal_GetFacePointSum( const ON_SubDFace* face, const ON_SubDVertex* vertex, double* facePsum ) { const ON_SubDEdge* e; ON__UINT_PTR e_ptr, edir; const double* faceP; if (nullptr == face) return 0; const unsigned int n = face->m_edge_count; facePsum[0] = 0.0; facePsum[1] = 0.0; facePsum[2] = 0.0; if (3 == n) { return n; } if (4 == n) { for (unsigned int i = 0; i < 4; i++) { e_ptr = face->m_edge4[i].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); if (nullptr != e && (vertex == e->m_vertex[0] || vertex == e->m_vertex[1])) { edir = ON_SUBD_EDGE_DIRECTION(e_ptr); e_ptr = face->m_edge4[(i + ((vertex == e->m_vertex[edir]) ? 2 : 3)) % 4].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); edir = ON_SUBD_EDGE_DIRECTION(e_ptr); if (nullptr == e || nullptr == e->m_vertex[edir]) return 0; faceP = e->m_vertex[edir]->m_P; facePsum[0] = faceP[0]; facePsum[1] = faceP[1]; facePsum[2] = faceP[2]; return n; } } return 0; } if (n <= 4 || nullptr == face->m_edgex) return 0; e_ptr = face->m_edgex[n-5].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); if (nullptr == e) return 0; edir = ON_SUBD_EDGE_DIRECTION(e_ptr); unsigned int skipped_edge_count = (vertex == e->m_vertex[edir]) ? 1 : 0; unsigned int facePcount = 0; const ON_SubDEdgePtr* edge_ptrs = face->m_edge4; for (unsigned int i = skipped_edge_count; i < n; i++) { if (4 == i) edge_ptrs = face->m_edgex - 4; e_ptr = edge_ptrs[i].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); if (nullptr == e) return 0; edir = ON_SUBD_EDGE_DIRECTION(e_ptr); if (vertex == e->m_vertex[0] || vertex == e->m_vertex[1]) { skipped_edge_count++; if (skipped_edge_count > 2) { facePsum[0] = 0.0; facePsum[1] = 0.0; facePsum[2] = 0.0; return 0; } if (vertex == e->m_vertex[edir]) { i++; if (4 == i) edge_ptrs = face->m_edgex - 4; } continue; } faceP = e->m_vertex[edir]->m_P; facePsum[0] += faceP[0]; facePsum[1] += faceP[1]; facePsum[2] += faceP[2]; facePcount++; } if (n == facePcount + 3) return n; facePsum[0] = 0.0; facePsum[1] = 0.0; facePsum[2] = 0.0; return 0; } bool ON_SubDimple::LocalSubdivide( ON_SubDFace const*const* face_list, size_t face_list_count ) { if (nullptr == face_list || face_list_count < 1 || m_levels.UnsignedCount() < 1) return false; unsigned int level0_index = ON_UNSET_UINT_INDEX; for (size_t i = 0; i < face_list_count; ++i) { const ON_SubDFace* f = face_list[i]; if (nullptr == f || f->m_edge_count < 3 || f->SubdivisionLevel() >= m_levels.UnsignedCount()) continue; level0_index = f->SubdivisionLevel(); break; } if (level0_index >= m_levels.UnsignedCount() || nullptr == m_levels[level0_index]) return ON_SUBD_RETURN_ERROR(false); ClearHigherSubdivisionLevels(level0_index + 1); if (level0_index + 1 != m_levels.UnsignedCount()) return ON_SUBD_RETURN_ERROR(false); m_active_level = m_levels[level0_index]; if ( nullptr == m_active_level || 0 == m_active_level->m_face_count) return ON_SUBD_RETURN_ERROR(false); ON_SubDLevel& level0 = *m_levels[level0_index]; m_active_level = &level0; level0.ClearRuntimeMarks(true, true, true); unsigned face_count = 0; unsigned edge_count = 0; unsigned vertex_count = 0; ON_3dPoint P; for (size_t i = 0; i < face_list_count; ++i) { const ON_SubDFace* f = face_list[i]; if (nullptr == f || f->m_edge_count < 3 || level0_index != f->SubdivisionLevel() ) continue; if (f->m_status.RuntimeMark()) continue; f->m_status.SetRuntimeMark(); if ( false == f->GetSubdivisionPoint(P)) return ON_SUBD_RETURN_ERROR(false); 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) return ON_SUBD_RETURN_ERROR(false); } ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) return ON_SUBD_RETURN_ERROR(false); if (e->m_status.RuntimeMark()) continue; e->UpdateEdgeSectorCoefficientsForExperts(true); e->m_status.SetRuntimeMark(); if (false == e->GetSubdivisionPoint(P)) return ON_SUBD_RETURN_ERROR(false); for (unsigned evi = 0; evi < 2; ++evi) { if (e->m_vertex[evi]->m_status.RuntimeMark()) continue; e->m_vertex[evi]->m_status.SetRuntimeMark(); if ( false == e->m_vertex[evi]->GetSubdivisionPoint(P) ) return ON_SUBD_RETURN_ERROR(false); ++vertex_count; } ++edge_count; } ++face_count; } if (face_count < 1 || edge_count < 3 || vertex_count < 3) return false; if (face_count >= level0.m_face_count) return GlobalSubdivide(); // Get face subdivision points ON_SimpleArray faces(face_count); ON_SimpleArray face_points(face_count); // this subd is being modifed. ChangeGeometryContentSerialNumber( false); for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) { if (false == f0->m_status.RuntimeMark()) continue; faces.Append(const_cast(f0)); P = f0->SubdivisionPoint(); if ( false == P.IsValid()) return ON_SUBD_RETURN_ERROR(false); face_points.Append(P); } if (face_count != face_points.UnsignedCount()) return ON_SUBD_RETURN_ERROR(false); // Get edge subdivision points ON_SimpleArray edge_points(edge_count); ON_SimpleArray edges(edge_count); for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge) { if (false == e0->m_status.RuntimeMark()) continue; P = (e0->IsSmooth() && 2 == e0->m_face_count && 2 == e0->MarkedFaceCount()) ? e0->SubdivisionPoint() : e0->ControlNetCenterPoint(); edge_points.Append(P); edges.Append(const_cast(e0)); } if ( edge_count != edge_points.UnsignedCount()) return ON_SUBD_RETURN_ERROR(false); // Set vertex points for (const ON_SubDVertex* v0 = level0.m_vertex[0]; nullptr != v0; v0 = v0->m_next_vertex) { if (false == v0->m_status.RuntimeMark() || v0->m_edge_count < 2 || ((unsigned)v0->m_edge_count) != v0->MarkedEdgeCount()) continue; P = v0->SubdivisionPoint(); ON_SubDVertex* v = const_cast(v0); v->m_P[0] = P.x; v->m_P[1] = P.y; v->m_P[2] = P.z; } // split edges for (unsigned edex = 0; edex < edge_count; ++edex) { ON_SubDEdge* e = edges[edex]; e->EdgeModifiedNofification(); const ON_SubDEdge* new_edge = SplitEdge(e, edge_points[edex]); if (nullptr == new_edge) return ON_SUBD_RETURN_ERROR(false); new_edge->m_status.ClearRuntimeMark(); e->m_status.SetRuntimeMark(); } ON_SimpleArray fbdry(32); ON_SimpleArray radial_edges(32); for (unsigned fdex = 0; fdex < face_count; ++fdex) { ON_SubDFace* f = faces[fdex]; f->FaceModifiedNofification(); P = face_points[fdex]; fbdry.SetCount(0); const unsigned e_count = f->GetEdgeArray(fbdry); if (e_count < 6 || 0 != e_count %2) return ON_SUBD_RETURN_ERROR(false); // Get edges[] with edge[0] = marked edges. const ON_SubDEdgePtr* eptrs = fbdry.Array(); if (nullptr != eptrs[0].RelativeVertex(0) && false == eptrs[0].RelativeVertex(0)->Mark() ) { fbdry.Append(eptrs[0]); eptrs = fbdry.Array() + 1; } // save face status and candidate_face unsigned int candidate_face_id = f->m_id; const ON_ComponentStatus fstatus = f->m_status; for (unsigned fei = 0; fei < e_count; ++fei) { ON_SubDEdge* e = eptrs[fei].Edge(); if ( nullptr == e) return ON_SUBD_RETURN_ERROR(false); const ON_SubDVertex* ev[2] = { eptrs[fei].RelativeVertex(0), eptrs[fei].RelativeVertex(1) }; if ( nullptr == ev[0] || nullptr == ev[1] || ev[0] == ev[1]) return ON_SUBD_RETURN_ERROR(false); if (0 == fei % 2) { if (false == ev[0]->Mark()) return ON_SUBD_RETURN_ERROR(false); if (ev[1]->Mark()) return ON_SUBD_RETURN_ERROR(false); } else { if (ev[0]->Mark()) return ON_SUBD_RETURN_ERROR(false); if (false == ev[1]->Mark()) return ON_SUBD_RETURN_ERROR(false); } } // remove face that will be subdivided; for (unsigned short fei = 0; fei < f->m_edge_count; ++fei) { eptrs[fei].Edge()->RemoveFaceFromArray(f); const_cast(eptrs[fei].RelativeVertex(0))->RemoveFaceFromArray(f); } ReturnFace(f); 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_SubDEdgeTag::Smooth, center, ON_SubDSectorType::UnsetSectorCoefficient, const_cast(eptrs[fei].RelativeVertex(1)), ON_SubDSectorType::UnsetSectorCoefficient); radial_edges.Append(r); } ON_SubDEdge* r[2] = { nullptr,radial_edges[e_count / 2 - 1] }; for (unsigned fei = 0; fei < e_count; fei += 2) { r[0] = r[1]; r[1] = radial_edges[fei / 2]; const ON_SubDEdgePtr qbdry[4] = { ON_SubDEdgePtr::Create(r[0],0), eptrs[(fei + e_count - 1) % e_count], eptrs[fei], ON_SubDEdgePtr::Create(r[1],1) }; ON_SubDFace* q = AddFace(candidate_face_id, 4, qbdry); candidate_face_id = 0; q->m_status = fstatus; } } level0.ClearRuntimeMarks(true, true, false); level0.ClearBoundingBox(); level0.ClearEvaluationCache(); level0.ClearEdgeFlags(); if (nullptr != m_active_level) m_active_level->UpdateAllTagsAndSectorCoefficients(true); return true; } unsigned int ON_SubDimple::GlobalSubdivide() { if (m_levels.UnsignedCount() <= 0) return ON_SUBD_RETURN_ERROR(0U); const unsigned int level0_index = m_levels.UnsignedCount()-1; if ( nullptr == m_levels[level0_index]) return ON_SUBD_RETURN_ERROR(0U); ON_SubDLevel& level0 = *m_levels[level0_index]; if (level0.IsEmpty()) return ON_SUBD_RETURN_ERROR(0U); if ( level0.m_edge_count <= 0U ) return ON_SUBD_RETURN_ERROR(0U); level0.UpdateEdgeSectorCoefficients(true); const unsigned int level1_index = level0_index+1; ON_SubDLevel* level1 = SubDLevel(level1_index,true); if ( nullptr == level1 ) return ON_SUBD_RETURN_ERROR(0); double P[3]; ON_SubDVertex* v; // If the object is currently symmetric, a global subdivision will not break symmetry const bool bChangePreservesSymmetry = true; this->ChangeGeometryContentSerialNumber(bChangePreservesSymmetry); // Add face points for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) { if (false == f0->GetSubdivisionPoint(P)) continue; if (nullptr == f0->m_subd_point1) { const_cast(f0)->m_subd_point1 = v = AllocateVertex(ON_SubDVertexTag::Smooth, level1_index, P); AddVertexToLevel(v); } else { v = const_cast(f0->m_subd_point1); v->m_P[0] = P[0]; v->m_P[1] = P[1]; v->m_P[2] = P[2]; } } // Add edge points for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge) { if (false == e0->GetSubdivisionPoint(P)) continue; // (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 ); AddVertexToLevel(v); } else { v = const_cast(e0->m_subd_point1); v->m_vertex_tag = vertex_tag; v->m_P[0] = P[0]; v->m_P[1] = P[1]; v->m_P[2] = P[2]; } } // Add vertex points for (const ON_SubDVertex* v0 = level0.m_vertex[0]; nullptr != v0; v0 = v0->m_next_vertex) { if (false == v0->GetSubdivisionPoint(P)) continue; if (nullptr == v0->m_subd_point1) { const_cast(v0)->m_subd_point1 = v = AllocateVertex(v0->m_vertex_tag, level1_index, P); AddVertexToLevel(v); } else { v = const_cast(v0->m_subd_point1); v->m_vertex_tag = v0->m_vertex_tag; v->m_P[0] = P[0]; v->m_P[1] = P[1]; v->m_P[2] = P[2]; } } // subdivide edges for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge) { if (nullptr == e0->m_subd_point1) continue; 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_SubDEdgeTag edge_tag = e0->m_edge_tag; if (ON_SubDEdgeTag::SmoothX == edge_tag && 2 == e0->m_face_count) { 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]); } for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) { Internal_GlobalQuadSubdivideFace(f0); } return level1_index; } unsigned int ON_SubDimple::Internal_GlobalQuadSubdivideFace( const ON_SubDFace* f0 ) { // This is a private member function. // The caller insures f0 != nullptr. const unsigned int f0_edge_count = f0->m_edge_count; if (f0_edge_count < 3) return 0; const int material_channel_index = f0->MaterialChannelIndex(); const ON_Color per_face_color = f0->PerFaceColor(); const unsigned int level_zero_face_id = (0 == f0->SubdivisionLevel()) ? f0->m_id : f0->m_level_zero_face_id; if (nullptr == f0->m_subd_point1) { // add face centroid double faceC[3]; if (false == f0->GetSubdivisionPoint( faceC)) return 0; f0->SetSavedSubdivisionPoint(faceC); unsigned int level1_index = f0->SubdivisionLevel() + 1; ON_SubDVertex* v = AllocateVertex(ON_SubDVertexTag::Smooth, level1_index, faceC ); AddVertexToLevel(v); const_cast(f0)->m_subd_point1 = v; } ON_SubDEdge* E0[2]; ON__UINT_PTR E0dir[2]; ON_SubDEdge* E1[4]; ON__UINT_PTR E1dir[4]; ON_SubDEdgePtr f1edges[4]; ON__UINT_PTR e_ptr; ON_SubDEdge* FirstE1(nullptr); double w; e_ptr = f0->EdgePtr(f0_edge_count - 1).m_ptr; E0[1] = ON_SUBD_EDGE_POINTER(e_ptr); E0dir[1] = ON_SUBD_EDGE_DIRECTION(e_ptr); E1[2] = nullptr; unsigned int f1_count = 0; const double w_2facesector = ON_SubDSectorType::CreaseSectorCoefficient(2); for (unsigned int i = 0; i < f0_edge_count; i++) { E0[0] = E0[1]; E0dir[0] = E0dir[1]; e_ptr = f0->EdgePtr(i).m_ptr; E0[1] = ON_SUBD_EDGE_POINTER(e_ptr); E0dir[1] = ON_SUBD_EDGE_DIRECTION(e_ptr); if (nullptr == E0[0] || nullptr == E0[1]) continue; if (nullptr == E0[0]->m_subd_point1 || nullptr == E0[1]->m_subd_point1) continue; e_ptr = E0[0]->m_subd_point1->m_edges[0 == E0dir[0] ? 1 : 0].m_ptr; E1[0] = ON_SUBD_EDGE_POINTER(e_ptr); E1dir[0] = E0dir[0]; e_ptr = E0[1]->m_subd_point1->m_edges[0 == E0dir[1] ? 0 : 1].m_ptr; E1[1] = ON_SUBD_EDGE_POINTER(e_ptr); E1dir[1] = E0dir[1]; E1[3] = E1[2]; if (nullptr == E1[3]) { // The value of E0[0]->m_subd_point1->m_vertex_tag should be either // 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_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]; } E1dir[3] = 0; if (i + 1 < f0_edge_count || nullptr == FirstE1) { // The value of E0[0]->m_subd_point1->m_vertex_tag should be either // 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_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 { E1[2] = FirstE1; } E1dir[2] = 1; f1edges[0] = ON_SubDEdgePtr::Create(E1[0], E1dir[0]); f1edges[1] = ON_SubDEdgePtr::Create(E1[1], E1dir[1]); f1edges[2] = ON_SubDEdgePtr::Create(E1[2], E1dir[2]); f1edges[3] = ON_SubDEdgePtr::Create(E1[3], E1dir[3]); ON_SubDFace* f1 = AddFace(4, f1edges); if (nullptr != f1) { f1->SetMaterialChannelIndex(material_channel_index); f1->SetPerFaceColor(per_face_color); f1->m_level_zero_face_id = level_zero_face_id; f1_count++; } } // return number of new faces return f1_count; } bool ON_SubD::GlobalSubdivide() { return GlobalSubdivide(1U); } bool ON_SubD::GlobalSubdivide( unsigned int count ) { ON_SubDimple* subdimple = SubDimple(false); if (nullptr == subdimple) return ON_SUBD_RETURN_ERROR(false); return subdimple->GlobalSubdivide(count); } bool ON_SubD::LocalSubdivide( const ON_SimpleArray& face_list ) { const int count = face_list.Count(); ON_SimpleArray< const ON_SubDFace* > ptr_list(count); for (int i = 0; i < count; ++i) { const ON_COMPONENT_INDEX ci = face_list[i]; if (ON_COMPONENT_INDEX::TYPE::subd_face != ci.m_type) continue; if (ci.m_index <= 0) continue; const ON_SubDFace* f = this->FaceFromId(ci.m_index); if (nullptr == f) continue; ptr_list.Append(f); } const bool rc = LocalSubdivide(ptr_list); return rc; } bool ON_SubD::LocalSubdivide(const ON_SimpleArray< const ON_SubDFace* >& face_list) { return LocalSubdivide(face_list.Array(), face_list.UnsignedCount()); } bool ON_SubD::LocalSubdivide( ON_SubDFace const*const* face_list, size_t face_count) { ON_SubDimple* subdimple = SubDimple(false); if (nullptr == subdimple) return ON_SUBD_RETURN_ERROR(false); return subdimple->LocalSubdivide(face_list, face_count); } bool ON_SubDimple::GlobalSubdivide( unsigned int count ) { if (m_levels.UnsignedCount() < 1) return ON_SUBD_RETURN_ERROR(false); if (nullptr == m_active_level) { m_active_level = m_levels[m_levels.UnsignedCount() - 1]; if (nullptr == m_active_level) return ON_SUBD_RETURN_ERROR(false); } const unsigned level0_index = m_active_level->m_level_index; if (level0_index >= m_levels.UnsignedCount() || nullptr == m_levels[level0_index]) return ON_SUBD_RETURN_ERROR(false); if (count <= 0) return ON_SUBD_RETURN_ERROR(false); if (level0_index + count > ON_SubD::maximum_subd_level) return ON_SUBD_RETURN_ERROR(false); ClearHigherSubdivisionLevels(level0_index + 1); if (level0_index + 1 != m_levels.UnsignedCount() ) return ON_SUBD_RETURN_ERROR(false); m_active_level = m_levels[level0_index]; for (unsigned int i = level0_index +1; i <= level0_index +count; i++) { unsigned int rc = GlobalSubdivide(); if (i != rc) return ON_SUBD_RETURN_ERROR(false); m_active_level = m_levels[i]; } return true; } ON_SubDEdgePtr ON_SubDimple::MergeConsecutiveEdges( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ) { if ( false == ON_SubD::EdgesAreConsecutive(eptr0,eptr1) ) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); ON_SubDEdge* e[2] = { ON_SUBD_EDGE_POINTER(eptr0.m_ptr), ON_SUBD_EDGE_POINTER(eptr1.m_ptr) }; ON__UINT_PTR edir[2] = { ON_SUBD_EDGE_DIRECTION(eptr0.m_ptr), ON_SUBD_EDGE_DIRECTION(eptr1.m_ptr) }; const ON_SubDVertex* end_v[2] = {e[0]->m_vertex[edir[0]], e[1]->m_vertex[1 - edir[1]]}; if (nullptr == end_v[0] || nullptr == end_v[1] || end_v[0] == end_v[1] ) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); ON_SubDEdgeTag merged_edge_tag = (e[0]->IsSmooth() || e[1]->IsSmooth()) ? ON_SubDEdgeTag::Smooth : ON_SubDEdgeTag::Crease; for (unsigned int j = 0; j < e[1]->m_face_count; j++) { ON_SubDFace* f = const_cast(e[1]->Face(j)); if (nullptr == f) continue; f->RemoveEdgeFromArray(e[1]); } // remove middle vertex ON_SubDVertex* middle_v = const_cast< ON_SubDVertex* >(e[1]->m_vertex[edir[1]]); if (nullptr != middle_v && middle_v != end_v[0] && middle_v != end_v[1] ) { if (middle_v->m_edge_count > 0 && nullptr != middle_v->m_edges) { unsigned int vei0 = middle_v->EdgeArrayIndex(e[0]); unsigned int vei1 = middle_v->EdgeArrayIndex(e[1]); unsigned int vei_count = middle_v->m_edge_count; middle_v->m_edge_count = 0; for (unsigned int vei = 0; vei < vei_count; vei++) { if ( vei == vei0 || vei == vei1 ) continue; // happens when middle_v is a multiple sector corner, non-manifold, or other rare cases if (vei > middle_v->m_edge_count) middle_v->m_edges[middle_v->m_edge_count] = middle_v->m_edges[vei]; middle_v->m_edge_count++; } } if (0 == middle_v->m_edge_count || nullptr == middle_v->m_edges) { ReturnVertex(middle_v); middle_v = nullptr; } } e[0]->m_vertex[1-edir[0]] = nullptr; e[1]->m_vertex[edir[1]] = nullptr; e[1]->m_vertex[1-edir[1]] = nullptr; for (unsigned int i = 0; i < end_v[1]->m_edge_count; i++) { if (e[1] == ON_SUBD_EDGE_POINTER(end_v[1]->m_edges[i].m_ptr)) { const_cast< ON_SubDVertex* >(end_v[1])->m_edges[i] = ON_SubDEdgePtr::Create(e[0], 1 - edir[0]); e[0]->m_vertex[1 - edir[0]] = end_v[1]; break; } } 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_SubDEdgeTag::Smooth == merged_edge_tag || false == bTagged[0] || false == bTagged[1]) { e[0]->m_edge_tag = (bTagged[0] && bTagged[1]) ? 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)) e[0]->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; if ( false == bTagged[1]) e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; else if (!(e[0]->m_sector_coefficient[1] > 0.0 && e[0]->m_sector_coefficient[1] < 1.0)) e[0]->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; } else { 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; } ReturnEdge(e[1]); return eptr0; } ON_SubDEdgePtr ON_SubD::MergeConsecutiveEdges( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ) { ON_SubDimple* subdimple = SubDimple(false); return (nullptr != subdimple) ? subdimple->MergeConsecutiveEdges(eptr0, eptr1) : ON_SubDEdgePtr::Null; } static bool Internal_EdgesAreConsecutive( const ON_SubDEdgePtr eptr[2], bool bTestColinearity, double distance_tolerance, double maximum_aspect, double sin_angle_tolerance ) { const ON_SubDEdge* e[2] = { ON_SUBD_EDGE_POINTER(eptr[0].m_ptr), ON_SUBD_EDGE_POINTER(eptr[1].m_ptr) }; ON__UINT_PTR edir[2] = { ON_SUBD_EDGE_DIRECTION(eptr[0].m_ptr), ON_SUBD_EDGE_DIRECTION(eptr[1].m_ptr) }; if ( nullptr == e[0] || nullptr == e[1] || e[0] == e[1] || edir[0] > 1 || edir[1] > 1 || e[0]->m_face_count != e[1]->m_face_count //|| e[0]->m_edge_tag != e[1]->m_edge_tag ) { return false; } if ( nullptr == e[1]->m_vertex[0] || nullptr == e[1]->m_vertex[1] ) { return false; } // v[0] = start // v[1] = end // v[2] = middle (will be removed) const ON_SubDVertex* v[4] = { e[0]->m_vertex[edir[0]], e[1]->m_vertex[1 - edir[1]], e[0]->m_vertex[1 - edir[0]], e[1]->m_vertex[edir[1]] }; if (nullptr == v[0] || nullptr == v[1] || nullptr == v[2] || v[0] == v[1] || v[2] != v[3]) { return false; } if (bTestColinearity) { if (ON_UNSET_UINT_INDEX == v[2]->EdgeArrayIndex(e[0]) || ON_UNSET_UINT_INDEX == v[2]->EdgeArrayIndex(e[1])) bTestColinearity = false; } // edges must have the same faces switch (e[0]->m_face_count) { case 0: break; case 1: if (ON_SUBD_FACE_POINTER(e[0]->m_face2[0].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[0].m_ptr)) break; return false; case 2: if (ON_SUBD_FACE_POINTER(e[0]->m_face2[0].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[0].m_ptr) && ON_SUBD_FACE_POINTER(e[0]->m_face2[1].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[1].m_ptr)) break; if (ON_SUBD_FACE_POINTER(e[0]->m_face2[0].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[1].m_ptr) && ON_SUBD_FACE_POINTER(e[0]->m_face2[1].m_ptr) == ON_SUBD_FACE_POINTER(e[1]->m_face2[0].m_ptr)) break; return false; default: // non-manifold edge { unsigned int j, k; for (j = 0; j < e[0]->m_face_count; j++) { const ON_SubDFace* f = e[0]->Face(j); for (k = 0; k < e[1]->m_face_count; k++) { if (f == e[1]->Face(k)) break; } if (k < e[1]->m_face_count) continue; break; } if (j != e[0]->m_face_count) return false; } break; } if (bTestColinearity) { const ON_3dPoint* P[3] = { (const ON_3dPoint*)v[0]->m_P, (const ON_3dPoint*)v[2]->m_P, (const ON_3dPoint*)v[1]->m_P }; ON_3dVector D(*P[2] - *P[0]); const double d = D.Length(); if (!(d > 0.0)) return false; const ON_3dVector V(*P[1] - *P[0]); const double t = (V*D) / (d*d); if (!(t > ON_EPSILON && t < 1.0 - ON_EPSILON)) return false; const ON_3dPoint M = (1.0 - t)*(*P[0]) + t*(*P[2]); const double h = P[1]->DistanceTo(M); if (0.0 == h) return true; if (!(h > 0.0)) return false; const double miniscule_distance_tolerance = ON_ZERO_TOLERANCE; if (h <= miniscule_distance_tolerance && !(distance_tolerance >= 0.0 && distance_tolerance < miniscule_distance_tolerance)) { // skip parameter tests for miniscule h. return true; } const double miniscule_maximum_aspect = 1e-4; if (h <= miniscule_maximum_aspect * d && !(maximum_aspect >= 0.0 && maximum_aspect < miniscule_maximum_aspect)) { // skip parameter tests for miniscule h/d. return true; } if (distance_tolerance >= 0.0 && !(h <= distance_tolerance)) return false; // failed distance to chord test if (maximum_aspect >= 0.0 && !(h <= maximum_aspect * d)) return false; // failed maximum aspect test if (sin_angle_tolerance >= 0.0 && sin_angle_tolerance < 1.0 && !(ON_CrossProduct(V, (*P[1] - *P[2])).Length() <= sin_angle_tolerance)) return false; // failed minimum angle test } return true; } bool ON_SubD::EdgesAreConsecutive( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ) { ON_SubDEdgePtr eptr[2] = { eptr0,eptr1 }; return Internal_EdgesAreConsecutive(eptr,false,ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN); } static bool Internal_EdgesPassTypeFilter( const ON_SubDEdgePtr eptr[2], bool bMergeBoundaryEdges, bool bMergeInteriorCreaseEdges, bool bMergeInteriorSmoothEdges ) { for (unsigned i = 0; i < 2; ++i) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr[i].m_ptr); if (nullptr == e || e->m_face_count < 1) return false; if (1 == e->m_face_count) { if (false == bMergeBoundaryEdges) return false; } else if (e->m_face_count >= 2) { if (false == (e->IsSmooth() ? bMergeInteriorSmoothEdges : bMergeInteriorCreaseEdges)) return false; } else return false; }; return true; } unsigned int ON_SubDimple::MergeColinearEdges( bool bMergeBoundaryEdges, bool bMergeInteriorCreaseEdges, bool bMergeInteriorSmoothEdges, double distance_tolerance, double maximum_aspect, double sin_angle_tolerance ) { if (1 != m_levels.UnsignedCount()) return false; unsigned int removed_edge_count = 0; for (const ON_SubDFace* f = ActiveLevel().m_face[0]; nullptr != f; f = f->m_next_face) { unsigned int edge_count = f->m_edge_count; if (edge_count < 3) continue; // First - find a pair of edges that should not be merged. ON_SubDEdgePtr eptr[2] = { ON_SubDEdgePtr::Null, f->EdgePtr(edge_count - 1) }; unsigned int fei0 = 0; while (fei0 < edge_count) { eptr[0] = eptr[1]; eptr[1] = f->EdgePtr(fei0); if (false == Internal_EdgesAreConsecutive(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance)) break; if (false == Internal_EdgesPassTypeFilter(eptr, bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges)) break; ++fei0; } if (fei0 >= edge_count) { // face is degenerate or corrupt. continue; } if (0 != fei0) { if (false == (const_cast(f)->RotateEdgeArray(fei0))) { // face is degenerate or corrupt. continue; } } // At this point, we know the last edge and the first edge should not me merged. eptr[1] = f->EdgePtr(0U); unsigned int fei = 1; while ( fei < edge_count) { eptr[0] = eptr[1]; eptr[1] = f->EdgePtr(fei); if ( Internal_EdgesAreConsecutive(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance) && Internal_EdgesPassTypeFilter(eptr, bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges) ) { // merge edges f->Edge(fei-1) and f->Edge(fei) into f->Edge(fei-1). if (eptr[0].m_ptr != MergeConsecutiveEdges(eptr[0], eptr[1]).m_ptr) { ON_SUBD_ERROR("Bug in consecutive edge merging."); break; } ++removed_edge_count; --edge_count; eptr[1] = eptr[0]; } else { ++fei; } } } return removed_edge_count; } unsigned int ON_SubD::MergeColinearEdges( bool bMergeBoundaryEdges, bool bMergeInteriorCreaseEdges, bool bMergeInteriorSmoothEdges, double distance_tolerance, double maximum_aspect, double sin_angle_tolerance ) { ON_SubDimple* subdimple = SubDimple(false); return (nullptr != subdimple) ? subdimple->MergeColinearEdges( bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges, distance_tolerance, maximum_aspect, sin_angle_tolerance) : 0; } unsigned int ON_SubD::LevelCount() const { const ON_SubDimple* subdimple = SubDimple(); return (0 != subdimple ? subdimple->LevelCount() : 0); } unsigned int ON_SubD::ActiveLevelIndex() const { return ActiveLevel().m_level_index; } bool ON_SubD::IsEmpty() const { return (nullptr == SubDimple()); } bool ON_SubD::IsNotEmpty() const { return (nullptr != SubDimple()); } ///////////////////////////////////////////////////////// // // Element (Vertex, Edge, Face) access // const ON_COMPONENT_INDEX ON_SubDComponentPtr::ComponentIndex() const { switch (ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: { const ON_SubDVertex* vertex = ON_SUBD_VERTEX_POINTER(m_ptr); if ( nullptr != vertex) return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_vertex,vertex->m_id); } break; case ON_SubDComponentPtr::Type::Edge: { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); if ( nullptr != edge) return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_edge,edge->m_id); } break; case ON_SubDComponentPtr::Type::Face: { const ON_SubDFace* face = ON_SUBD_FACE_POINTER(m_ptr); if ( nullptr != face) return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_face,face->m_id); } break; default: if (IsNull() ) return ON_COMPONENT_INDEX::UnsetComponentIndex; break; } return ON_SUBD_RETURN_ERROR(ON_COMPONENT_INDEX::UnsetComponentIndex); } const ON_SubDComponentPtr ON_SubD::ComponentPtrFromComponentIndex( ON_COMPONENT_INDEX component_index ) const { if (0 != component_index.m_index && -1 != component_index.m_index) { switch (component_index.m_type) { case ON_COMPONENT_INDEX::TYPE::subd_vertex: return ON_SubDComponentPtr::Create(VertexFromId(component_index.m_index)); case ON_COMPONENT_INDEX::TYPE::subd_edge: return ON_SubDComponentPtr::Create(EdgeFromId(component_index.m_index)); case ON_COMPONENT_INDEX::TYPE::subd_face: return ON_SubDComponentPtr::Create(FaceFromId(component_index.m_index)); default: break; } } else if (ON_COMPONENT_INDEX::TYPE::invalid_type == component_index.m_type) { return ON_SubDComponentPtr::Null; } return ON_SUBD_RETURN_ERROR(ON_SubDComponentPtr::Null); } ///////////////////////////////////////////////////////// // // Vertex access // unsigned int ON_SubD::VertexCount() const { return ActiveLevel().m_vertex_count; } const ON_SubDVertex* ON_SubD::FirstVertex() const { return ActiveLevel().m_vertex[0]; } const ON_SubDVertex* ON_SubD::LastVertex() const { return ActiveLevel().m_vertex[1]; } ON_SubDVertexIterator ON_SubD::VertexIterator() const { return ON_SubDVertexIterator(*this); } ON_SubDVertexIterator ON_SubDRef::VertexIterator() const { return ON_SubDVertexIterator(*this); } ON_SubDVertexArray ON_SubD::VertexArray() const { return ON_SubDVertexArray(*this); } const class ON_SubDVertex* ON_SubD::VertexFromId( unsigned int vertex_id ) const { for (;;) { if (0 == vertex_id || ON_UNSET_UINT_INDEX == vertex_id ) break; const ON_SubDimple* subdimple = SubDimple(); if (nullptr == subdimple) break; return subdimple->VertexFromId(vertex_id); } return nullptr; } const class ON_SubDVertex* ON_SubD::VertexFromComponentIndex( ON_COMPONENT_INDEX component_index ) const { return (ON_COMPONENT_INDEX::TYPE::subd_vertex == component_index.m_type) ? VertexFromId(component_index.m_index) : nullptr; } const ON_COMPONENT_INDEX ON_SubDVertex::ComponentIndex() const { return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_vertex,m_id); } const ON_SubDComponentPtr ON_SubDVertex::ComponentPtr() const { return ON_SubDComponentPtr::Create(this); } ///////////////////////////////////////////////////////// // // Edge access // unsigned int ON_SubD::EdgeCount() const { return ActiveLevel().m_edge_count; } const ON_SubDEdge* ON_SubD::FirstEdge() const { return ActiveLevel().m_edge[0]; } const ON_SubDEdge* ON_SubD::LastEdge() const { return ActiveLevel().m_edge[1]; } ON_SubDEdgeIterator ON_SubD::EdgeIterator() const { return ON_SubDEdgeIterator(*this); } ON_SubDEdgeIterator ON_SubDRef::EdgeIterator() const { return ON_SubDEdgeIterator(*this); } ON_SubDEdgeArray ON_SubD::EdgeArray() const { return ON_SubDEdgeArray(*this); } const class ON_SubDEdge* ON_SubD::EdgeFromId( unsigned int edge_id ) const { for (;;) { if (0 == edge_id || ON_UNSET_UINT_INDEX == edge_id ) break; const ON_SubDimple* subdimple = SubDimple(); if (nullptr == subdimple) break; return subdimple->EdgeFromId(edge_id); } return nullptr; } const class ON_SubDEdge* ON_SubD::EdgeFromComponentIndex( ON_COMPONENT_INDEX component_index ) const { return (ON_COMPONENT_INDEX::TYPE::subd_edge == component_index.m_type) ? EdgeFromId(component_index.m_index) : nullptr; } const ON_COMPONENT_INDEX ON_SubDEdge::ComponentIndex() const { return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_edge,m_id); } const ON_SubDComponentPtr ON_SubDEdge::ComponentPtr() const { return ON_SubDComponentPtr::Create(this); } ///////////////////////////////////////////////////////// // // Face access // unsigned int ON_SubD::FaceCount() const { return ActiveLevel().m_face_count; } const ON_SubDFace* ON_SubD::FirstFace() const { return ActiveLevel().m_face[0]; } const ON_SubDFace* ON_SubD::LastFace() const { return ActiveLevel().m_face[1]; } ON_SubDFaceIterator ON_SubD::FaceIterator() const { return ON_SubDFaceIterator(*this); } ON_SubDFaceIterator ON_SubDRef::FaceIterator() const { return ON_SubDFaceIterator(*this); } ON_SubDFaceArray ON_SubD::FaceArray() const { return ON_SubDFaceArray(*this); } const class ON_SubDFace* ON_SubD::FaceFromId( unsigned int face_id ) const { for (;;) { if (0 == face_id || ON_UNSET_UINT_INDEX == face_id ) break; const ON_SubDimple* subdimple = SubDimple(); if (nullptr == subdimple) break; return subdimple->FaceFromId(face_id); } return nullptr; } const class ON_SubDFace* ON_SubD::FaceFromComponentIndex( ON_COMPONENT_INDEX component_index ) const { return (ON_COMPONENT_INDEX::TYPE::subd_face == component_index.m_type) ? FaceFromId(component_index.m_index) : nullptr; } const ON_COMPONENT_INDEX ON_SubDFace::ComponentIndex() const { return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_face,m_id); } const ON_SubDComponentPtr ON_SubDFace::ComponentPtr() const { return ON_SubDComponentPtr::Create(this); } ///////////////////////////////////////////////////////// // // ON_SubD properties // bool ON_SubD::IsOriented() const { for (const ON_SubDEdge* edge = FirstEdge(); nullptr != edge; edge = edge->m_next_edge) { if ( 2 != edge->m_face_count ) continue; if (nullptr == ON_SUBD_FACE_POINTER(edge->m_face2[0].m_ptr) || nullptr == ON_SUBD_FACE_POINTER(edge->m_face2[1].m_ptr) ) continue; if ( ON_SUBD_FACE_DIRECTION(edge->m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(edge->m_face2[1].m_ptr) ) return false; } return true; } // reverses the orientation of all facets bool ON_SubD::ReverseOrientation() const { // Limit point normals and limit surface mesh fragments will need to be recalculated. // DestroyRuntimeCache() will clear all this information. const_cast(this)->DestroyRuntimeCache(true); for (const ON_SubDFace* face = FirstFace(); nullptr != face; face = face->m_next_face) { const_cast(face)->ReverseEdgeList(); } return true; } // Attempts to orient all facet to match the first facet. static unsigned int OrientFaceNeighbors( unsigned int recursion_level, const ON_SubDFace** face_list, unsigned int id0, const ON_SubDFace* face ) { ON_SubDFace* next_set[4]; const unsigned int next_set_capacity = (unsigned int)(sizeof(next_set) / sizeof(next_set[0])); unsigned int next_set_count = 0; const unsigned int edge_count = face->m_edge_count; const ON_SubDEdgePtr* face_eptr = face->m_edge4; const ON_SubDEdge* e; ON_SubDFace* neighbor_face; if (nullptr != face_list[face->m_id - id0]) { // search for an oriented neighbor neighbor_face = nullptr; for (unsigned int fei = 0; fei < edge_count; fei++, face_eptr++) { if (4 == fei) { face_eptr = face->m_edgex; if (nullptr == face_eptr) break; } e = ON_SUBD_EDGE_POINTER(face_eptr->m_ptr); if (nullptr == e || 2 != e->m_face_count) continue; neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[0].m_ptr); if (face == neighbor_face) neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr); else if (face != ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr)) continue; if (nullptr == neighbor_face) continue; if (nullptr == face_list[neighbor_face->m_id - id0]) return OrientFaceNeighbors(recursion_level,face_list,id0,neighbor_face); } // nothing near face is oriented. return 0; } unsigned int orient_count = 0; for (unsigned int fei = 0; fei < edge_count; fei++, face_eptr++) { if (4 == fei) { face_eptr = face->m_edgex; if ( nullptr == face_eptr) break; } e = ON_SUBD_EDGE_POINTER(face_eptr->m_ptr); if (nullptr == e || 2 != e->m_face_count) continue; neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[0].m_ptr); if (face == neighbor_face) neighbor_face = ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr); else if (face != ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr)) continue; if (nullptr == neighbor_face) continue; if (nullptr == face_list[neighbor_face->m_id - id0]) continue; if (ON_SUBD_FACE_DIRECTION(e->m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(e->m_face2[1].m_ptr)) neighbor_face->ReverseEdgeList(); face_list[neighbor_face->m_id - id0] = nullptr; orient_count++; if (recursion_level < 12) { if (next_set_count >= next_set_capacity) { for (unsigned i = 0; i < next_set_capacity; i++) orient_count += OrientFaceNeighbors(recursion_level + 1, face_list, id0, next_set[i]); next_set_count = 0; } next_set[next_set_count++] = neighbor_face; } } for ( unsigned i = 0; i < next_set_count; i++) orient_count += OrientFaceNeighbors(recursion_level+1,face_list,id0,next_set[i]); return orient_count; } bool ON_SubD::Orient() const { const ON_SubDFace* first_face = FirstFace(); if ( nullptr == first_face || nullptr == first_face->m_next_face) return true; unsigned int nonzero_face_count = 0; ON_SimpleArray< const ON_SubDFace* > faces_array(FaceCount()); unsigned int face_id0 = first_face->m_id; unsigned int face_id1 = first_face->m_id; for (const ON_SubDFace* face = FirstFace(); nullptr != face; face = face->m_next_face) { faces_array.Append(face); if (face->m_id > face_id1) face_id1 = face->m_id; else if (face->m_id < face_id0) face_id0 = face->m_id; nonzero_face_count++; } const unsigned int face_count = faces_array.UnsignedCount(); if (face_count <= 1) return true; const ON_SubDFace** faces = faces_array.Array(); if (face_id1 - face_id0 >= faces_array.UnsignedCount()) { 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; } } unsigned int orient_count = 0; unsigned int connected_region_count = 0; bool bSearchForNewComponent = true; unsigned int first_face_index = 0; for (;;) { unsigned int orient_count0 = orient_count; while (first_face_index < face_count && nullptr == faces[first_face_index]) first_face_index++; if ( first_face_index >= face_count) break; for (unsigned int i = first_face_index; i < face_count && orient_count < nonzero_face_count; i++) { const ON_SubDFace* face = faces[i]; if (nullptr == face) continue; if (bSearchForNewComponent) { // first face in new connected component orient_count++; connected_region_count++; faces[i] = nullptr; bSearchForNewComponent = false; first_face_index = i+1; } orient_count += OrientFaceNeighbors(0, faces, face_id0, face); } if ( orient_count >= nonzero_face_count) break; if (orient_count0 >= orient_count) { if (bSearchForNewComponent) break; bSearchForNewComponent = true; } } return (connected_region_count > 0 && orient_count > 0); } const ON_SubDVertex * ON_SubD::ReplaceFaceWithTriangleFan(ON_SubDFace * face, ON_3dPoint fan_center_point, bool bMarkFaces) { const unsigned edge_count = face->m_edge_count; if (edge_count < 3) return ON_SUBD_RETURN_ERROR(nullptr); ON_SubDimple* subdimple = SubDimple(false); if (nullptr == subdimple) return ON_SUBD_RETURN_ERROR(nullptr); // validate and get centroid (which may not be needed). ON_3dPoint P = ON_3dPoint::Origin; ON_SimpleArray edges(edge_count); const ON_SubDEdgePtr* eptr = face->m_edge4; for (unsigned i = 0; i < edge_count; ++i, ++eptr) { if (4U == i) { eptr = face->m_edgex; if (nullptr == eptr) return ON_SUBD_RETURN_ERROR(nullptr); } const ON_SubDVertex* v = eptr->RelativeVertex(0); if (nullptr == v) return ON_SUBD_RETURN_ERROR(nullptr); P += v->ControlNetPoint(); edges.Append(*eptr); } if (fan_center_point.IsValid()) P = fan_center_point; else P /= ((double)edge_count); 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_SubDEdgeTag::Smooth, v0, const_cast(edges[i].RelativeVertex(0)))) { ON_SubDComponentPtr cptr = ON_SubDComponentPtr::Create(v0); DeleteComponents(&cptr, 1, false); return ON_SUBD_RETURN_ERROR(nullptr); } } for (unsigned i = 0; i < edge_count; ++i, ++eptr) { if (i < 4) face->m_edge4[i] = ON_SubDEdgePtr::Null; else face->m_edgex[i - 4] = ON_SubDEdgePtr::Null;; ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(edges[i].m_ptr); e->RemoveFaceFromArray(face); const_cast(edges[i].RelativeVertex(0))->RemoveFaceFromArray(face); } subdimple->ReturnFace(face); for (unsigned i = 0; i < edge_count; ++i, ++eptr) { AddTriangleFace(v0->m_edges[i], edges[i], v0->m_edges[(i + 1) % edge_count].Reversed()); } for (unsigned i = 0; i < edge_count; ++i, ++eptr) { ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(edges[i].m_ptr); e->EdgeModifiedNofification(); e->UpdateEdgeSectorCoefficientsForExperts(false); } for (unsigned i = 0; i < edge_count; ++i, ++eptr) ON_SUBD_EDGE_POINTER(edges[i].m_ptr)->UpdateEdgeSectorCoefficientsForExperts(false); for (unsigned i = 0; i < edge_count; ++i, ++eptr) ON_SUBD_EDGE_POINTER(v0->m_edges[i].m_ptr)->UpdateEdgeSectorCoefficientsForExperts(false); return v0; } const ON_SubDEdge* ON_SubDimple::SplitEdge( ON_SubDEdge* edge, ON_3dPoint vertex_location ) { if ( nullptr == edge ) return ON_SUBD_RETURN_ERROR(nullptr); if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1] ) return ON_SUBD_RETURN_ERROR(nullptr); if (nullptr == edge->m_vertex[0]->m_edges || edge->m_vertex[0]->m_edge_count <= 0 || edge->m_vertex[0]->m_edge_capacity < edge->m_vertex[0]->m_edge_count ) return ON_SUBD_RETURN_ERROR(nullptr); if (nullptr == edge->m_vertex[1]->m_edges || edge->m_vertex[1]->m_edge_count <= 0 || edge->m_vertex[1]->m_edge_capacity < edge->m_vertex[1]->m_edge_count ) return ON_SUBD_RETURN_ERROR(nullptr); if ( vertex_location.IsUnsetOrNan() ) { ON_Line L; L.from = ON_3dPoint(edge->m_vertex[0]->m_P); L.to = ON_3dPoint(edge->m_vertex[1]->m_P); vertex_location = L.PointAt(0.5); } if ( false == vertex_location.IsValid() ) return ON_SUBD_RETURN_ERROR(nullptr); 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_SubDEdgeTag edge_tag = edge->m_edge_tag; ON_SubDVertexTag vertex_tag; switch (edge->m_edge_tag) { case ON_SubDEdgeTag::Smooth: vertex_tag = ON_SubDVertexTag::Smooth; break; case ON_SubDEdgeTag::Crease: vertex_tag = ON_SubDVertexTag::Crease; break; case ON_SubDEdgeTag::SmoothX: vertex_tag = ON_SubDVertexTag::Smooth; edge_tag = ON_SubDEdgeTag::Smooth; break; default: return ON_SUBD_RETURN_ERROR(nullptr); break; } ON_SubDVertex* end_vertex[2] = { const_cast(edge->m_vertex[0]), const_cast(edge->m_vertex[1]) }; ON_SubDVertex* new_vertex = nullptr; ON_SubDEdge* new_edge = nullptr; for (;;) { new_vertex = AllocateVertex( 0U, vertex_tag, edge->SubdivisionLevel(), static_cast(vertex_location), 2, edge->m_face_count); if (nullptr == new_vertex) break; new_edge = AllocateEdge( 0U, edge_tag, edge->SubdivisionLevel(), edge->m_face_count); if (nullptr == new_edge) break; // change end_vertex[1] edge reference from edge to new_edge bool bConnectedNewEdgeToEndVertex = false; const ON_SubDEdgePtr old_edge_reference = ON_SubDEdgePtr::Create(edge,1); for (unsigned short vei = 0; vei < end_vertex[1]->m_edge_count; vei++) { if (old_edge_reference.m_ptr == end_vertex[1]->m_edges[vei].m_ptr) { // change end_vertex[1]->m_edges[vei] reference // from "edge" to "new_edge". bConnectedNewEdgeToEndVertex = true; end_vertex[1]->m_edges[vei] = ON_SubDEdgePtr::Create(new_edge,1); break; } } if (false == bConnectedNewEdgeToEndVertex) { // end_vertex[1]->m_edges[] does not reference edge break; } // finish setting new_vertex and end_vertex[] <-> new_edge connections new_edge->m_vertex[0] = new_vertex; new_edge->m_vertex[1] = end_vertex[1]; new_vertex->m_edges[new_vertex->m_edge_count++] = ON_SubDEdgePtr::Create(new_edge,0); // finish setting new_vertex <-> input edge and connections edge->m_edge_tag = edge_tag; // changes "X" to "Smooth" if required edge->m_vertex[1] = new_vertex; new_vertex->m_edges[new_vertex->m_edge_count++] = ON_SubDEdgePtr::Create(edge,1); // add new_vertex and new_edge <-> edge->m_face[] connections. const ON_SubDFacePtr* fptr0 = edge->m_face2; ON_SubDFacePtr* fptr1 = new_edge->m_face2; for (unsigned short efi = 0; efi < edge->m_face_count; efi++) { if (2 == efi) { fptr0 = edge->m_facex; fptr1 = new_edge->m_facex; } ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr0->m_ptr); if (nullptr != face) { face->FaceModifiedNofification(); // add new_vertex reference to face new_vertex->m_faces[new_vertex->m_face_count++] = face; // insert new_edge into face->m_edge[] list after "edge" if (GrowFaceEdgeArray(face, face->m_edge_count + 1)) { if ( face->m_edge_count < 4 ) face->m_edge4[face->m_edge_count] = ON_SubDEdgePtr::Null; else face->m_edgex[face->m_edge_count-4] = ON_SubDEdgePtr::Null; face->m_edge_count++; ON_SubDEdgePtr* face_edge = face->m_edge4; for (unsigned short fei = 0; fei < face->m_edge_count; fei++) { if (4 == fei) face_edge = face->m_edgex; if (edge == ON_SUBD_EDGE_POINTER(face_edge->m_ptr)) { ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(face_edge->m_ptr); ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Create(new_edge,edir); if (0 == edir) { fei++; face_edge++; } for (/*empty init*/; fei < face->m_edge_count; fei++) { if (4 == fei) face_edge = face->m_edgex; const ON_SubDEdgePtr tmp = *face_edge; *face_edge = eptr; eptr = tmp; face_edge++; } break; } face_edge++; } } } *fptr1++ = *fptr0++; new_edge->m_face_count++; } // original ending vertex new_edge->m_sector_coefficient[1] = edge->m_sector_coefficient[1]; // Either edge was a crease, new_edge is a crease, and sector weights do not applie // or edge was X or Smooth, edge is smooth, new_edge is smooth, new_vertex is smooth, // and the sector weights at this vertex do not apply. edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; new_edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; AddVertexToLevel(new_vertex); AddEdgeToLevel(new_edge); end_vertex[0]->VertexModifiedNofification(); end_vertex[1]->VertexModifiedNofification(); // TODO // Delete any levels greater than this level. return new_edge; } if ( nullptr != new_vertex) ReturnVertex(new_vertex); if ( nullptr != new_edge) ReturnEdge(new_edge); return ON_SUBD_RETURN_ERROR(nullptr); } const ON_SubDEdge* ON_SubDimple::SplitFace( ON_SubDFace* face, unsigned int fvi0, unsigned int fvi1 ) { if ( nullptr == face || fvi0 == fvi1) return ON_SUBD_RETURN_ERROR(nullptr); const unsigned int edge_count = face->m_edge_count; if (edge_count < 4 || (edge_count > 4 && nullptr == face->m_edgex)) return ON_SUBD_RETURN_ERROR(nullptr); if (fvi0 >= edge_count || fvi1 >= edge_count) return ON_SUBD_RETURN_ERROR(nullptr); if ((fvi0 + 1) % edge_count == fvi1 || (fvi1 + 1) % edge_count == fvi0) return ON_SUBD_RETURN_ERROR(nullptr); if (face->SubdivisionLevel() >= m_levels.UnsignedCount()) return ON_SUBD_RETURN_ERROR(nullptr); ON_SubDLevel* level = m_levels[face->SubdivisionLevel()]; if ( nullptr == level) return ON_SUBD_RETURN_ERROR(nullptr); ON_SubDVertex* v[2] = { const_cast(face->Vertex(fvi0)), const_cast(face->Vertex(fvi1)) }; if (nullptr == v[0] || v[0]->m_face_count <= 0 || nullptr == v[0]->m_faces || v[0]->m_edge_count < 2 || nullptr == v[0]->m_edges ) return ON_SUBD_RETURN_ERROR(nullptr); if (nullptr == v[1] || v[1]->m_face_count <= 0 || nullptr == v[1]->m_faces || v[1]->m_edge_count < 2 || nullptr == v[1]->m_edges) return ON_SUBD_RETURN_ERROR(nullptr); if (v[0] == v[1]) return ON_SUBD_RETURN_ERROR(nullptr); unsigned new_edge_count[2]; if (fvi0 < fvi1) { new_edge_count[1] = fvi1 - fvi0 + 1; new_edge_count[0] = (edge_count + 2) - new_edge_count[1]; } else { new_edge_count[0] = fvi0 - fvi1 + 1; new_edge_count[1] = (edge_count + 2) - new_edge_count[0]; } // make sure each side is at least a triangle and no overflows occured if (new_edge_count[0] < 3 || new_edge_count[0] >= edge_count) return ON_SUBD_RETURN_ERROR(nullptr); if (new_edge_count[1] < 3 || new_edge_count[1] >= edge_count) return ON_SUBD_RETURN_ERROR(nullptr); if (new_edge_count[0] + new_edge_count[1] != edge_count+2 ) return ON_SUBD_RETURN_ERROR(nullptr); // make sure face topology is valid ON_SimpleArray< ON_SubDEdgePtr > edges(edge_count); ON_SubDEdgePtr* eptr = face->m_edge4; edges.SetCount(edge_count); for (unsigned int fei = 0; fei < edge_count; ++fei, ++eptr) { if (4 == fei) eptr = face->m_edgex; const ON_SubDEdge* face_e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr == face_e) return ON_SUBD_RETURN_ERROR(nullptr); if (ON_UNSET_UINT_INDEX == face_e->FaceArrayIndex(face) ) return ON_SUBD_RETURN_ERROR(nullptr); const ON_SubDVertex* face_v = face_e->m_vertex[ON_SUBD_EDGE_DIRECTION(eptr->m_ptr)]; if ( nullptr == face_v) return ON_SUBD_RETURN_ERROR(nullptr); if (ON_UNSET_UINT_INDEX == face_v->FaceArrayIndex(face) ) return ON_SUBD_RETURN_ERROR(nullptr); edges[fei] = *eptr; } // create diagonal edge ON_SubDEdge* new_e = nullptr; ON_SubDFace* new_f = nullptr; for (;;) { if (false == m_heap.GrowVertexFaceArrayByOne(v[0])) break; if (false == m_heap.GrowVertexFaceArrayByOne(v[1])) break; new_f = AllocateFace(); if (nullptr == new_f) break; new_f->SetSubdivisionLevel( face->SubdivisionLevel() ); AddFaceToLevel(new_f); if (new_edge_count[1] > 4) { if (false == m_heap.GrowFaceEdgeArray(new_f, new_edge_count[1])) break; } new_e = AddEdge( ((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) break; face->FaceModifiedNofification(); v[0]->m_faces[v[0]->m_face_count++] = new_f; v[1]->m_faces[v[1]->m_face_count++] = new_f; new_e->m_face2[0] = ON_SubDFacePtr::Create(face, 0); new_e->m_face2[1] = ON_SubDFacePtr::Create(new_f, 1); new_e->m_face_count = 2; const ON_SubDEdgePtr new_eptr = ON_SubDEdgePtr::Create(new_e); eptr = face->m_edge4; for (unsigned int fei = 0; fei < edge_count; ++fei, ++eptr) { if (4 == fei) eptr = face->m_edgex; *eptr = ON_SubDEdgePtr::Null; } face->m_edge_count = 0; if (new_edge_count[0] <= 4 && nullptr != face->m_edgex) m_heap.ReturnFaceExtraArray(face); // update old face face->m_edge4[0] = new_eptr; eptr = &(face->m_edge4[1]); for (unsigned fei = 1; fei < new_edge_count[0]; ++fei, ++eptr) { if (4 == fei) eptr = face->m_edgex; *eptr = edges[(fvi1 + fei - 1) % edge_count]; } face->m_edge_count = (unsigned short)new_edge_count[0]; // initialize new_f new_f->m_edge4[0] = new_eptr.Reversed(); eptr = &(new_f->m_edge4[1]); for (unsigned fei = 1; fei < new_edge_count[1]; ++fei, ++eptr) { if (4 == fei) eptr = new_f->m_edgex; *eptr = edges[(fvi0 + fei - 1) % edge_count]; // change edge's face reference from old face to new_f. ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); e->ReplaceFaceInArray(face, new_f); ON_SubDVertex* vtx = const_cast(eptr->RelativeVertex(0)); if (nullptr != vtx && v[0] != vtx && v[1] != vtx) vtx->ReplaceFaceInArray(face, new_f); } new_f->m_edge_count = (unsigned short)new_edge_count[1]; // update sector weights because they depend on the number of edges for (unsigned int vi = 0; vi < 2; vi++) { for (unsigned short evi = 0; evi < v[vi]->m_edge_count; ++evi) { ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(v[vi]->m_edges[evi].m_ptr); if (nullptr != edge) edge->UpdateEdgeSectorCoefficientsForExperts(true); } } // Debugging code const ON_SubDVertex* v0[6] = { face->m_edge4[0].RelativeVertex(0),face->m_edge4[0].RelativeVertex(1), face->m_edge4[1].RelativeVertex(0),face->m_edge4[1].RelativeVertex(1), face->EdgePtr(face->m_edge_count-1).RelativeVertex(0),face->EdgePtr(face->m_edge_count - 1).RelativeVertex(1), }; const ON_SubDEdge* e0[3] = { face->m_edge4[0].Edge(), face->m_edge4[1].Edge(), face->EdgePtr(face->m_edge_count - 1).Edge() }; const ON_SubDVertex* v1[6] = { new_f->m_edge4[0].RelativeVertex(0),new_f->m_edge4[0].RelativeVertex(1), new_f->m_edge4[1].RelativeVertex(0),new_f->m_edge4[1].RelativeVertex(1), new_f->EdgePtr(new_f->m_edge_count - 1).RelativeVertex(0),new_f->EdgePtr(new_f->m_edge_count - 1).RelativeVertex(1), }; const ON_SubDEdge* e1[3] = { new_f->m_edge4[0].Edge(), new_f->m_edge4[1].Edge(), new_f->EdgePtr(new_f->m_edge_count - 1).Edge() }; if (nullptr == v0[5] && nullptr == v1[5] && nullptr == e0[2] && nullptr == e1[2]) return nullptr; return new_e; } if ( nullptr != new_f ) ReturnFace(new_f); if (nullptr != new_e) { v[0]->m_edge_count--; v[0]->m_edges[v[0]->m_edge_count] = ON_SubDEdgePtr::Null; v[1]->m_edge_count--; v[1]->m_edges[v[1]->m_edge_count] = ON_SubDEdgePtr::Null; new_e->m_vertex[0] = nullptr; new_e->m_vertex[1] = nullptr; new_e->m_face_count = 0; ReturnEdge(new_e); } return ON_SUBD_RETURN_ERROR(nullptr); } const ON_SubDEdge* ON_SubD::SplitEdge( ON_SubDEdge* edge, ON_3dPoint vertex_location ) { ON_SubDimple* subdimple = SubDimple(false); if ( nullptr == subdimple ) return ON_SUBD_RETURN_ERROR(nullptr); return subdimple->SplitEdge(edge,vertex_location); } const ON_SubDEdgePtr ON_SubD::SplitEdge( ON_SubDEdgePtr eptr, ON_3dPoint vertex_location, unsigned new_edge_end ) { if ( 0 != new_edge_end && 1 != new_edge_end) return ON_SubDEdgePtr::Null; ON_SubDEdge* e = eptr.Edge(); if (nullptr == e) return ON_SubDEdgePtr::Null; const ON_SubDVertex* v[3] = { e->m_vertex[0],nullptr,e->m_vertex[1] }; ON_SubDEdge* e1 = const_cast(this->SplitEdge(e, vertex_location)); if ( nullptr == e1) return ON_SubDEdgePtr::Null; const ON__UINT_PTR eptr_dir = eptr.EdgeDirection(); const ON_SubDEdgePtr e1ptr = ON_SubDEdgePtr::Create(e1, eptr_dir); if (v[0] != e->m_vertex[0] || v[2] != e1->m_vertex[1]) { ON_SUBD_ERROR("Error in SplitEdge(ON_SubDEdge*,...)."); return e1ptr; } v[1] = e->m_vertex[1]; if ( nullptr == v[1] || v[1] != e1->m_vertex[0] || 2 != v[1]->m_edge_count) { ON_SUBD_ERROR("Error in SplitEdge(ON_SubDEdge*,...)."); return e1ptr; } if (e->m_face_count != e1->m_face_count) { ON_SUBD_ERROR("Error in SplitEdge(ON_SubDEdge*,...)."); return e1ptr; } const unsigned v0edex = v[0]->EdgeArrayIndex(e); const unsigned v2edex = v[2]->EdgeArrayIndex(e1); if (v0edex >= v[0]->EdgeCount() || v2edex >= v[2]->EdgeCount()) { ON_SUBD_ERROR("Error in SplitEdge(ON_SubDEdge*,...)."); return e1ptr; } if (0 == eptr_dir && 1 == new_edge_end) { return e1ptr; } if (1 == eptr_dir && 0 == new_edge_end) { return e1ptr; } // swap e and e1 topology connections to put the "new edge" where the caller wants it. for (unsigned short efi = 0; efi < e->m_face_count; ++efi) { ON_SubDFace* f = const_cast(e->Face(efi)); if (nullptr == f) continue; const unsigned edge_count = f->EdgeCount(); const unsigned fei = f->EdgeArrayIndex(e); const unsigned fe1i = f->EdgeArrayIndex(e1); if (fei >= edge_count || fe1i >= edge_count) continue; const ON_SubDEdgePtr feptr = ON_SubDEdgePtr::Create(e1, f->EdgePtr(fei).EdgeDirection()); // e1 is correct - we are swapping if (fei < 4) f->m_edge4[fei] = feptr; else f->m_edgex[fei - 4] = feptr; const ON_SubDEdgePtr fe1ptr = ON_SubDEdgePtr::Create(e, f->EdgePtr(fe1i).EdgeDirection()); // e is correct - we are swapping if (fe1i < 4) f->m_edge4[fe1i] = fe1ptr; else f->m_edgex[fe1i - 4] = fe1ptr; } const ON_SubDEdgePtr new_v0eptr = ON_SubDEdgePtr::Create(e1, v[0]->EdgePtr(v0edex).EdgeDirection()); const ON_SubDEdgePtr new_v2eptr = ON_SubDEdgePtr::Create(e, v[2]->EdgePtr(v2edex).EdgeDirection()); v[0]->m_edges[v0edex] = new_v0eptr; v[1]->m_edges[0] = new_v0eptr.Reversed(); v[1]->m_edges[1] = new_v2eptr.Reversed(); v[2]->m_edges[v2edex] = new_v2eptr; e1->m_vertex[0] = v[0]; e1->m_vertex[1] = v[1]; e->m_vertex[0] = v[1]; e->m_vertex[1] = v[2]; return e1ptr; } const ON_SubDEdge * ON_SubD::SplitFace( ON_SubDFace * face, const ON_SubDVertex * v0, const ON_SubDVertex * v1 ) { if (nullptr == face || nullptr == v0 || nullptr == v1 || v0 == v1) return ON_SUBD_RETURN_ERROR(nullptr); unsigned int fvi0 = ON_UNSET_UINT_INDEX; unsigned int fvi1 = ON_UNSET_UINT_INDEX; ON_SubDEdgePtr* eptr = face->m_edge4; for (unsigned short fei = 0; fei < face->m_edge_count; ++fei, ++eptr) { if (4 == fei) { eptr = face->m_edgex; if (nullptr == eptr) return ON_SUBD_RETURN_ERROR(nullptr); } const ON_SubDVertex* v = eptr->RelativeVertex(0); if (v == v0) { if (ON_UNSET_UINT_INDEX != fvi0) return ON_SUBD_RETURN_ERROR(nullptr); fvi0 = fei; } else if (v == v1) { if (ON_UNSET_UINT_INDEX != fvi1) return ON_SUBD_RETURN_ERROR(nullptr); fvi1 = fei; } } return (ON_UNSET_UINT_INDEX != fvi0 && ON_UNSET_UINT_INDEX != fvi1) ? SplitFace(face, fvi0, fvi1) : ON_SUBD_RETURN_ERROR(nullptr); } const ON_SubDEdge* ON_SubD::SplitFace( ON_SubDFace* face, unsigned int fvi0, unsigned int fvi1 ) { ON_SubDimple* subdimple = SubDimple(false); if ( 0 == subdimple ) return ON_SUBD_RETURN_ERROR(nullptr); return subdimple->SplitFace(face,fvi0,fvi1); } static void Internal_SplitFaceSwapFacePtr( ON_SubDFace* f, ON__UINT_PTR pairA[2], ON__UINT_PTR pairB[2] ) { if (nullptr == f) return; if (pairA[0] == pairB[0] || pairA[1] == pairB[0] || pairA[0] == pairB[1] || pairA[1] == pairB[1]) { ON_SUBD_ERROR("pairA[] and pairB[] must be disjoint sets of values."); return; } ON_SubDEdgePtr* feptr = f->m_edge4; for (unsigned short fei = 0; fei < f->m_edge_count; ++fei, ++feptr) { if (4 == fei) { feptr = f->m_edgex; if (nullptr == feptr) break; } ON_SubDEdge* fe = ON_SUBD_EDGE_POINTER(feptr->m_ptr); if (nullptr == fe) continue; ON_SubDFacePtr* efptr = fe->m_face2; for (unsigned short efi = 0; efi < fe->m_face_count; ++efi, ++efptr) { if (2 == efi) { efptr = fe->m_facex; if (nullptr == efptr) break; } const ON__UINT_PTR x = (efptr->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); const ON__UINT_PTR d = (efptr->m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK); if (x == pairA[0]) efptr->m_ptr = (pairA[1] | d); else if (x == pairB[0]) efptr->m_ptr = (pairB[1] | d); } ON_SubDVertex* v = const_cast(feptr->RelativeVertex(0)); if (nullptr != v && nullptr != v->m_faces) { for (unsigned short vfi = 0; vfi < v->m_face_count; ++vfi) { const ON__UINT_PTR x = (ON__UINT_PTR)v->m_faces[vfi]; if (x == pairA[0]) v->m_faces[vfi] = (ON_SubDFace*)pairA[1]; else if (x == pairB[0]) v->m_faces[vfi] = (ON_SubDFace*)pairB[1]; } } } } static bool Internal_ValidateFaceTopology( const ON_SubDFace* f ) { if (nullptr == f) return false; if (f->m_edge_count < 3 || 0 == f->m_id) return ON_SUBD_RETURN_ERROR(false); const ON_SubDVertex* firstv = nullptr; const ON_SubDVertex* ev[2] = {}; bool rc = true; unsigned short fdex; const ON_SubDEdgePtr* feptr = f->m_edge4; for (unsigned short fei = 0; fei < f->m_edge_count; ++fei, ++feptr) { if (4 == fei) { feptr = f->m_edgex; if (nullptr == feptr) { ON_SUBD_ERROR("face m_edge_count > 4 and m_edgex is nullptr."); rc = false; break; } } ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(feptr->m_ptr); if (nullptr == e || 0 == e->m_id) { ON_SUBD_ERROR("null edge in face."); rc = false; continue; } if (e != f->Edge(fei)) { ON_SUBD_ERROR("face->Edge(fei) != e"); rc = false; } for (unsigned short k = fei + 1; k < f->m_edge_count; ++k) { if (e == f->Edge(k)) { ON_SUBD_ERROR("face references e 2 or more times."); rc = false; } } const ON__UINT_PTR fedir = ON_SUBD_FACE_DIRECTION(feptr->m_ptr); if (0 == fei) { firstv = e->m_vertex[fedir]; ev[1] = firstv; } if (ev[1] != e->m_vertex[fedir]) { ON_SUBD_ERROR("consecutive edge pointers do not have the same vertex at the end/start."); rc = false; } ev[0] = e->m_vertex[fedir]; ev[1] = e->m_vertex[1-fedir]; if (nullptr == ev[0] || nullptr == ev[1]) { ON_SUBD_ERROR("edge has null vertex pointers."); rc = false; } else if (ev[0] == ev[1]) { ON_SUBD_ERROR("edge begins and ends at the same vertex."); rc = false; } fdex = e->m_face_count; const ON_SubDFacePtr* efptr = e->m_face2; for (unsigned short efi = 0; efi < e->m_face_count; ++efi, ++efptr) { if (2 == efi) { efptr = e->m_facex; if (nullptr == efptr) { ON_SUBD_ERROR("null edge in face."); rc = false; break; } } const ON_SubDFace* ef = ON_SUBD_FACE_POINTER(efptr->m_ptr); if (f == ef) { if (fdex < e->m_face_count) { ON_SUBD_ERROR("ef references face 2 or more times."); rc = false; break; } fdex = efi; ON__UINT_PTR efdir = ON_SUBD_FACE_DIRECTION(efptr->m_ptr); if (efdir != fedir) { ON_SUBD_ERROR("face and edge have inconsistent direction settings."); rc = false; break; } } } if (fdex >= e->m_face_count) { ON_SUBD_ERROR("The face references and edge that does not reference the face."); rc = false; } const ON_SubDVertex* v = ev[0]; if (nullptr != v ) { if (v != f->Vertex(fei)) { ON_SUBD_ERROR("face->Vertex(fei) != v"); rc = false; } fdex = v->m_face_count; for (unsigned short vfi = 0; vfi < v->m_face_count; ++vfi) { const ON_SubDFace* vf = v->m_faces[vfi]; if (f == vf) { if (fdex < v->m_face_count) { ON_SUBD_ERROR("ef references face 2 or more times."); rc = false; break; } fdex = vfi; } } if (fdex >= v->m_face_count) { ON_SUBD_ERROR("The face references a vertex that does not reference the face."); rc = false; } for (unsigned short k = fei + 1; k < f->m_edge_count; ++k) { if (v == f->Vertex(k)) { ON_SUBD_ERROR("face references v 2 or more times."); rc = false; } } } } if (firstv != ev[1]) { ON_SUBD_ERROR("The vertices at the end ofthe final edge and start of the initial edges are different."); rc = false; } return rc; } const ON_SubDEdgePtr ON_SubD::SplitFace( class ON_SubDFace* face, unsigned int fvi0, unsigned int fvi1, unsigned new_face_side ) { if (new_face_side != 0 && new_face_side != 1) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); face->ClearSavedSubdivisionPoints(true); ON_SubDEdge* e = const_cast(this->SplitFace(face, fvi0, fvi1)); if (nullptr == e) return ON_SubDEdgePtr::Null; const ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Create(e, 0); ON_SubDFace* f2[2] = { const_cast(eptr.RelativeFace(0)), const_cast(eptr.RelativeFace(1)) }; if (nullptr == f2[0] || nullptr == f2[1] || f2[0] == f2[1]) return ON_SUBD_RETURN_ERROR(eptr); if (face != f2[0] && face != f2[1]) return ON_SUBD_RETURN_ERROR(eptr); if ( face == f2[new_face_side]) { // swap faces // markA and markB need to be different, must be values no valid heap pointer will // ever have, and need to be chosen so that debug runtime pointer checking code will not // throw exceptions. Values like 1 and 2 would work just fine except debug runtime // pointer checking code is mindlessly diligent. the addresses of marking_faces[0] // and marking_faces[1] "tricks" the debug runtime pointer checking code and // are values no valid app heap will ever have (they point to stack // used by this call). const ON_SubDFace marking_faces[2] = {}; const ON__UINT_PTR markA = (ON__UINT_PTR)(&marking_faces[0]); const ON__UINT_PTR markB = (ON__UINT_PTR)(&marking_faces[1]); // The mark first, then change approach must be used because f2[0] and f2[1] // have multiple vertex and edge references and we must avoid swapping twice. // First change face references f2[0] to markA and f2[1] to markB. ON__UINT_PTR pairA[2] = { (ON__UINT_PTR)f2[0], markA }; ON__UINT_PTR pairB[2] = { (ON__UINT_PTR)f2[1], markB }; for (unsigned fdex = 0; fdex < 2; ++fdex) Internal_SplitFaceSwapFacePtr(f2[fdex], pairA, pairB); // ... then change markA to f2[1] and markB to f2[0]. pairA[0] = markA; pairA[1] = (ON__UINT_PTR)f2[1]; pairB[0] = markB; pairB[1] = (ON__UINT_PTR)f2[0]; for (unsigned fdex = 0; fdex < 2; ++fdex) Internal_SplitFaceSwapFacePtr(f2[fdex], pairA, pairB); // At this point, all the vertex and edge references to f2[0] and f2[1] have been swapped. // All that is left is to wwap edge references on f2[0] and f2[1]. const unsigned short edge_count[2] = { f2[1]->m_edge_count, f2[0]->m_edge_count }; const unsigned short edgex_capacity[2] = { f2[1]->m_edgex_capacity, f2[0]->m_edgex_capacity }; ON_SubDEdgePtr edge4[2][4] = { {f2[1]->m_edge4[0],f2[1]->m_edge4[1],f2[1]->m_edge4[2],f2[1]->m_edge4[3]}, {f2[0]->m_edge4[0],f2[0]->m_edge4[1],f2[0]->m_edge4[2],f2[0]->m_edge4[3]} }; ON_SubDEdgePtr* edgex[2] = { f2[1]->m_edgex, f2[0]->m_edgex }; for (unsigned i = 0; i < 2; ++i) { f2[i]->m_edge_count = edge_count[i]; f2[i]->m_edgex_capacity = edgex_capacity[i]; f2[i]->m_edge4[0] = edge4[i][0]; f2[i]->m_edge4[1] = edge4[i][1]; f2[i]->m_edge4[2] = edge4[i][2]; f2[i]->m_edge4[3] = edge4[i][3]; f2[i]->m_edgex = edgex[i]; } //// The Internal_SplitFaceSwapFacePtr() calls above have switched the //// ON_SubDFace pointer values in e->m_face2[]. Because the faces //// are switching sides, we need to swap the directions in e->m_face2[]. //// //e->m_face2[0] = e->m_face2[0].Reversed(); //e->m_face2[1] = e->m_face2[1].Reversed(); } if (2 == e->m_face_count && face == e->m_face2[new_face_side].Face()) { // Adjust the e->m_face2[] so it jibes with the relative face values exactly. ON_SubDFacePtr tmp = e->m_face2[0]; e->m_face2[0] = e->m_face2[1]; e->m_face2[1] = tmp; } if (false == Internal_ValidateFaceTopology(f2[0]) || false == Internal_ValidateFaceTopology(f2[1]) || f2[0] == f2[1] ) { ON_SUBD_ERROR("Invalid faces."); } return eptr; // new face is on the requested side } const ON_SubDEdgePtr ON_SubD::SplitFace( class ON_SubDFace* face, const class ON_SubDVertex* v0, const class ON_SubDVertex* v1, unsigned new_face_side ) { if (nullptr == face) return ON_SubDEdgePtr::Null; return this->SplitFace(face, face->VertexIndex(v0), face->VertexIndex(v1), new_face_side); } void ON_SubD::MarkAggregateComponentStatusAsNotCurrent() const { const ON_SubDLevel* level = ActiveLevelConstPointer(); if ( level ) level->MarkAggregateComponentStatusAsNotCurrent(); } unsigned int ON_SubDLevel::ClearStates( ON_ComponentStatus states_to_clear ) const { unsigned int rc = 0; m_aggregates.m_aggregate_status.ClearAggregateStatus(states_to_clear); for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) rc += vertex->m_status.ClearStates(states_to_clear); for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) rc += edge->m_status.ClearStates(states_to_clear); for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) rc += face->m_status.ClearStates(states_to_clear); return rc; } unsigned int ON_SubDLevel::ClearRuntimeMarks( bool bClearVertexMarks, bool bClearEdgeMarks, bool bClearFaceMarks ) const { unsigned int rc = 0; if (bClearVertexMarks) { for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) { if (vertex->m_status.ClearRuntimeMark()) ++rc; } } if (bClearEdgeMarks) { for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) { if (edge->m_status.ClearRuntimeMark()) ++rc; } } if (bClearFaceMarks) { for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) { if (face->m_status.ClearRuntimeMark()) ++rc; } } return rc; } unsigned int ON_SubD::ClearComponentStates( ON_ComponentStatus states_to_clear ) const { const ON_SubDLevel* level = ActiveLevelConstPointer(); if ( level ) return level->ClearStates(states_to_clear); return ON_SUBD_RETURN_ERROR(0); } ON_AggregateComponentStatus ON_SubD::AggregateComponentStatus() const { return ActiveLevel().AggregateComponentStatus(); } unsigned int ON_SubDLevel::GetComponentsWithSetStates( ON_ComponentStatus states_filter, bool bAllEqualStates, ON_SimpleArray< ON_SubDComponentPtr >& components_with_set_states ) const { components_with_set_states.SetCount(0); if (states_filter.IsClear()) return 0; const ON_AggregateComponentStatusEx acs = AggregateComponentStatus(); ON_ComponentStatus as = acs.AggregateStatus(); if (bAllEqualStates) { if ( false == as.AllEqualStates(states_filter, states_filter) ) return 0; } else { if ( false == as.SomeEqualStates(states_filter, states_filter) ) return 0; } unsigned int c = 0; if ( states_filter.IsSelected() && c < m_aggregates.m_aggregate_status.SelectedCount() ) c = m_aggregates.m_aggregate_status.SelectedCount(); if ( states_filter.IsHighlighted() && c < m_aggregates.m_aggregate_status.HighlightedCount() ) c = m_aggregates.m_aggregate_status.HighlightedCount(); if ( states_filter.IsHidden() && c < m_aggregates.m_aggregate_status.HiddenCount() ) c = m_aggregates.m_aggregate_status.HiddenCount(); if ( states_filter.IsLocked() && c < m_aggregates.m_aggregate_status.LockedCount() ) c = m_aggregates.m_aggregate_status.LockedCount(); if ( states_filter.IsDamaged() && c < m_aggregates.m_aggregate_status.DamagedCount() ) c = m_aggregates.m_aggregate_status.DamagedCount(); if ( states_filter.IsSelected() && c < m_aggregates.m_aggregate_status.SelectedCount() ) c = m_aggregates.m_aggregate_status.SelectedCount(); components_with_set_states.Reserve(c); if (bAllEqualStates) { for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) { if (vertex->m_status.AllEqualStates(states_filter, states_filter)) components_with_set_states.Append(ON_SubDComponentPtr::Create(vertex)); } for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) { if (edge->m_status.AllEqualStates(states_filter, states_filter)) components_with_set_states.Append(ON_SubDComponentPtr::Create(edge)); } for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) { if (face->m_status.AllEqualStates(states_filter, states_filter)) components_with_set_states.Append(ON_SubDComponentPtr::Create(face)); } } else { for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) { if (vertex->m_status.SomeEqualStates(states_filter, states_filter)) components_with_set_states.Append(ON_SubDComponentPtr::Create(vertex)); } for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) { if (edge->m_status.SomeEqualStates(states_filter, states_filter)) components_with_set_states.Append(ON_SubDComponentPtr::Create(edge)); } for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) { if (face->m_status.SomeEqualStates(states_filter, states_filter)) components_with_set_states.Append(ON_SubDComponentPtr::Create(face)); } } return components_with_set_states.UnsignedCount(); } unsigned int ON_SubD::GetComponentsWithSetStates( ON_ComponentStatus states_filter, bool bAllEqualStates, ON_SimpleArray< ON_SubDComponentPtr >& components_with_set_states ) const { return ActiveLevel().GetComponentsWithSetStates( states_filter, bAllEqualStates, components_with_set_states ); } unsigned int ON_SubD::GetComponentsWithSetStates( ON_ComponentStatus states_filter, bool bAllEqualStates, ON_SimpleArray< ON_COMPONENT_INDEX >& components_with_set_states ) const { components_with_set_states.SetCount(0); ON_SimpleArray< ON_SubDComponentPtr > cptr; GetComponentsWithSetStates( states_filter, bAllEqualStates, cptr ); unsigned int count = cptr.UnsignedCount(); if (count > 0) { components_with_set_states.Reserve(count); components_with_set_states.SetCount(count); const ON_SubDComponentPtr* cp = cptr.Array(); ON_COMPONENT_INDEX* ci = components_with_set_states.Array(); for ( const ON_SubDComponentPtr* cp1 = cp+count; cp < cp1; cp++ ) *ci++ = cp->ComponentIndex(); } return count; } unsigned int ON_SubDLevel::SetStates( ON_SubDComponentPtr component_ptr, ON_ComponentStatus states_to_set ) const { if (0 != component_ptr.SetStates(states_to_set)) { m_aggregates.m_aggregate_status.MarkAsNotCurrent(); return 1; } return 0; } unsigned int ON_SubDLevel::ClearStates( ON_SubDComponentPtr component_ptr, ON_ComponentStatus states_to_clear ) const { if (0 != component_ptr.ClearStates(states_to_clear)) { m_aggregates.m_aggregate_status.MarkAsNotCurrent(); return 1; } return 0; } unsigned int ON_SubDLevel::SetStatus( ON_SubDComponentPtr component_ptr, ON_ComponentStatus status_to_copy ) const { if (0 != component_ptr.SetStatus(status_to_copy)) { m_aggregates.m_aggregate_status.MarkAsNotCurrent(); return 1; } return 0; } unsigned int ON_SubD::SetComponentStates( ON_COMPONENT_INDEX component_index, ON_ComponentStatus states_to_set ) const { return SetComponentStates(ComponentPtrFromComponentIndex(component_index),states_to_set); } unsigned int ON_SubD::SetComponentStates( ON_SubDComponentPtr component_ptr, ON_ComponentStatus states_to_set ) const { const ON_SubDLevel* level = ActiveLevelConstPointer(); if ( nullptr != level ) return level->SetStates(component_ptr,states_to_set); return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubD::ClearComponentStates( ON_COMPONENT_INDEX component_index, ON_ComponentStatus states_to_clear ) const { return ClearComponentStates(ComponentPtrFromComponentIndex(component_index),states_to_clear); } unsigned int ON_SubD::ClearComponentStates( ON_SubDComponentPtr component_ptr, ON_ComponentStatus states_to_clear ) const { const ON_SubDLevel* level = ActiveLevelConstPointer(); if ( nullptr != level ) return level->ClearStates(component_ptr,states_to_clear); return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubD::SetComponentStatus( ON_COMPONENT_INDEX component_index, ON_ComponentStatus status_to_copy ) const { return ClearComponentStates(ComponentPtrFromComponentIndex(component_index),status_to_copy); } unsigned int ON_SubD::SetComponentStatus( ON_SubDComponentPtr component_ptr, ON_ComponentStatus status_to_copy ) const { const ON_SubDLevel* level = ActiveLevelConstPointer(); if ( nullptr != level ) return level->SetStatus(component_ptr,status_to_copy); return ON_SUBD_RETURN_ERROR(0); } void ON_SubDLevel::ClearEvaluationCache() const { ClearEdgeFlags(); 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) { vertex->ClearSavedSubdivisionPoints(); } for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) { edge->ClearSavedSubdivisionPoints(); // NO // edge->UnsetSectorCoefficients(); // Leave these set - they are not "cached" values and except for corner case below // the are independent vertex locations. if ( edge->IsSmooth() ) { for (unsigned evi = 0; evi < 2; evi++) { if ( false == (edge->m_sector_coefficient[evi] > 0.0 && edge->m_sector_coefficient[evi] < 1.0) ) continue; const ON_SubDVertex* v = edge->m_vertex[evi]; if (nullptr == v) continue; 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. const_cast(edge)->m_sector_coefficient[evi] = ON_SubDSectorType::Create(edge, evi).SectorCoefficient(); } } } for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) { face->ClearSavedSubdivisionPoints(); } } bool ON_SubD::CopyEvaluationCacheForExperts(const ON_SubD& src) { 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; } 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(this->m_heap , *src_level, src.m_heap) : false; } bool ON_SubDLevel::CopyEvaluationCacheForExperts( ON_SubDHeap& this_heap, const ON_SubDLevel& src, const ON_SubDHeap& src_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); src.m_level_index; // The built in fragment cache always has adaptive ON_SubDDisplayParameters::DefaultDensity const unsigned subd_display_density = ON_SubDDisplayParameters::AbsoluteDisplayDensityFromSubDFaceCount(ON_SubDDisplayParameters::DefaultDensity,m_face_count); const unsigned this_level_index = this->m_level_index; const unsigned src_level_index = src.m_level_index; // It is critical to use the this_vit/src_vit iterators so we got through the vertices in id order. // When a copy of an editied subd is made, it is frequently the case that the vertex linked lists // are in different order. ON_SubDVertexIdIterator this_vit; ON_SubDVertexIdIterator src_vit; this_heap.InitializeVertexIdIterator(this_vit); src_heap.InitializeVertexIdIterator(src_vit); 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 = const_cast(this_vit.FirstVertexOnLevel(this_level_index)), src_vertex = src_vit.FirstVertexOnLevel(src_level_index); nullptr != this_vertex && nullptr != src_vertex; this_vertex = const_cast(this_vit.NextVertexOnLevel(this_level_index)), src_vertex = src_vit.NextVertexOnLevel(src_level_index) ) { 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; if (false == src_vertex->SavedSubdivisionPointIsSet()) continue; bCopyVertexCache = true; for (unsigned short vei = 0; vei < this_vertex->m_edge_count; ++vei) { 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); // It is critical to use the this_eit/src_eit iterators so we got through the edges in id order. // When a copy of an editied subd is made, it is frequently the case that the edge linked lists // are in different order. ON_SubDEdgeIdIterator this_eit; ON_SubDEdgeIdIterator src_eit; this_heap.InitializeEdgeIdIterator(this_eit); src_heap.InitializeEdgeIdIterator(src_eit); 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 = const_cast(this_eit.FirstEdgeOnLevel(this_level_index)), src_edge = src_eit.FirstEdgeOnLevel(src_level_index); nullptr != this_edge && nullptr != src_edge; this_edge = const_cast(this_eit.NextEdgeOnLevel(this_level_index)), src_edge = src_eit.NextEdgeOnLevel(src_level_index) ) { 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) { 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); // It is critical to use the this_fit/src_fit iterators so we got through the faces in id order. // When a copy of an editied subd is made, it is frequently the case that the face linked lists // are in different order. ON_SubDFaceIdIterator this_fit; ON_SubDFaceIdIterator src_fit; this_heap.InitializeFaceIdIterator(this_fit); src_heap.InitializeFaceIdIterator(src_fit); const ON_SubDEdgePtr* this_eptr; const ON_SubDEdgePtr* src_eptr; bool bCopyFaceCache = false; for ( this_face = const_cast(this_fit.FirstFaceOnLevel(this_level_index)), src_face = src_fit.FirstFaceOnLevel(src_level_index); nullptr != this_face && nullptr != src_face; this_face = const_cast(this_fit.NextFaceOnLevel(this_level_index)), src_face = src_fit.NextFaceOnLevel(src_level_index) ) { 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) { // It is critical to use the this_vit/src_vit iterators so we got through the vertices in id order. // When a copy of an editied subd is made, it is frequently the case that the vertex linked lists // are in different order. ON_SubDSectorSurfacePoint this_limit_point; for ( this_vertex = const_cast(this_vit.FirstVertexOnLevel(this_level_index)), src_vertex = src_vit.FirstVertexOnLevel(src_level_index); nullptr != this_vertex && nullptr != src_vertex; this_vertex = const_cast(this_vit.NextVertexOnLevel(this_level_index)), src_vertex = src_vit.NextVertexOnLevel(src_level_index) ) { 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); } } } } if (bCopyEdgeCache) { // It is critical to use the this_eit/src_eit iterators so we got through the edges in id order. // When a copy of an editied subd is made, it is frequently the case that the edge linked lists // are in different order. ON_SimpleArray edge_curve_cvs(ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity); for ( this_edge = const_cast(this_eit.FirstEdgeOnLevel(this_level_index)), src_edge = src_eit.FirstEdgeOnLevel(src_level_index); nullptr != this_edge && nullptr != src_edge; this_edge = const_cast(this_eit.NextEdgeOnLevel(this_level_index)), src_edge = src_eit.NextEdgeOnLevel(src_level_index) ) { if (false == src_edge->GetSavedSubdivisionPoint(subdivision_point)) continue; 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) { // It is critical to use the this_fit/src_fit iterators so we got through the faces in id order. // When a copy of an editied subd is made, it is frequently the case that the face linked lists // are in different order. for ( this_face = const_cast(this_fit.FirstFaceOnLevel(this_level_index)), src_face = src_fit.FirstFaceOnLevel(src_level_index); nullptr != this_face && nullptr != src_face; this_face = const_cast(this_fit.NextFaceOnLevel(this_level_index)), src_face = src_fit.NextFaceOnLevel(src_level_index) ) { 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, subd_display_density, this_face); } } return true; } unsigned int ON_SubD::ComponentPtrFromComponentIndex( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, bool bIncludeVertices, bool bIncludeEdges, bool bIncludeFaces, ON_SimpleArray& cptr_list ) const { if ( ci_count <= 0 ) return 0; if ( false == bIncludeVertices && false == bIncludeEdges && false == bIncludeFaces ) return 0; if ( nullptr == ci_list ) return ON_SUBD_RETURN_ERROR(0); const unsigned int count0 = cptr_list.UnsignedCount(); cptr_list.Reserve(count0 + ci_count); const bool bFilter = false == bIncludeVertices || false == bIncludeEdges || false == bIncludeFaces; for (size_t i = 0; i < ci_count; i++) { const ON_COMPONENT_INDEX ci = ci_list[i]; if (bFilter) { if (false == bIncludeVertices || ON_COMPONENT_INDEX::TYPE::subd_vertex == ci.m_type) continue; if (false == bIncludeEdges || ON_COMPONENT_INDEX::TYPE::subd_edge == ci.m_type) continue; if (false == bIncludeFaces || ON_COMPONENT_INDEX::TYPE::subd_face == ci.m_type) continue; } ON_SubDComponentPtr cptr = ComponentPtrFromComponentIndex(ci_list[i]); if (cptr.IsNull()) continue; cptr_list.Append(cptr); } return (cptr_list.UnsignedCount() - count0); } unsigned int ON_SubD::ComponentPtrFromComponentIndex( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, ON_SimpleArray& cptr_list ) const { return ComponentPtrFromComponentIndex(ci_list, ci_count, true, true, true, cptr_list); } bool ON_SubD::DeleteComponents( const ON_COMPONENT_INDEX* ci_list, size_t ci_count ) { ON_SimpleArray cptr_list; if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0) return true; // nothing to delete return DeleteComponents(cptr_list.Array(),cptr_list.UnsignedCount(),false); } bool ON_SubD::DeleteComponents( const ON_SimpleArray& cptr_list, bool bMarkDeletedFaceEdges ) { return DeleteComponents(cptr_list.Array(), cptr_list.UnsignedCount(), bMarkDeletedFaceEdges); } bool ON_SubD::DeleteComponents( const ON_SubDComponentPtr* cptr_list, size_t cptr_count, bool bMarkDeletedFaceEdges ) { const bool bDeleteIsolatedEdges = true; const bool bUpdateTagsAndCoefficients = true; return DeleteComponentsForExperts(cptr_list, cptr_count, bDeleteIsolatedEdges, bUpdateTagsAndCoefficients, bMarkDeletedFaceEdges); } bool ON_SubD::DeleteComponentsForExperts( const ON_SubDComponentPtr* cptr_list, size_t cptr_count, bool bDeleteIsolatedEdges, bool bUpdateTagsAndCoefficients, bool bMarkDeletedFaceEdges ) { if (bMarkDeletedFaceEdges) ClearComponentMarks(false,true,false,nullptr); if ( cptr_count <= 0 ) return true; if ( nullptr == cptr_list ) return ON_SUBD_RETURN_ERROR(false); ON_SubDimple* subdimple = SubDimple(false); if ( nullptr == subdimple ) return ON_SUBD_RETURN_ERROR(false); const unsigned int level_count = subdimple->LevelCount(); if (level_count <= 0) return ON_SUBD_RETURN_ERROR(false); unsigned level_index = level_count; for (size_t i = 0; i < cptr_count; i++) { const ON_SubDComponentBase* c = cptr_list[i].ComponentBase(); if ( nullptr == c) continue; if ( c->SubdivisionLevel() < level_index ) level_index = c->SubdivisionLevel(); } if ( level_index == level_count ) return ON_SUBD_RETURN_ERROR(false); if ( false == subdimple->SetActiveLevel(level_index) ) return ON_SUBD_RETURN_ERROR(false); subdimple->ClearHigherSubdivisionLevels(level_index); const ON_SubDLevel* level = subdimple->ActiveLevelPointer(); if ( nullptr == level || level->m_level_index != level_index) return ON_SUBD_RETURN_ERROR(false); // Make sure no components have a status = ON_ComponentStatus::AllSet // because this uncommon status value is used to mark components that will be be deleted. ON_SubDComponentIterator cit(*this); for (ON_SubDComponentPtr cptr = cit.FirstComponent(); cptr.IsNotNull(); cptr = cit.NextComponent()) { if ( ON_ComponentStatus::AllSet == cptr.Status() ) cptr.ClearStates(ON_ComponentStatus::Damaged); } // Set the status of every compoent in cptr_list[] to ON_ComponentStatus::AllSet. // If that component is a vertex, set the status of every edge and face that // touch the vertex to ON_ComponentStatus::AllSet. // If that component is an edge, set the status of every face that // touches the edge to ON_ComponentStatus::AllSet. for (size_t i = 0; i < cptr_count; i++) { ON_SubDComponentPtr cptr = cptr_list[i]; const ON_SubDComponentBase* c = cptr.ComponentBase(); if (nullptr == c) continue; if (c->SubdivisionLevel() != level_index) continue; c->m_status = ON_ComponentStatus::AllSet; switch (cptr.ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: { const ON_SubDVertex* vertex = cptr.Vertex(); if (nullptr == vertex) continue; for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++) { const ON_SubDEdge* edge = vertex->Edge(vei); if (nullptr == edge) continue; edge->m_status = ON_ComponentStatus::AllSet; } for (unsigned short vfi = 0; vfi < vertex->m_face_count; vfi++) { const ON_SubDFace* face = vertex->Face(vfi); if (nullptr == face) continue; face->m_status = ON_ComponentStatus::AllSet; } } break; case ON_SubDComponentPtr::Type::Edge: { const ON_SubDEdge* edge = cptr.Edge(); if (nullptr == edge) continue; edge->m_status = ON_ComponentStatus::AllSet; for (unsigned short efi = 0; efi < edge->m_face_count; efi++) { const ON_SubDFace* face = edge->Face(efi); if (nullptr == face) continue; face->m_status = ON_ComponentStatus::AllSet; } } break; case ON_SubDComponentPtr::Type::Face: { const ON_SubDFace* face = cptr.Face(); if (nullptr == face) continue; face->m_status = ON_ComponentStatus::AllSet; } break; } } // Minimum count of what will be deleted. ( unsigned int deleted_vertex_count = 0; unsigned int deleted_edge_count = 0; unsigned int deleted_face_count = 0; for (ON_SubDComponentPtr cptr = cit.FirstComponent(); cptr.IsNotNull(); cptr = cit.NextComponent()) { if (ON_ComponentStatus::AllSet == cptr.Status()) { switch (cptr.ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: deleted_vertex_count++; break; case ON_SubDComponentPtr::Type::Edge: deleted_edge_count++; break; case ON_SubDComponentPtr::Type::Face: deleted_face_count++; break; } continue; } } if ( 0 == deleted_vertex_count && 0 == deleted_edge_count && 0 == deleted_face_count ) return false; const bool bDestroy = deleted_vertex_count >= level->m_vertex_count || deleted_edge_count >= level->m_edge_count || (deleted_face_count >= level->m_face_count && bDeleteIsolatedEdges) ; if (bDestroy) { Destroy(); return true; } unsigned int deleted_component_count = subdimple->DeleteComponents(level_index,bDeleteIsolatedEdges,bUpdateTagsAndCoefficients,bMarkDeletedFaceEdges); if (0 == subdimple->LevelCount()) { Destroy(); return true; } if (deleted_component_count > 0 || level_index > 0) { // remove lower levels subdimple->ClearLowerSubdivisionLevels(level_index); } return (deleted_component_count > 0); } bool ON_SubD::DeleteMarkedComponents( bool bDeleteMarkedComponents, ON__UINT8 mark_bits, bool bMarkDeletedFaceEdges ) { ON_SimpleArray cptr_list; GetMarkedComponents(bDeleteMarkedComponents, mark_bits, true, true, true, cptr_list); return DeleteComponents( cptr_list.Array(), cptr_list.UnsignedCount(), bMarkDeletedFaceEdges ); } bool ON_SubD::DeleteMarkedComponentsForExperts( bool bDeleteMarkedComponents, ON__UINT8 mark_bits, bool bDeleteIsolatedEdges, bool bUpdateTagsAndCoefficients, bool bMarkDeletedFaceEdges ) { ON_SimpleArray cptr_list; GetMarkedComponents(bDeleteMarkedComponents, mark_bits, true, true, true, cptr_list); return DeleteComponentsForExperts( cptr_list.Array(), cptr_list.UnsignedCount(), bDeleteIsolatedEdges, bUpdateTagsAndCoefficients, bMarkDeletedFaceEdges ); } unsigned int ON_SubDLevel::UpdateEdgeTags( bool bUnsetEdgeTagsOnly ) { // Update edge flags and sector weights. unsigned int edge_change_count = 0; ON_SubDEdge* next_edge = m_edge[0]; for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge) { next_edge = const_cast(edge->m_next_edge); if (2 != edge->m_face_count && edge->IsSmooth()) { // Dale Lear - Added April 5, 2021 - don't tolerate obvious errors / oversights. edge->m_edge_tag = ON_SubDEdgeTag::Unset; } const ON_SubDEdgeTag edge_tag0 = edge->m_edge_tag; if (bUnsetEdgeTagsOnly && ON_SubDEdgeTag::Unset != edge_tag0 ) { continue; } if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1]) { ON_SUBD_ERROR("nullptr edge->m_vertex[] values"); continue; } const double edge_sector_coefficient0[2] = { edge->m_sector_coefficient[0], edge->m_sector_coefficient[1] }; if (2 != edge->m_face_count) { edge->m_edge_tag = ON_SubDEdgeTag::Crease; edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; } else { edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; const bool bBothVertexTagsAreSet = 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) edge->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::IgnoredSectorCoefficient; switch (edge_tag0) { case ON_SubDEdgeTag::Unset: if (2 == tagged_end_index) { edge->m_edge_tag = ON_SubDEdgeTag::SmoothX; } else if ( bBothVertexTagsAreSet ) { edge->m_edge_tag = ON_SubDEdgeTag::Smooth; if (3 == tagged_end_index) { edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; } } break; case ON_SubDEdgeTag::Smooth: if (2 == tagged_end_index) { edge->m_edge_tag = ON_SubDEdgeTag::SmoothX; } else if (3 == tagged_end_index && bBothVertexTagsAreSet) { edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; } break; case ON_SubDEdgeTag::Crease: edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; break; //case ON_SubDEdgeTag::Sharp: // ON_SUBD_ERROR("ON_SubDEdgeTag::Sharp is not valid in this version of opennurbs."); // break; case ON_SubDEdgeTag::SmoothX: if ( 2 != tagged_end_index && bBothVertexTagsAreSet) edge->m_edge_tag = ON_SubDEdgeTag::Smooth; break; default: break; } } if (!(edge_tag0 == edge->m_edge_tag && edge_sector_coefficient0[0] == edge->m_sector_coefficient[0] && edge_sector_coefficient0[1] == edge->m_sector_coefficient[1])) edge_change_count++; } return edge_change_count; } unsigned int ON_SubDLevel::UpdateVertexTags( bool bUnsetVertexTagsOnly ) { // Update edge flags and sector weights. unsigned int vertex_change_count = 0; ON_SubDVertex* next_vertex = m_vertex[0]; for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex) { next_vertex = const_cast(vertex->m_next_vertex); const ON_SubDVertexTag vertex_tag0 = vertex->m_vertex_tag; if (bUnsetVertexTagsOnly && ON_SubDVertexTag::Unset != vertex_tag0 ) { continue; } const ON_SubDVertexTag vertex_tag1 = vertex->SuggestedVertexTag(true, false); if (ON_SubDVertexTag::Unset == vertex_tag1) continue; if ( vertex_tag0 != vertex_tag1) { vertex->m_vertex_tag = vertex_tag1; vertex_change_count++; } } return vertex_change_count; } unsigned int ON_SubDLevel::UpdateAllTagsAndSectorCoefficients( bool bUnsetValuesOnly ) { unsigned int change_count = 0; bool bUpdateEdges = true; bool bUpdateVertices = true; for ( unsigned int it_count = 0; it_count < 8; it_count++) { const unsigned int edge_change_count = bUpdateEdges ? UpdateEdgeTags(bUnsetValuesOnly) : 0; bUpdateVertices = (edge_change_count > 0 || 0 == it_count); change_count += edge_change_count; const unsigned int vertex_change_count = bUpdateVertices ? UpdateVertexTags(bUnsetValuesOnly) : 0; bUpdateEdges = (vertex_change_count > 0); change_count += vertex_change_count; bUpdateVertices = false; if ( false == bUpdateEdges) break; } if (bUpdateVertices && bUpdateEdges) { ON_SUBD_ERROR("Recursion limit exceeded."); } // Adjust edge tag smooth/X settings // This must be done before UpdateEdgeSectorCoefficients(). // It is done between the heavy handed setting above so as not to disturb that delicate code. ON_SubDEdge* next_edge = m_edge[0]; for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge) { next_edge = const_cast(edge->m_next_edge); if (edge->IsSmooth()) { const ON_SubDEdgeTag etag = (2 == edge->TaggedEndIndex()) ? ON_SubDEdgeTag::SmoothX : ON_SubDEdgeTag::Smooth; if (etag != edge->m_edge_tag) { edge->m_edge_tag = etag; ++change_count; } } } change_count += UpdateEdgeSectorCoefficients(false); if (change_count > 0) { m_surface_mesh = ON_SubDMesh::Empty; m_control_net_mesh = ON_SubDMesh::Empty; } return change_count; } unsigned int ON_SubDLevel::ClearComponentDamagedState() const { return ClearComponentDamagedState(true, true, true); } unsigned int ON_SubDLevel::ClearComponentDamagedState( bool bClearVerticesDamagedState, bool bClearEdgesDamagedState, bool bClearFacesDamagedState ) const { unsigned int change_count = 0; unsigned int i; if (bClearVerticesDamagedState) { i = 0; for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v && i++ < m_vertex_count; v = v->m_next_vertex) if (0 != v->m_status.SetDamagedState(false)) ++change_count; } if (bClearEdgesDamagedState) { i = 0; for (const ON_SubDEdge* e = m_edge[0]; nullptr != e && i++ < m_edge_count; e = e->m_next_edge) if (0 != e->m_status.SetDamagedState(false)) ++change_count; } if (bClearFacesDamagedState) { i = 0; for (const ON_SubDFace* f = m_face[0]; nullptr != f && i++ < m_face_count; f = f->m_next_face) if (0 != f->m_status.SetDamagedState(false)) ++change_count; } return change_count; } unsigned int ON_SubD::UpdateVertexTags( bool bUnsetVertexTagsOnly ) { ON_SubDLevel* level = ActiveLevelPointer(); if ( nullptr == level ) return ON_SUBD_RETURN_ERROR(0); return level->UpdateVertexTags(bUnsetVertexTagsOnly); } unsigned int ON_SubD::UpdateEdgeTags( bool bUnsetEdgeTagsOnly ) { ON_SubDLevel* level = ActiveLevelPointer(); if ( nullptr == level ) return ON_SUBD_RETURN_ERROR(0); return level->UpdateEdgeTags(bUnsetEdgeTagsOnly); } unsigned int ON_SubD::UpdateEdgeSectorCoefficients( bool bUnsetSectorCoefficientsOnly ) const { const ON_SubDLevel* level = ActiveLevelConstPointer(); if ( nullptr == level ) return ON_SUBD_RETURN_ERROR(0); return level->UpdateEdgeSectorCoefficients(bUnsetSectorCoefficientsOnly); } void ON_SubD::SubDModifiedNofification() { // DestroyRuntimeCache() // Clears // Saved subdivision points. // Saved limit surface information. // Bounding boxes. // DestroyRuntimeCache(); // This is a heavy handed tag update. UpdateAllTagsAndSectorCoefficients(false); } unsigned int ON_SubD::UpdateAllTagsAndSectorCoefficients( bool bUnsetValuesOnly ) { ON_SubDLevel* level = ActiveLevelPointer(); if ( nullptr == level ) return ON_SUBD_RETURN_ERROR(0); return level->UpdateAllTagsAndSectorCoefficients(bUnsetValuesOnly); } unsigned int ON_SubDimple::DeleteComponents( unsigned int level_index, bool bDeleteIsolatedEdges, bool bUpdateTagsAndCoefficients, bool bMarkDeletedFaceEdges ) { unsigned int deleted_component_count = 0; if (level_index >= m_levels.UnsignedCount()) return ON_SUBD_RETURN_ERROR(0); ON_SubDLevel* level = m_levels[level_index]; if (nullptr == level) return ON_SUBD_RETURN_ERROR(0); if (bMarkDeletedFaceEdges) level->ClearRuntimeMarks(false,true,false); ON_SubDFace* next_face = level->m_face[0]; for (ON_SubDFace* face = next_face; nullptr != face; face = next_face) { next_face = const_cast< ON_SubDFace* >(face->m_next_face); bool bDelete = (ON_ComponentStatus::AllSet == face->m_status || 0 == face->m_edge_count); if (false == bDelete) { const ON_SubDEdgePtr* eptr = face->m_edge4; for (unsigned short fei = 0; fei < face->m_edge_count && false == bDelete; ++fei, ++eptr) { if (4 == fei) { eptr = face->m_edgex; if (nullptr == eptr) break; } const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr == edge || nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1] || ON_ComponentStatus::AllSet == edge->m_status || ON_ComponentStatus::AllSet == edge->m_vertex[0]->m_status || ON_ComponentStatus::AllSet == edge->m_vertex[1]->m_status ) { bDelete = true; if (nullptr != edge && ON_ComponentStatus::AllSet != edge->m_status) edge->m_status = ON_ComponentStatus::AllSet; } } if (false == bDelete) continue; } if (bMarkDeletedFaceEdges) { // Set runtime mark on face's boundary edges. const ON_SubDEdgePtr* eptr = face->m_edge4; for (unsigned short fei = 0; fei < face->m_edge_count ; ++fei, ++eptr) { if (4 == fei) { eptr = face->m_edgex; if (nullptr == eptr) break; } const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr != edge) edge->m_status.SetRuntimeMark(); } } level->RemoveFace(face); m_heap.ReturnFace(face); deleted_component_count++; } ON_ComponentStatus allsetcheck; ON_SubDEdge* next_edge = level->m_edge[0]; for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge) { next_edge = const_cast< ON_SubDEdge* >(edge->m_next_edge); allsetcheck = ON_ComponentStatus::LogicalAnd(ON_ComponentStatus::AllSet, edge->m_status); bool bDelete = (ON_ComponentStatus::AllSet == allsetcheck || (bDeleteIsolatedEdges && 0 == edge->m_face_count) ); if (false == bDelete) { for (unsigned short evi = 0; evi < 2 && false == bDelete; evi++) { if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1] || ON_ComponentStatus::AllSet == edge->m_vertex[0]->m_status || ON_ComponentStatus::AllSet == edge->m_vertex[1]->m_status ) bDelete = true; } if (false == bDelete) continue; } level->RemoveEdge(edge); m_heap.ReturnEdge(edge); deleted_component_count++; } ON_SubDVertex* next_vertex = level->m_vertex[0]; for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex) { next_vertex = const_cast(vertex->m_next_vertex); allsetcheck = ON_ComponentStatus::LogicalAnd(ON_ComponentStatus::AllSet, vertex->m_status); bool bDelete = (ON_ComponentStatus::AllSet == allsetcheck || (bDeleteIsolatedEdges && 0 == vertex->m_face_count) || 0 == vertex->m_edge_count ); if ( false == bDelete ) continue; level->RemoveVertex(vertex); m_heap.ReturnVertex(vertex); deleted_component_count++; } if ( 0 == deleted_component_count ) return 0; // Remove edge references to deleted faces next_edge = level->m_edge[0]; for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge) { next_edge = const_cast(edge->m_next_edge); ON_SubDFacePtr* fptr0 = edge->m_face2; ON_SubDFacePtr* fptr1 = edge->m_face2; const unsigned short edge_face_count = edge->m_face_count; edge->m_face_count = 0; for (unsigned short efi = 0; efi < edge_face_count; efi++, fptr0++) { if (2 == efi) fptr0 = edge->m_facex; const ON_SubDFace* face = fptr0->Face(); if (nullptr == face || ON_UNSET_UINT_INDEX == face->ArchiveId()) continue; *fptr1++ = *fptr0; edge->m_face_count++; if (2 == edge->m_face_count) fptr1 = edge->m_facex; } if (0 == edge->m_face_count && bDeleteIsolatedEdges) { level->RemoveEdge(edge); m_heap.ReturnEdge(edge); deleted_component_count++; continue; } if (edge->m_face_count <= 2 && nullptr != edge->m_facex) m_heap.ReturnEdgeExtraArray(edge); if (bUpdateTagsAndCoefficients) { if (edge->m_face_count != 2) edge->m_edge_tag = ON_SubDEdgeTag::Crease; edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; } } // Remove vertex references to deleted edges and faces next_vertex = level->m_vertex[0]; for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex) { next_vertex = const_cast(vertex->m_next_vertex); unsigned int count = vertex->m_edge_count; vertex->m_edge_count = 0; bool bInteriorVertex = true; unsigned int crease_count = 0; for (unsigned short vei = 0; vei < count; vei++) { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); if (nullptr == edge || ON_UNSET_UINT_INDEX == edge->ArchiveId()) { bInteriorVertex = false; continue; } if (edge->IsCrease()) crease_count++; if (2 != edge->m_face_count) bInteriorVertex = false; vertex->m_edges[vertex->m_edge_count++] = vertex->m_edges[vei]; } count = vertex->m_face_count; vertex->m_face_count = 0; for (unsigned short vfi = 0; vfi < count; vfi++) { const ON_SubDFace* face = vertex->m_faces[vfi]; if (nullptr == face || ON_UNSET_UINT_INDEX == face->ArchiveId()) continue; vertex->m_faces[vertex->m_face_count++] = vertex->m_faces[vfi]; } if (0 == vertex->m_face_count && 0 == vertex->m_edge_count) { level->RemoveVertex(vertex); m_heap.ReturnVertex(vertex); deleted_component_count++; } else { if (1 == crease_count && 1 == vertex->m_edge_count && 0 == vertex->m_face_count) vertex->m_vertex_tag = ON_SubDVertexTag::Corner; else if (crease_count > 2) vertex->m_vertex_tag = ON_SubDVertexTag::Corner; else if (false == bInteriorVertex || crease_count > 1) { if (false == vertex->IsCreaseOrCorner()) vertex->m_vertex_tag = ON_SubDVertexTag::Crease; } } } if (0 == level->m_vertex_count || 0 == level->m_edge_count || (bDeleteIsolatedEdges && 0 == level->m_face_count)) { Destroy(); } else { // remove all information that is no longer valid level->MarkAggregateComponentStatusAsNotCurrent(); level->ClearEvaluationCache(); ClearHigherSubdivisionLevels(level_index); if (bUpdateTagsAndCoefficients) { // Update vertex tags, edge tags, and sector weights. level->UpdateAllTagsAndSectorCoefficients(false); } } ChangeGeometryContentSerialNumber(false); return deleted_component_count; } unsigned int ON_SubD::ClearComponentMarks() const { return ClearComponentMarks(true, true, true, nullptr); } unsigned int ON_SubD::ClearVertexMarks() const { return ClearComponentMarks(true, false, false, nullptr); } unsigned int ON_SubD::ClearEdgeMarks() const { return ClearComponentMarks(false, true, false, nullptr); } unsigned int ON_SubD::ClearFaceMarks() const { return ClearComponentMarks(false, false, true, nullptr); } unsigned int ON_SubD::ClearComponentMarks( bool bClearVertexMarks, bool bClearEdgeMarks, bool bClearFaceMarks, ON_SimpleArray< const ON_SubDComponentBase* >* marked_component_list ) const { unsigned int clear_count = 0; if (bClearVertexMarks) { ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { if (v->m_status.ClearRuntimeMark()) { if (nullptr != marked_component_list) marked_component_list->Append(v); clear_count++; } } } if (bClearEdgeMarks) { ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { if (e->m_status.ClearRuntimeMark()) { if (nullptr != marked_component_list) marked_component_list->Append(e); clear_count++; } } } if (bClearFaceMarks) { ON_SubDFaceIterator fit(*this); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { if (f->m_status.ClearRuntimeMark()) { if (nullptr != marked_component_list) marked_component_list->Append(f); clear_count++; } } } return clear_count; } unsigned int ON_SubD::ClearGroupIds() const { return ClearComponentGroupIds(true, true, true); } unsigned int ON_SubD::ClearVertexGroupIds() const { return ClearComponentGroupIds(true, false, false); } unsigned int ON_SubD::ClearEdgeGroupIds() const { return ClearComponentGroupIds(false, true, false); } unsigned int ON_SubD::ClearFaceGroupIds() const { return ClearComponentGroupIds(false, false, true); } unsigned int ON_SubD::ClearComponentGroupIds( bool bClearVertexGroupIds, bool bClearEdgeGroupIds, bool bClearFaceGroupIds ) const { unsigned int clear_count = 0; if (bClearVertexGroupIds) { ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { if (0 != v->m_group_id) { v->m_group_id = 0; clear_count++; } } } if (bClearEdgeGroupIds) { ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { if (0 != e->m_group_id) { e->m_group_id = 0; clear_count++; } } } if (bClearFaceGroupIds) { ON_SubDFaceIterator fit(*this); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { if (0 != f->m_group_id) { f->m_group_id = 0; clear_count++; } } } return clear_count; } unsigned int ON_SubD::ClearMarkBits() const { return ClearComponentMarkBits(true, true, true); } unsigned int ON_SubD::ClearVertexMarkBits() const { return ClearComponentMarkBits(true, false, false); } unsigned int ON_SubD::ClearEdgeMarkBits() const { return ClearComponentMarkBits(false, true, false); } unsigned int ON_SubD::ClearFaceMarkBits() const { return ClearComponentMarkBits(false, false, true); } unsigned int ON_SubD::ClearComponentMarkBits( bool bClearVertexMarkBits, bool bClearEdgeMarkBits, bool bClearFaceMarkBits ) const { unsigned int clear_count = 0; if (bClearVertexMarkBits) { ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { if (0 != v->m_status.MarkBits()) { v->m_status.SetMarkBits(0); clear_count++; } } } if (bClearEdgeMarkBits) { ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { if (0 != e->m_status.MarkBits()) { e->m_status.SetMarkBits(0); clear_count++; } } } if (bClearFaceMarkBits) { ON_SubDFaceIterator fit(*this); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { if (0 != f->m_status.MarkBits()) { f->m_status.SetMarkBits(0); clear_count++; } } } return clear_count; } unsigned int ON_SubD::UnselectComponents( bool bUnselectAllVertices, bool bUnselectAllEdges, bool bUnselectAllFaces ) const { unsigned int unselected_count = 0; if (bUnselectAllVertices) { ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { if (v->m_status.SetSelectedState(false, false, false)) ++unselected_count; } } if (bUnselectAllEdges) { ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { if (e->m_status.SetSelectedState(false, false, false)) ++unselected_count; } } if (bUnselectAllFaces) { ON_SubDFaceIterator fit(*this); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { if (f->m_status.SetSelectedState(false, false, false)) ++unselected_count; } } return unselected_count; } unsigned int ON_SubD::SetComponentMarks( bool bClearBeforeSet, const ON_SimpleArray< const ON_SubDComponentBase* >& marked_component_list ) const { unsigned int set_count = 0; if (bClearBeforeSet) ClearComponentMarks(true, true, true, nullptr); const unsigned count = marked_component_list.Count(); if (count <= 0) return 0; const ON_SubDComponentBase*const* a = marked_component_list.Array(); if (nullptr == a) return 0; for (const ON_SubDComponentBase*const* a1 = a + count; a < a1; a++) { const ON_SubDComponentBase* c = *a; if (nullptr == c) continue; if (c->m_status.SetRuntimeMark()) set_count++; } return set_count; } unsigned int ON_SubD::GetMarkedComponents( bool bAddMarkedComponents, ON__UINT8 mark_bits, bool bIncludeVertices, bool bIncludeEdges, bool bIncludeFaces, ON_SimpleArray< ON_SubDComponentPtr >& component_list ) const { bAddMarkedComponents = bAddMarkedComponents ? true : false; // so we can use == on boolean values unsigned int mark_count = 0; if (bIncludeVertices) { ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { if (bAddMarkedComponents == v->m_status.IsMarked(mark_bits)) { component_list.Append(v->ComponentPtr()); mark_count++; } } } if (bIncludeEdges) { ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { if (bAddMarkedComponents == e->m_status.IsMarked(mark_bits)) { component_list.Append(e->ComponentPtr()); mark_count++; } } } if (bIncludeFaces) { ON_SubDFaceIterator fit(*this); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { if (bAddMarkedComponents == f->m_status.IsMarked(mark_bits)) { component_list.Append(f->ComponentPtr()); mark_count++; } } } return mark_count; } unsigned int ON_SubD::GetMarkedComponents( bool bIncludeVertices, bool bIncludeEdges, bool bIncludeFaces, ON_SimpleArray< const ON_SubDComponentBase* >& marked_component_list ) const { unsigned int mark_count = 0; if (bIncludeVertices) { ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { if (v->m_status.RuntimeMark()) { marked_component_list.Append(v); mark_count++; } } } if (bIncludeEdges) { ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { if (e->m_status.RuntimeMark()) { marked_component_list.Append(e); mark_count++; } } } if (bIncludeFaces) { ON_SubDFaceIterator fit(*this); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { if (f->m_status.RuntimeMark()) { marked_component_list.Append(f); mark_count++; } } } return mark_count; } unsigned int ON_SubD::GetComponentStatus( bool bGetVertexStatus, bool bGetEdgeStatus, bool bGetFaceStatus, bool bClearStatus, ON_ComponentStatus status_mask, ON_SimpleArray< const class ON_SubDComponentBase* >& component_list, ON_SimpleArray< ON_ComponentStatus >& status_list ) const { component_list.SetCount(0); status_list.SetCount(0); if ( ON_ComponentStatus::NoneSet == status_mask ) return 0; ON_ComponentStatus s; if (bGetVertexStatus) { ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { s = ON_ComponentStatus::LogicalAnd(status_mask, v->m_status); if (ON_ComponentStatus::NoneSet == s) continue; component_list.Append(v); status_list.Append(s); } } if (bGetEdgeStatus) { ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { s = ON_ComponentStatus::LogicalAnd(status_mask, e->m_status); if (ON_ComponentStatus::NoneSet == s) continue; component_list.Append(e); status_list.Append(s); } } if (bGetFaceStatus) { ON_SubDFaceIterator fit(*this); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { s = ON_ComponentStatus::LogicalAnd(status_mask, f->m_status); if (ON_ComponentStatus::NoneSet == s) continue; component_list.Append(f); status_list.Append(s); } } const unsigned int count = component_list.UnsignedCount(); if (bClearStatus && count > 0) { const bool bRuntimeMark = status_mask.RuntimeMark(); for (unsigned int i = 0; i < count; ++i) { const ON_SubDComponentBase* c = component_list[i]; if (nullptr == c) continue; c->m_status.ClearStates(status_mask); if (bRuntimeMark) c->m_status.ClearRuntimeMark(); } } return count; } unsigned int ON_SubD::SetComponentStatus( ON_ComponentStatus status_mask, const ON_SimpleArray< const class ON_SubDComponentBase* >& component_list, const ON_SimpleArray< ON_ComponentStatus >& status_list ) const { const unsigned int count = component_list.UnsignedCount(); if (count < 1 || count != status_list.UnsignedCount()) return 0; const bool bRuntimeMark = status_mask.RuntimeMark(); for (unsigned int i = 0; i < count; ++i) { const ON_SubDComponentBase* c = component_list[i]; if (nullptr == c) continue; const ON_ComponentStatus s = status_list[i]; c->m_status.ClearStates(status_mask); c->m_status.SetStates(s); if (bRuntimeMark) { if ( s.RuntimeMark()) c->m_status.SetRuntimeMark(); else c->m_status.ClearRuntimeMark(); } } return count; } ON_SubDComponentMarksClearAndRestore::ON_SubDComponentMarksClearAndRestore( const ON_SubD& subd ) { m_subd.ShareDimple(subd); m_subd.ClearComponentMarks(true, true, true, &m_component_list); } ON_SubDComponentMarksClearAndRestore::~ON_SubDComponentMarksClearAndRestore() { Restore(true); } const ON_SimpleArray& ON_SubDComponentMarksClearAndRestore::ComponentList() const { return m_component_list; } bool ON_SubDComponentMarksClearAndRestore::Restore( bool bDisableFutureRestore ) { const bool rc = m_bRestore; if (rc) { if ( bDisableFutureRestore) m_bRestore = false; m_subd.ClearComponentMarks(true, true, true, nullptr); // if ( ON_ComponentStatus::Marked == m_status_mask ) { // RuntimeMark is the only bit being managed if (m_component_list.UnsignedCount() > 0) m_subd.SetComponentMarks(false, m_component_list); } //else if ( m_status_mask.IsNotClear() ) //{ // // something fancier is going on // // clear current settings // ON_SubDVertexIterator vit(m_subd); // for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) // v->m_status.ClearStates(m_status_mask); // ON_SubDEdgeIterator eit(m_subd); // for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) // e->m_status.ClearStates(m_status_mask); // ON_SubDFaceIterator fit(m_subd); // for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) // f->m_status.ClearStates(m_status_mask); // // restore settings // m_subd.SetComponentStatus(m_status_mask, m_component_list, m_status_list); //} if (bDisableFutureRestore) { m_component_list.Destroy(); //m_status_list.Destroy(); } } return rc; } void ON_SubDComponentMarksClearAndRestore::DisableRestore() { m_bRestore = false; } unsigned int ON_SubD::TransformComponents( const ON_Xform& xform, const ON_COMPONENT_INDEX* ci_list, size_t ci_count, ON_SubDComponentLocation component_location ) { if ( false == xform.IsValidAndNotZeroAndNotIdentity() || ci_count <= 0 || nullptr == ci_list ) return 0; ON_SimpleArray cptr_list; if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0) return true; // nothing to delete return TransformComponents(xform,cptr_list.Array(),cptr_list.UnsignedCount(),component_location); } static unsigned int Internal_MarkStuffAndMaybeMoveVertices( const ON_SubD& subd, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, const ON_Xform* xform, ON_SubDComponentLocation component_location, bool bExtrusionMarking, bool bExtrudeBoundaries, unsigned int& list_vertex_count, unsigned int& list_edge_count, unsigned int& list_face_count ) { list_vertex_count = 0; list_edge_count = 0; list_face_count = 0; if (false == bExtrusionMarking) bExtrudeBoundaries = false; const bool bTransform = false == bExtrusionMarking && nullptr != xform && xform->IsValidAndNotZeroAndNotIdentity() ; if ((bTransform ? 1 : 0) == (bExtrusionMarking ? 1 : 0)) { ON_SUBD_ERROR("Invalid input."); return 0; } unsigned int marked_vertex_count = 0; //unsigned int potential_isolated_vertex_count = 0; unsigned int potential_isolated_edge_count = 0; ON_SimpleArray moved_vertices; if (bExtrusionMarking && 0 == cptr_count && nullptr == cptr_list) { // entire subd is being extruded ON_SubDFaceIterator fit(subd); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { ++list_face_count; f->m_status.SetRuntimeMark(); const unsigned int face_vertex_count = f->m_edge_count; for (unsigned int fvi = 0; fvi < face_vertex_count; ++fvi) { // used when extruding selected components const ON_SubDEdge* e = f->Edge(fvi); if (nullptr != e && false == e->m_status.RuntimeMark()) e->m_status.SetRuntimeMark(); const ON_SubDVertex* v = f->Vertex(fvi); if (nullptr != v && false == v->m_status.RuntimeMark()) { v->m_status.SetRuntimeMark(); ++marked_vertex_count; } } } } else { for (size_t i = 0; i < cptr_count; i++) { switch (cptr_list[i].ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: { const ON_SubDVertex* v = cptr_list[i].Vertex(); if (nullptr == v) continue; ++list_vertex_count; if (v->m_status.RuntimeMark()) continue; if (bTransform) { v->m_status.SetRuntimeMark(); if (bTransform) { const_cast(v)->Transform(false, *xform); moved_vertices.Append(v->m_id); } ++marked_vertex_count; } } break; case ON_SubDComponentPtr::Type::Edge: { const ON_SubDEdge* e = cptr_list[i].Edge(); if (nullptr == e) continue; ++list_edge_count; if (e->Mark()) continue; if (bTransform) { e->SetMark(); for (unsigned int evi = 0; evi < 2; ++evi) { const ON_SubDVertex* v = e->m_vertex[evi]; if (nullptr != v && false == v->Mark()) { v->SetMark(); const_cast(v)->Transform(false, *xform); moved_vertices.Append(v->m_id); ++marked_vertex_count; } } } else { if (bExtrudeBoundaries && 1 == e->m_face_count && nullptr != e->m_face2[0].Face()) { // It's a boundary edge and we will mark it and its vertices later after we make sure // a face touching this edge isn't in the cptr_list[]. ++potential_isolated_edge_count; } // otherwise ignore interior edges } } break; case ON_SubDComponentPtr::Type::Face: { const ON_SubDFace* f = cptr_list[i].Face(); if (nullptr != f && false == f->Mark()) { ++list_face_count; f->SetMark(); const unsigned int face_vertex_count = f->m_edge_count; for (unsigned int fvi = 0; fvi < face_vertex_count; ++fvi) { if (bExtrusionMarking) { // used when extruding selected components const ON_SubDEdge* e = f->Edge(fvi); if (nullptr != e && false == e->m_status.RuntimeMark()) e->m_status.SetRuntimeMark(); } const ON_SubDVertex* v = f->Vertex(fvi); if (nullptr != v && false == v->m_status.RuntimeMark()) { v->m_status.SetRuntimeMark(); if (bTransform) { const_cast(v)->Transform(false, *xform); moved_vertices.Append(v->m_id); } ++marked_vertex_count; } } } } break; } } if (bExtrusionMarking && potential_isolated_edge_count > 0) { for (size_t i = 0; i < cptr_count; i++) { if (ON_SubDComponentPtr::Type::Edge != cptr_list[i].ComponentType()) continue; const ON_SubDEdge* e = cptr_list[i].Edge(); if (nullptr == e) continue; if (e->Mark()) continue; // this edge us part of a boundary belonging to a face in cptr_list[] if (1 == e->m_face_count && nullptr != e->m_face2[0].Face()) { // this boundary edge was explicitly picked its attached face was not picked. // It will be extruded to a face. e->SetMark(); for (unsigned int evi = 0; evi < 2; ++evi) { const ON_SubDVertex* v = e->m_vertex[evi]; if (nullptr != v && false == v->Mark()) { v->SetMark(); ++marked_vertex_count; } } if (0 == --potential_isolated_edge_count) break; } } } } const bool bSymmetryIsSet = false ; bool bChangePreservesSymmetry = false; if (bTransform) { if ( bSymmetryIsSet || 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(bChangePreservesSymmetry); } return marked_vertex_count; } static unsigned int Internal_MarkExtrudeComponents( const ON_SubD& subd, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, bool bExtrudeBoundaries, unsigned int& list_vertex_count, unsigned int& list_edge_count, unsigned int& list_face_count ) { const bool bExtrusionMarking = true; const int marked_vertex_count = Internal_MarkStuffAndMaybeMoveVertices( subd, cptr_list, cptr_count, nullptr, ON_SubDComponentLocation::Unset, bExtrusionMarking, bExtrudeBoundaries, list_vertex_count, list_edge_count, list_face_count ); // It appears the best "hurestic" is to require the user to pick edges and faces. // isolated vertices will be ignored and no attempts to guess if a user wants to // extrude the boundary of a face (all its edges) or the face itself. // lots of delete "hurestic" code here :) return marked_vertex_count; } static unsigned int Internal_TransformComponents( const ON_SubD& subd, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, const ON_Xform& xform, ON_SubDComponentLocation component_location ) { // This version is used by SDK tools that simply transform components in cptr_list if (false == xform.IsValidAndNotZeroAndNotIdentity()) return 0; const bool bExtrusionMarking = false; unsigned int list_vertex_count = 0; unsigned int list_edge_count = 0; unsigned int list_face_count = 0; return Internal_MarkStuffAndMaybeMoveVertices( subd, cptr_list, cptr_count, &xform, component_location, bExtrusionMarking, false, list_vertex_count, list_edge_count, list_face_count ); } unsigned int ON_SubD::TransformComponents( const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, ON_SubDComponentLocation component_location ) { if ( false == xform.IsValidAndNotZeroAndNotIdentity() || cptr_count <= 0 || nullptr == cptr_list ) return 0; ON_SimpleArray marked_components; const bool bRestoreMarks = ClearComponentMarks(true, true, true, &marked_components) > 0; const unsigned int v_count = Internal_TransformComponents(*this, cptr_list, cptr_count, xform, component_location); if (bRestoreMarks) SetComponentMarks(true, marked_components); return (v_count > 0); } unsigned int ON_SubD::ExtrudeComponents( const ON_Xform& xform, const ON_COMPONENT_INDEX* ci_list, size_t ci_count ) { const bool bExtrudeBoundaries = true; const bool bPermitNonManifoldEdgeCreation = false; return ExtrudeComponents(xform, ci_list, ci_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation); } unsigned int ON_SubD::ExtrudeComponents( const ON_Xform& xform, const ON_COMPONENT_INDEX* ci_list, size_t ci_count, bool bExtrudeBoundaries, bool bPermitNonManifoldEdgeCreation ) { if ( false == xform.IsValidAndNotZeroAndNotIdentity() || xform.IsIdentity() || ci_count <= 0 || nullptr == ci_list ) return 0; ON_SimpleArray cptr_list; if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0) return true; // nothing to extrude return ExtrudeComponents( xform, cptr_list.Array(), cptr_list.UnsignedCount(), bExtrudeBoundaries, bPermitNonManifoldEdgeCreation ); } /* ON_Internal_ExtrudedVertex manages a vertex that is extruded into a "side" edge during the extrusion process. */ class ON_Internal_ExtrudedVertex { public: ON_Internal_ExtrudedVertex() = default; ~ON_Internal_ExtrudedVertex() = default; ON_Internal_ExtrudedVertex(const ON_Internal_ExtrudedVertex&) = default; ON_Internal_ExtrudedVertex& operator=(const ON_Internal_ExtrudedVertex&) = default; static const ON_Internal_ExtrudedVertex Unset; ///////////////////////////////////////////////////////////// // // Up to 2 extruded edges that share this extruded vertex. // In rare cases there may be more. This information is used only // to speed up the setting of m_side_group_id. // class ON_Internal_ExtrudedEdge* m_extruded_edges[2] = {}; unsigned char m_extruded_edges_count = 0; // 0, 1, 2, or 3. 3 means 3 or more ///////////////////////////////////////////////////////////// // // State of the vertex before the extrusion was applied // // tag the original vertex had before the extrusion was performed. 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_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; //// The face counts do not include the side faces created during the extrusion process. //unsigned short m_moved_face_count = 0; //unsigned short m_stationary_face_count = 0; //// Edge counts do not include the side edge "m_connecting_edge" that is created during the extrusion process. //// There are three sets of edges //// Existing edges that will be extruded into side faces. //// Existing edges that will be moved during the extrusion process. //// Existing edges that will be stationary during the extrusion process. //unsigned short m_extruded_edge_count = 0; // these are edges in the new_sides[] array //unsigned short m_wire_edge_count = 0; // moved or stationary //unsigned short m_moved_crease_edge_count = 0; //unsigned short m_moved_smooth_edge_count = 0; //unsigned short m_stationary_crease_edge_count = 0; //unsigned short m_stationary_smooth_edge_count = 0; ///////////////////////////////////////////////////////////// // // Additional information that will be created or modified during the extrusion. // // Used to sort the sides into groups of edges that are connected unsigned m_side_group_id = 0; // This is the vertex in the original object. (This vertex is moved). ON_SubDVertex* m_original_vertex = nullptr; // This vertex is new. It is a copy of the original vertex and remains at the original location. ON_SubDVertex* m_copied_vertex = nullptr; // m_connecting_edge begins at m_copied_vertex and terminates at m_original_vertex; ON_SubDEdge* m_connecting_edge = nullptr; bool SetFromInitialVertex(ON_SubDVertex* initial_vertex) { *this = ON_Internal_ExtrudedVertex::Unset; if ( nullptr == initial_vertex || 0 == initial_vertex->m_id ) return false; // validate initial_vertex topology information so subsequent code assumptions are met. if (initial_vertex->m_edge_count <= 0) return false; if (initial_vertex->m_edge_count > initial_vertex->m_edge_capacity) return false; if (nullptr == initial_vertex->m_edges) return false; for (unsigned short vei = 0; vei < initial_vertex->m_edge_count; ++vei) { const ON_SubDEdgePtr eptr = initial_vertex->m_edges[vei]; const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr); if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) return false; if (e->m_vertex[0]->m_id == e->m_vertex[1]->m_id) return false; if (initial_vertex != e->m_vertex[ON_SUBD_EDGE_DIRECTION(eptr.m_ptr)]) return false; } if (initial_vertex->m_face_count > 0) { if (initial_vertex->m_face_count > initial_vertex->m_face_capacity) return false; if (nullptr == initial_vertex->m_faces) return false; } m_initial_vertex_tag = initial_vertex->m_vertex_tag; m_initial_vertex_id = initial_vertex->m_id; m_original_vertex = initial_vertex; return true; } bool SetConnectingEdgeTag(); bool ExtrudeVertex(ON_SubD& subd, bool bIsInset, const ON_Xform& xform) { if (nullptr != m_copied_vertex) return ON_SUBD_RETURN_ERROR(false); if (nullptr == m_original_vertex) return ON_SUBD_RETURN_ERROR(false); const ON_3dPoint P = m_original_vertex->ControlNetPoint(); for (;;) { // transform the original vertex m_original_vertex->m_vertex_tag = ON_SubDVertexTag::Unset; if (false == bIsInset) { if (false == m_original_vertex->Transform(false, xform)) break; } m_copied_vertex = subd.AddVertex(ON_SubDVertexTag::Unset, P); if (nullptr == m_copied_vertex) break; const unsigned short face_count = m_original_vertex->m_face_count; if (face_count > 0 && false == subd.GrowVertexFaceArray(m_copied_vertex, face_count)) break; if (false == subd.GrowVertexEdgeArray(m_copied_vertex, m_original_vertex->m_edge_count + 1)) break; // edge from m_copied_vertex (stationary) to m_original_vertex (moved). m_connecting_edge = subd.AddEdge(m_connecting_edge_tag, m_copied_vertex, m_original_vertex); if (nullptr == m_connecting_edge) break; //if (bMarkedInteriorCrease && bUnmarkedInteriorCrease) // m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Crease; for (unsigned short vei = 0; vei < m_original_vertex->m_edge_count; ++vei) { ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_original_vertex->m_edges[vei].m_ptr); if (nullptr != e) e->UnsetSectorCoefficientsForExperts(); } return true; } if (nullptr != m_copied_vertex) { ON_SubDComponentPtr cptr = m_copied_vertex->ComponentPtr(); subd.DeleteComponentsForExperts(&cptr, 1, false, false, false); m_copied_vertex = nullptr; } ON_SUBD_ERROR("Unable to extrude m_original_vertex"); m_original_vertex->m_vertex_tag = this->m_initial_vertex_tag; m_original_vertex->SetControlNetPoint(P,false); return false; } bool AttachUnmarkedFacesToCopiedVertex() { if (nullptr == m_copied_vertex) return ON_SUBD_RETURN_ERROR(false); if (0 != m_copied_vertex->m_face_count) return ON_SUBD_RETURN_ERROR(false); if (nullptr == m_original_vertex) return ON_SUBD_RETURN_ERROR(false); if (0 == m_original_vertex->m_face_count) return true; // wire edge case const unsigned short face_count = m_original_vertex->m_face_count; if (face_count > m_original_vertex->m_face_capacity) return ON_SUBD_RETURN_ERROR(false); if (face_count > m_copied_vertex->m_face_capacity) return ON_SUBD_RETURN_ERROR(false); // marked faces remain attached to m_original_vertex // unmarked faces are attached to m_copied_vertex m_original_vertex->m_face_count = 0; for (unsigned short vfi = 0; vfi < face_count; vfi++) { const ON_SubDFace* f = m_original_vertex->m_faces[vfi]; if (nullptr == f) continue; ON_SubDVertex* v = (f->Mark()) ? m_original_vertex : m_copied_vertex; v->m_faces[v->m_face_count] = f; v->m_face_count++; } for (unsigned short vfi = m_original_vertex->m_face_count; vfi < face_count; ++vfi) m_original_vertex->m_faces[vfi] = nullptr; return true; } void UndoAttachUnmarkedFacesToCopiedVertex() { // This is used to resort the subd to a somewhat valid state after a critical error occurs if (nullptr == m_copied_vertex) return; if (0 == m_copied_vertex->m_face_count) return; if (nullptr == m_original_vertex) return; // Move faces from m_copied_vertex back to m_original_vertex (which is where they started). for (unsigned short vfi = 0; vfi < m_copied_vertex->m_face_count; vfi++) { const ON_SubDFace* f = m_copied_vertex->m_faces[vfi]; if (nullptr == f) continue; m_copied_vertex->m_faces[vfi] = nullptr; if (m_original_vertex->m_face_count < m_original_vertex->m_face_capacity) { m_original_vertex->m_faces[m_original_vertex->m_face_count] = f; m_original_vertex->m_face_count++; } } m_copied_vertex->m_face_count = 0; } bool AddExtrudedEdgeReference( class ON_Internal_ExtrudedEdge* extruded_edge, bool bSetExtrudedEdgeToo ); static int CompareInitialVertexId( const ON_Internal_ExtrudedVertex* lhs, const ON_Internal_ExtrudedVertex* rhs ); void SetBothVertexTags(ON_SubDVertexTag vertex_tag) { if (nullptr != m_original_vertex) m_original_vertex->m_vertex_tag = vertex_tag; if (nullptr != m_copied_vertex) m_copied_vertex->m_vertex_tag = vertex_tag; if (nullptr != m_connecting_edge) { switch (vertex_tag) { case ON_SubDVertexTag::Unset: m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Unset; break; case ON_SubDVertexTag::Smooth: m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Smooth; break; case ON_SubDVertexTag::Crease: m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::SmoothX; break; case ON_SubDVertexTag::Corner: m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Crease; break; case ON_SubDVertexTag::Dart: m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::SmoothX; break; default: m_connecting_edge->m_edge_tag = ON_SubDEdgeTag::Unset; break; } } } bool IsValidTopology(bool bCheckCopies) const; }; const ON_Internal_ExtrudedVertex ON_Internal_ExtrudedVertex::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Internal_ExtrudedVertex); /* ON_Internal_ExtrudedVertex manages an edge that is extruded into a "side" face extrusion process. */ class ON_Internal_ExtrudedEdge { public: ON_Internal_ExtrudedEdge() = default; ~ON_Internal_ExtrudedEdge() = default; ON_Internal_ExtrudedEdge(const ON_Internal_ExtrudedEdge&) = default; ON_Internal_ExtrudedEdge& operator=(const ON_Internal_ExtrudedEdge&) = default; static const ON_Internal_ExtrudedEdge Unset; // tag m_original_edge had before the extrusion was performed. ON_SubDEdgeTag m_initial_edge_tag = ON_SubDEdgeTag::Unset; unsigned int m_initial_vertex_id[2] = {}; unsigned int m_initial_edge_face_count = 0; // Used to sort the sides into groups of edges that are connected unsigned m_side_group_id = 0; // This is the edge in the original object. (This edge is moved). ON_SubDEdge* m_original_edge = nullptr; // This edge is new. It is a copy of the original edge and remains at the original location. ON_SubDEdge* m_copied_edge = nullptr; // m_extruded_vertex[0]->m_connecting_edge begins at m_copied_edge->m_vertex[0] and terminates at m_original_edge->m_vertex[0] // m_extruded_vertex[1]->m_connecting_edge begins at m_copied_edge->m_vertex[1] and terminates at m_original_edge->m_vertex[1] ON_Internal_ExtrudedVertex* m_extruded_vertex[2] = {}; // This is the new "side" quad with boundary made from the 4 edges above. ON_SubDFace* m_new_face = nullptr; static int CompareSideGroupId(const ON_Internal_ExtrudedEdge* lhs, const ON_Internal_ExtrudedEdge* rhs); bool SetFromInitialEdge( ON_SubDEdge* initial_edge ) { *this = ON_Internal_ExtrudedEdge::Unset; if (nullptr == initial_edge) return false; if (nullptr == initial_edge->m_vertex[0] || nullptr == initial_edge->m_vertex[1] || initial_edge->m_vertex[0]->m_id == initial_edge->m_vertex[1]->m_id) return false; // validate edge / face topology information - corrupt information will cause great difficulties during the extrusion process const ON_SubDFacePtr* fptr = initial_edge->m_face2; for (unsigned short efi = 0; efi < initial_edge->m_face_count; ++efi, ++fptr) { if (2 == efi) { fptr = initial_edge->m_facex; if (nullptr == fptr) return false; // corrupt edge / face topology information if (initial_edge->m_facex_capacity < initial_edge->m_face_count - 2) return false; // corrupt edge / face topology information } const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr->m_ptr); if (nullptr == f || f->m_edge_count < 3) return false; // corrupt edge / face topology information bool bFoundInitialEdge = false; 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) return false; // corrupt edge / face topology information if (f->m_edgex_capacity < f->m_edge_count - 4) return false; // corrupt edge / face topology information } const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr == e) return false; if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] || e->m_vertex[0]->m_id == e->m_vertex[1]->m_id) return false; if (e == initial_edge) { if (bFoundInitialEdge) return false; bFoundInitialEdge = true; if (ON_SUBD_EDGE_DIRECTION(eptr->m_ptr) != ON_SUBD_FACE_DIRECTION(fptr->m_ptr)) return false; } } if (false == bFoundInitialEdge) return false; } m_initial_edge_tag = initial_edge->m_edge_tag; m_initial_vertex_id[0] = initial_edge->m_vertex[0]->m_id; m_initial_vertex_id[1] = initial_edge->m_vertex[1]->m_id; m_initial_edge_face_count = initial_edge->m_face_count; m_original_edge = initial_edge; return true; } ON_SubDFace* ExtrudeFace(ON_SubD& subd); const ON_Internal_ExtrudedVertex* ExtrudedVertex(ON__UINT_PTR evi) const { return (0 == evi || 1 == evi) ? m_extruded_vertex[evi] : nullptr; } /// Returns "side" edge created by extruding the reference vertex ON_SubDEdge* ConnectingEdge(ON__UINT_PTR evi) const { const ON_Internal_ExtrudedVertex* extruded_vertex = ExtrudedVertex(evi); return (nullptr != extruded_vertex) ? extruded_vertex->m_connecting_edge : nullptr; } ON_SubDEdge* CreateCopiedEdge(ON_SubD& subd) { if (nullptr != m_copied_edge) return ON_SUBD_RETURN_ERROR(nullptr); ON_SubDVertex* copied_vertex[2] = {}; for (unsigned evi = 0; evi < 2; evi++) { if (nullptr == m_extruded_vertex[evi]) return ON_SUBD_RETURN_ERROR(nullptr); copied_vertex[evi] = m_extruded_vertex[evi]->m_copied_vertex; if (nullptr == copied_vertex[evi]) return ON_SUBD_RETURN_ERROR(nullptr); } 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_SubDEdgeTag::Unset; return m_copied_edge; } /// returns true if an unset vertex pair had its id set which means recursive id setting should continue. bool SetSideGroupId(unsigned side_group_id) { if (m_side_group_id > 0) return false; bool rc = false; m_side_group_id = side_group_id; for (unsigned evi = 0; evi < 2; ++evi) { if (nullptr != m_extruded_vertex[evi] && 0 == m_extruded_vertex[evi]->m_side_group_id) { rc = true; m_extruded_vertex[evi]->m_side_group_id = side_group_id; } } return rc; } /// Returns true if an unset vertex pair had its id set which means recursive id setting should continue. bool SetSideGroupIdFromVertexPairs() { if (m_side_group_id > 0) return false; for (unsigned evi = 0; evi < 2; ++evi) { if (nullptr != m_extruded_vertex[evi] && m_extruded_vertex[evi]->m_side_group_id > 0) return SetSideGroupId(m_extruded_vertex[evi]->m_side_group_id); } return false; } bool InitialCrease() const { return (ON_SubDEdgeTag::Crease == m_initial_edge_tag); } bool InitialCreaseWithCorner() const { if (ON_SubDEdgeTag::Crease == m_initial_edge_tag) { for (unsigned evi = 0; evi < 2; ++evi) { if (nullptr != m_extruded_vertex[evi] && ON_SubDVertexTag::Corner == m_extruded_vertex[evi]->m_initial_vertex_tag) return true; } } return false; } void SetBothEdgeTags(ON_SubDEdgeTag edge_tag) { if (ON_SubDEdgeTag::Crease == edge_tag) { if (nullptr != m_original_edge) m_original_edge->m_edge_tag = ON_SubDEdgeTag::Crease; if (nullptr != m_copied_edge) m_copied_edge->m_edge_tag = ON_SubDEdgeTag::Crease; for (unsigned evi = 0; evi < 2; ++evi) { if (nullptr != m_extruded_vertex[evi] && ON_SubDEdgeTag::Unset == m_extruded_vertex[evi]->m_connecting_edge->m_edge_tag) { 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_SubDEdgeTag::Smooth; } } } } bool IsValidTopology( bool bCheckCopies ) const; }; static bool Internal_IsNotValidExtrudedTopology() { return ON_SUBD_RETURN_ERROR(false); } bool ON_Internal_ExtrudedVertex::IsValidTopology(bool bCheckCopies) const { if (nullptr == this->m_original_vertex) return Internal_IsNotValidExtrudedTopology(); if (this->m_initial_vertex_id != this->m_original_vertex->m_id) return Internal_IsNotValidExtrudedTopology(); if (this->m_extruded_edges_count == 0) return Internal_IsNotValidExtrudedTopology(); if (bCheckCopies && nullptr == this->m_copied_vertex) return Internal_IsNotValidExtrudedTopology(); for (unsigned vei = 0; vei < 2 && vei < this->m_extruded_edges_count; ++vei) { if (nullptr == this->m_extruded_edges[vei]) return Internal_IsNotValidExtrudedTopology(); const ON_Internal_ExtrudedEdge& extruded_edge = *this->m_extruded_edges[vei]; if (nullptr == extruded_edge.m_original_edge) return Internal_IsNotValidExtrudedTopology(); const unsigned evi = (this->m_original_vertex == extruded_edge.m_original_edge->m_vertex[0]) ? 0 : 1; if (this->m_initial_vertex_id != extruded_edge.m_initial_vertex_id[evi]) return Internal_IsNotValidExtrudedTopology(); for (unsigned k = 0; k < (bCheckCopies ? 2U : 1U); ++k) { const ON_SubDEdge* e = (0 == k) ? extruded_edge.m_original_edge : extruded_edge.m_copied_edge; if (nullptr == e) return Internal_IsNotValidExtrudedTopology(); if (e->m_vertex[0] == e->m_vertex[1]) return Internal_IsNotValidExtrudedTopology(); const ON_SubDVertex* v = (0 == k) ? this->m_original_vertex : this->m_copied_vertex; if (nullptr == v) return Internal_IsNotValidExtrudedTopology(); if (e->m_vertex[evi] != v) return Internal_IsNotValidExtrudedTopology(); } if (extruded_edge.m_side_group_id != this->m_side_group_id) return Internal_IsNotValidExtrudedTopology(); } return true; } bool ON_Internal_ExtrudedEdge::IsValidTopology( bool bCheckCopies ) const { if (nullptr == m_original_edge) return Internal_IsNotValidExtrudedTopology(); if (bCheckCopies && nullptr == m_copied_edge) return Internal_IsNotValidExtrudedTopology(); for (unsigned evi = 0; evi < 2; ++evi) { if (nullptr == this->m_extruded_vertex[evi]) return Internal_IsNotValidExtrudedTopology(); const ON_Internal_ExtrudedVertex& extruded_vertex = *this->m_extruded_vertex[evi]; for (unsigned k = 0; k < (bCheckCopies ? 2U : 1U); ++k) { const ON_SubDEdge* e = (0 == k) ? this->m_original_edge : this->m_copied_edge; if (nullptr == e) return Internal_IsNotValidExtrudedTopology(); const ON_SubDVertex* v = (0 == k) ? extruded_vertex.m_original_vertex : extruded_vertex.m_copied_vertex; if (nullptr == v) return Internal_IsNotValidExtrudedTopology(); if (e->m_vertex[evi] != v) return Internal_IsNotValidExtrudedTopology(); if (v->EdgeArrayIndex(e) >= v->m_edge_count) return Internal_IsNotValidExtrudedTopology(); } if (extruded_vertex.m_initial_vertex_id != extruded_vertex.m_original_vertex->m_id) return Internal_IsNotValidExtrudedTopology(); if (extruded_vertex.m_initial_vertex_id != this->m_initial_vertex_id[evi]) return Internal_IsNotValidExtrudedTopology(); if (this->m_side_group_id != extruded_vertex.m_side_group_id) return Internal_IsNotValidExtrudedTopology(); } return true; } bool ON_Internal_ExtrudedVertex::AddExtrudedEdgeReference( class ON_Internal_ExtrudedEdge* extruded_edge, bool bSetExtrudedEdgeToo ) { if (nullptr == m_original_vertex || 0 == m_initial_vertex_id || m_initial_vertex_id != m_original_vertex->m_id) return ON_SUBD_RETURN_ERROR(false); if (nullptr == extruded_edge || nullptr == extruded_edge->m_original_edge ) return ON_SUBD_RETURN_ERROR(false); unsigned int evi; for (evi = 0; evi < 2; ++evi) { if (m_original_vertex == extruded_edge->m_original_edge->m_vertex[evi] && m_initial_vertex_id == extruded_edge->m_initial_vertex_id[evi]) break; } if (evi > 1) { // edge and vertex are not attached return ON_SUBD_RETURN_ERROR(false); } if ( nullptr != extruded_edge->m_extruded_vertex[evi] && this != extruded_edge->m_extruded_vertex[evi]) return ON_SUBD_RETURN_ERROR(false); switch (m_extruded_edges_count) { case 0: m_extruded_edges[0] = extruded_edge; m_extruded_edges_count = 1; break; case 1: if (m_extruded_edges[0] != extruded_edge) { m_extruded_edges[1] = extruded_edge; m_extruded_edges_count = 2; } break; case 2: if (m_extruded_edges[0] != extruded_edge && m_extruded_edges[1] != extruded_edge) m_extruded_edges_count = 3; // rare case with 3 or more extruded edges sharing an extruded vertex break; case 3: // do nothing this is vertex will be extruded into a non-manifold "side" connecting edge break; default: // invalid value of m_extruded_edges_count return ON_SUBD_RETURN_ERROR(false); break; } if (bSetExtrudedEdgeToo && nullptr == extruded_edge->m_extruded_vertex[evi]) extruded_edge->m_extruded_vertex[evi] = this; return true; } bool ON_Internal_ExtrudedVertex::SetConnectingEdgeTag() { if (nullptr == m_original_vertex || 0 == m_initial_vertex_id) return ON_SUBD_RETURN_ERROR(false); // to early 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_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_SubDEdgeTag::Crease; return true; } //if (ON_SubDVertexTag::Corner == m_initial_vertex_tag) //{ // // simple case // m_connecting_edge_tag = ON_SubDEdgeTag::Crease; // return true; //} 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 bool bMovedCrease = false; bool bStationaryCrease = false; const ON_SubDEdge* extruded_edge[2] = {}; for (unsigned vei = 0; vei < 2; ++vei) { extruded_edge[vei] = (nullptr != m_extruded_edges[vei]) ? m_extruded_edges[vei]->m_original_edge : nullptr; if (nullptr == extruded_edge[vei]) return ON_SUBD_RETURN_ERROR(false); // corrupt information if (1 == extruded_edge[vei]->m_face_count) { if (extruded_edge[vei]->MarkedFaceCount() > 0) bStationaryCrease = true; // the boundary (crease) will not move but the attached face does move else { // the boundary (crease) will move but the attached face does not move if (ON_SubDVertexTag::Corner == m_initial_vertex_tag) { m_connecting_edge_tag = ON_SubDEdgeTag::Crease; return true; } bMovedCrease = true; } } } // determine vertex topology and edge demographics 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 || false == e->IsCrease() || e == extruded_edge[0] || e == extruded_edge[1]) continue; if (e->MarkedFaceCount() > 0) bMovedCrease = true; else bStationaryCrease = true; } // 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_SubDEdgeTag::Crease; return true; } int ON_Internal_ExtrudedEdge::CompareSideGroupId(const ON_Internal_ExtrudedEdge* lhs, const ON_Internal_ExtrudedEdge* rhs) { const unsigned lhs_side_group_id = lhs->m_side_group_id; const unsigned rhs_side_group_id = rhs->m_side_group_id; if (lhs_side_group_id < rhs_side_group_id) return -1; if (lhs_side_group_id > rhs_side_group_id) return 1; return 0; } const ON_Internal_ExtrudedEdge ON_Internal_ExtrudedEdge::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Internal_ExtrudedEdge); int ON_Internal_ExtrudedVertex::CompareInitialVertexId( const ON_Internal_ExtrudedVertex* lhs, const ON_Internal_ExtrudedVertex* rhs ) { const unsigned int lhs_id = lhs->m_initial_vertex_id; const unsigned int rhs_id = rhs->m_initial_vertex_id; if (lhs_id < rhs_id) return -1; if (lhs_id > rhs_id) return 1; return 0; } static void Internal_SetEdgeVertices( ON_SubD& subd, ON_Internal_ExtrudedVertex& extruded_vertex ) { // marked edges use the marked vertex. ON_SubDVertex* marked_vertex = extruded_vertex.m_original_vertex; ON_SubDVertex* unmarked_vertex = extruded_vertex.m_copied_vertex; const unsigned int vertex_edge_count = marked_vertex->EdgeCount(); unsigned int marked_edge_count = 0; unsigned int unmarked_edge_count = 0; unsigned int new_edge_count = 0; for (unsigned int vei = 0; vei < vertex_edge_count; vei++) { ON_SubDEdgePtr eptr = marked_vertex->m_edges[vei]; const ON_SubDEdge* e = eptr.Edge(); if (nullptr == e) continue; if (extruded_vertex.m_connecting_edge == e) new_edge_count++; else if (e->m_status.RuntimeMark()) marked_edge_count++; else unmarked_edge_count++; } if (unmarked_edge_count <= 0) return; unmarked_edge_count += unmarked_vertex->m_edge_count; if ( unmarked_vertex->m_edge_capacity < (unmarked_edge_count+new_edge_count) ) subd.GrowVertexEdgeArray(unmarked_vertex, unmarked_edge_count); marked_vertex->m_edge_count = 0; for (unsigned int vei = 0; vei < vertex_edge_count; vei++) { ON_SubDEdgePtr eptr = marked_vertex->m_edges[vei]; ON_SubDEdge* e = eptr.Edge(); if (nullptr == e) continue; if (extruded_vertex.m_connecting_edge == e || e->m_status.RuntimeMark()) { marked_vertex->m_edges[marked_vertex->m_edge_count] = eptr; marked_vertex->m_edge_count++; } else { if (e->m_vertex[0] == marked_vertex) e->m_vertex[0] = unmarked_vertex; else if (e->m_vertex[1] == marked_vertex) e->m_vertex[1] = unmarked_vertex; unmarked_vertex->m_edges[unmarked_vertex->m_edge_count] = eptr; unmarked_vertex->m_edge_count++; } } } ON_SubDFace* ON_Internal_ExtrudedEdge::ExtrudeFace( ON_SubD& subd ) { // All components that will be moved have the runtime mark set. // All other components have a clear runtime mark. // The original_edge will be moved. // The new_edge will not be moved. // new edge and original edge go the same direction. // new_side_edges[2] run from new to original edges. // change edges of unmarked faces to use the new edge ON__UINT_PTR edir = 0; ON_SubDEdge* marked_edge = this->m_original_edge; // will be moved ON_SubDEdge* unmarked_edge = this->m_copied_edge; // a copy of the original edge at the original location unsigned int marked_edge_face_count0 = marked_edge->m_face_count; ON_SubDFacePtr* marked_edge_fptr1 = marked_edge->m_face2; const ON_SubDFacePtr* marked_edge_fptr0 = marked_edge_fptr1; unsigned int marked_edge_face_count1 = 0; subd.GrowEdgeFaceArray(unmarked_edge, marked_edge_face_count0); for (unsigned int efi = 0; efi < marked_edge_face_count0; efi++, marked_edge_fptr0++) { if (2 == efi) marked_edge_fptr0 = marked_edge->m_facex; if (2 == marked_edge_face_count1) marked_edge_fptr1 = marked_edge->m_facex; ON_SubDFace* f = marked_edge_fptr0->Face(); if (nullptr == f) { ON_SUBD_ERROR("null face pointer"); continue; } if (f->m_status.RuntimeMark()) { edir = marked_edge_fptr0->FaceDirection(); marked_edge_face_count1++; *marked_edge_fptr1 = *marked_edge_fptr0; marked_edge_fptr1++; continue; // this face will be moved and keeps edge e1 } // f is unmarked. // change referenced edge from marked_edge to unmarked_edge. f->ReplaceEdgeInArray(0, marked_edge, unmarked_edge); unmarked_edge->AddFaceToArray(*marked_edge_fptr0); } // When marked_edge is a manifold edge, face_count goes from 2 to 1. marked_edge->m_face_count = static_cast(marked_edge_face_count1); ON_SubDEdge* side0 = this->ConnectingEdge(edir); ON_SubDEdge* side1 = this->ConnectingEdge(1 - edir); ON_SubDEdgePtr new_face_eptr[4]; new_face_eptr[0] = ON_SubDEdgePtr::Create(this->m_original_edge, 1-edir); new_face_eptr[1] = ON_SubDEdgePtr::Create(side0, 1); new_face_eptr[2] = ON_SubDEdgePtr::Create(this->m_copied_edge, edir); new_face_eptr[3] = ON_SubDEdgePtr::Create(side1, 0); this->m_new_face = subd.AddFace(new_face_eptr, 4); if (nullptr != this->m_new_face) { // When isolated edges are extruded, we need to flip the face. // In all other cases, we don't. bool bFlip = false; bool bFlipSet = false; for (unsigned int fei = 0; fei < 4; fei++) { const ON_SubDEdgePtr eptr = this->m_new_face->m_edge4[fei]; const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr); if (nullptr == e || e->m_face_count > 2) { bFlipSet = false; break; } if (2 != e->m_face_count) continue; const ON__UINT_PTR fedir = ON_SUBD_EDGE_DIRECTION(eptr.m_ptr); const ON_SubDFacePtr fptr[2] = { e->m_face2[0], e->m_face2[1] }; const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(fptr[0].m_ptr), ON_SUBD_FACE_POINTER(fptr[1].m_ptr) }; if (nullptr == f[0] || nullptr == f[1] || f[0] == f[1]) { bFlipSet = false; break; } if (this->m_new_face != f[0] && this->m_new_face != f[1] ) { bFlipSet = false; break; } const ON__UINT_PTR fdir[2] = { ON_SUBD_FACE_DIRECTION(fptr[0].m_ptr), ON_SUBD_FACE_DIRECTION(fptr[1].m_ptr) }; if (fedir != fdir[(f[0] == this->m_new_face) ? 0 : 1]) { bFlipSet = false; break; } const bool bSameDir = (fdir[0] == fdir[1]) ? true : false; if (false == bFlipSet) { bFlipSet = true; bFlip = bSameDir; } else if (bSameDir != bFlip) { bFlipSet = false; break; } } if (bFlip) this->m_new_face->ReverseEdgeList(); } return this->m_new_face; } unsigned int ON_SubD::ExtrudeComponents( const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, size_t cptr_count ) { const bool bExtrudeBoundaries = true; const bool bPermitNonManifoldEdgeCreation = false; return ExtrudeComponents(xform, cptr_list, cptr_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation); } unsigned int ON_SubD::ExtrudeComponents( const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, bool bExtrudeBoundaries, bool bPermitNonManifoldEdgeCreation ) { if (nullptr == cptr_list || cptr_count <= 0) return 0; return Internal_ExtrudeComponents( xform, cptr_list, cptr_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation ); } unsigned int ON_SubD::Extrude(const ON_Xform & xform) { if (IsSolid()) return 0; const bool bExtrudeBoundaries = true; const bool bPermitNonManifoldEdgeCreation = false; return Internal_ExtrudeComponents( xform, nullptr, 0, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation ); } static bool Internal_SetSideGroupIds(ON_SimpleArray& new_sides) { unsigned count = new_sides.UnsignedCount(); if (count <= 0) return false; unsigned side_group_id = 1; for (unsigned int i = 0; i < count; ++i) { if (false == new_sides[i].SetSideGroupId(side_group_id)) continue; // 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()) bContinue = true; } } ++side_group_id; } if (side_group_id <= 1) return false; if (side_group_id > 2) new_sides.QuickSort(ON_Internal_ExtrudedEdge::CompareSideGroupId); return true; } static bool Internal_DebugValdateExtrudedTopology( bool bCheckCopies, const ON_SimpleArray& extruded_edges, const ON_SimpleArray& extruded_vertices ) { const unsigned edge_count = extruded_edges.UnsignedCount(); const unsigned vertex_count = extruded_vertices.UnsignedCount(); for (unsigned ei = 0; ei < edge_count; ++ei) { const ON_Internal_ExtrudedEdge& extruded_edge = extruded_edges[ei]; if (false == extruded_edge.IsValidTopology(bCheckCopies)) return false; } for (unsigned vi = 0; vi < vertex_count; ++vi) { const ON_Internal_ExtrudedVertex& extruded_vertex = extruded_vertices[vi]; if (false == extruded_vertex.IsValidTopology(bCheckCopies)) return false; } if (vertex_count < edge_count) return Internal_IsNotValidExtrudedTopology(); return true; } static bool Internal_IsInsetHack( const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, bool bExtrudeBoundaries, 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) { bool bIsManifold = false; bool bIsOriented = false; bool bHasBoundary = false; int solid_orientation = 0; ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation); bExtrudeAll = bHasBoundary; } if ( 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[]. // Marks every edge attached to a marked face. // Marks every subd boundary edge in the cptr_list[]. // Marks every vertex touching a marked edge or marked face. unsigned int list_vertex_count = 0; unsigned int list_edge_count = 0; unsigned int list_face_count = 0; const unsigned int marked_vertex_count = Internal_MarkExtrudeComponents( *this, cptr_list, cptr_count, bExtrudeBoundaries, list_vertex_count, list_edge_count, list_face_count ); 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); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { const unsigned int face_vertex_count = f->m_edge_count; if (face_vertex_count < 3 || false == f->m_status.RuntimeMark()) continue; moved_faces.Append(f); } const unsigned int moved_face_count = moved_faces.UnsignedCount(); ON_SimpleArray extruded_edges(64); ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { const bool bMarkedEdge = e->Mark(); e->ClearMark(); e->ClearMarkBits(); if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) continue; const unsigned int edge_face_count = e->m_face_count; // marked wire edges and marked boundary edges get extruded whenever bExtrudeBoundaries is true. bool bExtrudeEdge = bMarkedEdge && (0 == edge_face_count || (1 == edge_face_count && bExtrudeBoundaries)); bool bAttachedToMarkedFace = false; bool bAttachedToUnmarkedFace = false; for (unsigned int efi = 0; efi < edge_face_count; efi++) { const ON_SubDFace* f = e->Face(efi); if (nullptr == f) continue; if (f->Mark()) bAttachedToMarkedFace = true; // f is in the subset of faces that will be moved. else bAttachedToUnmarkedFace = true; // f is in the subset of faces that are stationary (will not be moved). if (bAttachedToMarkedFace && bAttachedToUnmarkedFace) { // e is on the boundary between the subset of moved faces and stationary faces // and will be extruded into a face. bExtrudeEdge = true; break; } } if (bExtrudeEdge) { ON_Internal_ExtrudedEdge& extruded_edge = extruded_edges.AppendNew(); if (false == extruded_edge.SetFromInitialEdge(const_cast(e))) { ON_SUBD_ERROR("null e or vertex pointers"); return 0; } // Marking e here indicates e is an extruded edge. // ON_Internal_ExtrudedVertex.SetFromInitialVertex() assumes marked edges are extruded edges. e->SetMark(); } } const unsigned int extruded_edge_count = extruded_edges.UnsignedCount(); if (0 == extruded_edge_count) { // no new faces will be made if (moved_face_count > 0 && false == bIsInset) { // Every face is moving Transform(xform); return FaceCount(); } return 0; } // Mark edges on the boundary of the moved subset. // clear vertex marks. this->ClearVertexMarks(); this->ClearVertexMarkBits(); // build a list extruded_vertices[] of vertices that will be extruded into "side" edges ON_SimpleArray extruded_vertices(extruded_edge_count + 8); for (unsigned int i = 0; i < extruded_edge_count; i++) { ON_Internal_ExtrudedEdge& extruded_edge = extruded_edges[i]; const ON_SubDEdge* e = extruded_edge.m_original_edge; if (nullptr == e) { ON_SUBD_ERROR("Should never be the case."); return 0; } for (unsigned int evi = 0; evi < 2; evi++) { ON_SubDVertex* v = const_cast(e->m_vertex[evi]); if (v->Mark()) continue; // this vertex has already been added to extruded_vertices[] // Mark this vertex to indicate it will be extruded into a "side" edge. v->SetMark(); ON_Internal_ExtrudedVertex& extruded_vertex = extruded_vertices.AppendNew(); if (false == extruded_vertex.SetFromInitialVertex(v)) { ON_SUBD_ERROR("Should never be the case."); return 0; } // Partially setting the topology connections here reduces the number of binary searches by about half. // The scecond paramter is false because the sorting of extruded_vertices[] will change the memory // location where extruded_vertex is stored. if (false == extruded_vertex.AddExtrudedEdgeReference(&extruded_edge, false)) return 0; } } // sort vertex pairs so they can be located by the original vertex id. extruded_vertices.QuickSort(ON_Internal_ExtrudedVertex::CompareInitialVertexId); // After sorting but before searching, set extruded_edge.m_extruded_vertex[] pointer // for the extruded_edge used to generate the extruded_vertex in the for() loop // immediately before the sorting of extruded_vertices[]. // This reduces the number of binary searches by about half. for (unsigned int i = 0; i < extruded_vertices.UnsignedCount(); i++) { ON_Internal_ExtrudedVertex& extruded_vertex = extruded_vertices[i]; if ( extruded_vertex.m_initial_vertex_id > 0 && 1 == extruded_vertex.m_extruded_edges_count && nullptr != extruded_vertex.m_extruded_edges[0] ) { if (extruded_vertex.AddExtrudedEdgeReference(extruded_vertex.m_extruded_edges[0], true)) continue; } ON_SUBD_ERROR("bug introduced in code above since Feb 2020"); return 0; } // Use the sorted extruded_vertices[] array and initial vertex ids to // finish set the topology connections between extruded_vertices[] and extruded_edges[] ON_Internal_ExtrudedVertex bsearch_key; for (unsigned int i = 0; i < extruded_edges.UnsignedCount(); i++) { ON_Internal_ExtrudedEdge& extruded_edge = extruded_edges[i]; for (unsigned int evi = 0; evi < 2; evi++) { if (nullptr != extruded_edge.m_extruded_vertex[evi]) continue; // this topology connection was set above and we can skip the binary search step // Use a binary search to find the element of extruded_vertices[] that corresponds to extruded_edge.m_original_edge->m_vertex[evi]. bsearch_key.m_initial_vertex_id = extruded_edge.m_initial_vertex_id[evi]; const int i0 = extruded_vertices.BinarySearch(&bsearch_key, ON_Internal_ExtrudedVertex::CompareInitialVertexId); if (i0 < 0) { ON_SUBD_ERROR("Should never be the case."); return 0; } if (false == extruded_vertices[i0].AddExtrudedEdgeReference(&extruded_edge, true)) return 0; } } if (false == bPermitNonManifoldEdgeCreation) { for (unsigned int i = 0; i < extruded_vertices.UnsignedCount(); i++) { // 3 or more "side" faces will be attached to the edge made by extruding the vertex. // Thus, this edge will be a nonmanifold edge. if (extruded_vertices[i].m_extruded_edges_count >= 3) return 0; } } if (false == Internal_DebugValdateExtrudedTopology(false, extruded_edges, extruded_vertices)) return 0; // Need to set some tag information that is used when we created the extruded edges and faces // before modifications are made to this subd. for (unsigned int i = 0; i < extruded_vertices.UnsignedCount(); ++i) extruded_vertices[i].SetConnectingEdgeTag(); // 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, bIsInset, xform)) return 0; } // Create "side" face edge that will be opposite the original edge. for (unsigned int i = 0; i < extruded_edges.UnsignedCount(); ++i) { if (nullptr == extruded_edges[i].CreateCopiedEdge(*this)) return 0; } if (false == Internal_DebugValdateExtrudedTopology(true, extruded_edges, extruded_vertices)) return 0; // At this point moved_faces[] = set of marked faces. // Efficiency determines whither the face mark or the moved_faces[] array // is used to decide if a face is moved or stationary. for (unsigned int i = 0; i < extruded_vertices.UnsignedCount(); ++i) { // Attach unmarked faces (stationary) to m_copied_vertex. // Leave the marked faces (transformed) attached to m_original_vertex. if (false == extruded_vertices[i].AttachUnmarkedFacesToCopiedVertex()) { for (unsigned j = 0; j < i; ++j) extruded_vertices[i].UndoAttachUnmarkedFacesToCopiedVertex(); return 0; } } // 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. // (The vertices in extruded_vertices[] are currently marked and have already been transformed.) 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()) { if (false == bIsInset) v->Transform(false, xform); v->SetMark(); if (false == bDestroyEvaluationCache) v->VertexModifiedNofification(); } } } } // For the original boundary vertrex, move unmarked edges to use the new vertex. for (unsigned int i = 0; i < extruded_vertices.UnsignedCount(); i++) Internal_SetEdgeVertices(*this, extruded_vertices[i]); // extrude edges into new "side" faces. for (unsigned int i = 0; i < extruded_edge_count; i++) extruded_edges[i].ExtrudeFace(*this); // remove cached subdivision calculations if (bDestroyEvaluationCache) ClearEvaluationCache(); // Set tags that are clearly determined by the extrusion. if ( Internal_SetSideGroupIds(extruded_edges)) { const unsigned count = extruded_edges.UnsignedCount(); unsigned i1 = 0; for (unsigned i0 = 0; i0 < count; i0 = (i1 > i0) ? i1 : (i0 + 1)) { const unsigned side_group_id = extruded_edges[i0].m_side_group_id; unsigned crease_count = 0; unsigned interior_crease_count = 0; unsigned corner_count = 0; for (i1 = i0; i1 < count && side_group_id == extruded_edges[i1].m_side_group_id; ++i1) { 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_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_SubDEdgeTag::Crease == extruded_edge.m_initial_edge_tag) ) extruded_edge.m_copied_edge->m_edge_tag = ON_SubDEdgeTag::Crease; } if (extruded_edge.InitialCrease()) { ++crease_count; if (2 == extruded_edge.m_initial_edge_face_count) ++interior_crease_count; if (extruded_edge.InitialCreaseWithCorner()) { ++corner_count; for (unsigned k = 0; k < 2; ++k) { 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_SubDEdgeTag::Crease; for (unsigned evi = 0; evi < 2; ++evi) { ON_Internal_ExtrudedVertex* extruded_vertex = extruded_edge.m_extruded_vertex[evi]; 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_SubDVertexTag::Corner; } } } } } } } if ( (crease_count == (i1 - i0)) && (corner_count > 0 || crease_count == interior_crease_count) ) { for (unsigned i = i0; i < i1; ++i) extruded_edges[i].SetBothEdgeTags(ON_SubDEdgeTag::Crease); } } } for (unsigned i = 0; i < extruded_vertices.UnsignedCount(); ++i) { ON_Internal_ExtrudedVertex& pair = extruded_vertices[i]; if (ON_SubDVertexTag::Corner == pair.m_initial_vertex_tag) { for (unsigned vdex = 0; vdex < 2; ++vdex) { ON_SubDVertex* v = (0 == vdex) ? pair.m_original_vertex : pair.m_copied_vertex; if (nullptr == v) continue; if (v->EdgeProperties().m_crease_edge_count >= 3) 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. if (false == bIsInset) this->UpdateAllTagsAndSectorCoefficients(true); #if defined(ON_DEBUG) if ( false == bIsInset) IsValid(); #endif this->ChangeGeometryContentSerialNumberForExperts(false); // number of moved faces and new faces created by extruding edges return moved_face_count + extruded_edge_count; } unsigned int ON_SubD::SetVertexTags( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, ON_SubDVertexTag vertex_tag ) { if ( ON_SubDVertexTag::Smooth != vertex_tag && ON_SubDVertexTag::Crease != vertex_tag && ON_SubDVertexTag::Corner != vertex_tag ) return 0; if ( ci_count <= 0 || nullptr == ci_list || VertexCount() <= 0 ) return 0; ON_SimpleArray cptr_list; if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0) return 0; // nothing to change return SetVertexTags( cptr_list.Array(), cptr_list.UnsignedCount(), vertex_tag ); } unsigned int ON_SubD::SetVertexTags( const ON_SubDComponentPtr* cptr_list, size_t cptr_count, ON_SubDVertexTag vertex_tag ) { if ( ON_SubDVertexTag::Smooth != vertex_tag && ON_SubDVertexTag::Crease != vertex_tag && ON_SubDVertexTag::Corner != vertex_tag ) return 0; if (cptr_count <= 0 || nullptr == cptr_list) return 0; ON_SubDComponentMarksClearAndRestore mark_guard(*this); // be less generous with markbits. ClearComponentMarkBits(true, true, true); 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 unsigned int candidate_count = 0; for (size_t i = 0; i < cptr_count; i++) { ON_SubDVertex* vertex = cptr_list[i].Vertex(); if (nullptr == vertex) continue; if (vertex->m_vertex_tag == vertex_tag) continue; 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) continue; const ON_SubDVertexEdgeProperties ep = vertex->EdgeProperties(); if (ep.m_nonmanifold_edge_count > 0) continue; // nonmanifold edge if (ep.m_boundary_edge_count + ep.m_wire_edge_count > 2) continue; if (ON_SubDVertexTag::Smooth == vertex_tag) { if (ep.m_interior_edge_count != vertex->m_edge_count) continue; } else if (ON_SubDVertexTag::Crease == vertex_tag) { if (2 == ep.m_crease_edge_count) { // attempt change - further refinement may be needed here } else if (2 == ep.m_boundary_edge_count) { // attempt change - further refinement may be needed here } else if (2 == ep.m_wire_edge_count) { // attempt change - further refinement may be needed here } else { // dont' attempt change - further refinement may be needed here continue; } } } candidate_count++; vertex->m_status.SetRuntimeMark(); if (nullptr != vertex->m_edges) { if (ON_SubDVertexTag::Corner == vertex_tag) { const unsigned int crease_count = vertex->EdgeCount(ON_SubDEdgeTag::Crease); if (crease_count >= 2) continue; // do not crease additional edges } 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); if (nullptr == edge) continue; if (bNewVertexTagIsSmooth) { // new vertex_tag is Smooth if (edge->IsSmoothNotX()) continue; } else { // new vertex_tag is Crease or Corner if (edge->IsCrease()) continue; } // This edge tag will need to be changed edge->m_status.SetRuntimeMark(); } } } if (0 == candidate_count) return 0; bool bUpdateTags = (ON_SubDVertexTag::Crease != vertex_tag); // 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 ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* edge = eit.FirstEdge(); nullptr != edge; edge = eit.NextEdge()) { if (false == edge->m_status.RuntimeMark()) continue; edge->m_status.ClearRuntimeMark(); if (false == edge->IsSmooth()) continue; const ON_SubDVertex* v[2] = { edge->m_vertex[0], edge->m_vertex[1] }; if (nullptr == v[0] || nullptr == v[1]) continue; 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_SubDEdgeTag edge_tag; for (;;) { if (2 != edge->m_face_count) { edge_tag = ON_SubDEdgeTag::Crease; break; } if (bGettingSmoother) { edge_tag = ON_SubDEdgeTag::Smooth; break; } if (2 == v[0]->m_edge_count && (ON_SubDVertexTag::Crease == vtag[0] || ON_SubDVertexTag::Corner == vtag[0])) { 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_SubDVertexTag::Crease == vtag[0] || ON_SubDVertexTag::Corner == vtag[0] || ON_SubDVertexTag::Dart == vtag[0]) && (ON_SubDVertexTag::Crease == vtag[1] || ON_SubDVertexTag::Corner == vtag[1] || ON_SubDVertexTag::Dart == vtag[1]) ) { edge_tag = ON_SubDEdgeTag::Crease; break; } edge_tag = ON_SubDEdgeTag::Smooth; break; } 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 bUpdateTags = true; for (size_t i = 0; i < cptr_count; i++) { ON_SubDVertex* vertex = cptr_list[i].Vertex(); if (nullptr == vertex) continue; if (false == vertex->m_status.RuntimeMark()) continue; unsigned int crease_count = 0; unsigned int marked_count = 0; 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); if (nullptr == edge) continue; if (edge->IsCrease()) ++crease_count; else if (edge->m_status.RuntimeMark()) ++marked_count; } if (crease_count + marked_count <= 2 && (0 != crease_count || 0 != marked_count)) continue; if (pass > 0) return 0; bUpdateTags = false; if (2 == crease_count) { 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); if (nullptr != edge) edge->m_status.ClearRuntimeMark(); } } else { vertex->m_status.ClearRuntimeMark(); candidate_count--; } } if (0 == candidate_count) return 0; } if (false == bUpdateTags) return 0; unsigned int changed_vertex_count = 0; for (size_t i = 0; i < cptr_count; i++) { ON_SubDVertex* vertex = cptr_list[i].Vertex(); if (nullptr == vertex) continue; if (false == vertex->m_status.RuntimeMark()) continue; 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); if (nullptr == edge) continue; if ( false == bNewVertexTagIsSmooth && edge->m_status.RuntimeMark() && false == edge->IsCrease() ) { 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_SubDEdgeTag::Crease == edge->m_edge_tag && other_vertex->IsSmooth() ) { const_cast(other_vertex)->m_vertex_tag = ON_SubDVertexTag::Dart; other_vertex->VertexModifiedNofification(); } } } if (0 == changed_vertex_count) return 0; ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* edge = eit.FirstEdge(); nullptr != edge; edge = eit.NextEdge()) { if (false == edge->m_status.RuntimeMark()) continue; const ON_SubDVertex* v[2] = { edge->m_vertex[0], edge->m_vertex[1] }; if (nullptr == v[0] || nullptr == v[1]) continue; ON_SubDEdgeTag edge_tag; if (v[0]->IsDartOrCreaseOrCorner() && v[1]->IsDartOrCreaseOrCorner()) edge_tag = (smooth_bias == edge->MarkBits()) ? ON_SubDEdgeTag::SmoothX : ON_SubDEdgeTag::Crease; else edge_tag = ON_SubDEdgeTag::Smooth; if (edge->m_edge_tag == edge_tag) continue; const_cast(edge)->m_edge_tag = edge_tag; edge->EdgeModifiedNofification(); } ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* vertex = vit.FirstVertex(); nullptr != vertex; vertex = vit.NextVertex()) { if (false == vertex->m_status.RuntimeMark()) continue; 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_SubDVertexTag::Crease; } else if (1 == crease_count) vtag = ON_SubDVertexTag::Dart; else if (crease_count > 2) vtag = ON_SubDVertexTag::Corner; else 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); return changed_vertex_count; } unsigned int ON_SubD::SetEdgeTags( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, ON_SubDEdgeTag edge_tag ) { if (ON_SubDEdgeTag::Smooth != edge_tag && ON_SubDEdgeTag::Crease != edge_tag) return 0; if ( ci_count <= 0 || nullptr == ci_list || EdgeCount() <= 0 ) return 0; ON_SimpleArray cptr_list; if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0) return 0; // nothing to change return SetEdgeTags( cptr_list.Array(), cptr_list.UnsignedCount(), edge_tag ); } unsigned int ON_SubD::SetEdgeTags( const ON_SubDComponentPtr* cptr_list, size_t cptr_count, ON_SubDEdgeTag edge_tag ) { if (ON_SubDEdgeTag::Smooth != edge_tag && ON_SubDEdgeTag::Crease != edge_tag) return 0; if ( cptr_count <= 0 || nullptr == cptr_list || EdgeCount() <= 0 ) return 0; unsigned int changed_edge_count = 0; const bool bChangeToSmooth = (ON_SubDEdgeTag::Smooth == edge_tag) ? true : false; for (size_t i = 0; i < cptr_count; i++) { ON_SubDEdge* edge = cptr_list[i].Edge(); if (nullptr == edge) continue; if (bChangeToSmooth == edge->IsSmooth()) continue; if (bChangeToSmooth && 2 != edge->FaceCount()) continue; edge->EdgeModifiedNofification(); changed_edge_count++; edge->m_edge_tag = edge_tag; edge->UnsetSectorCoefficientsForExperts(); for (int evi = 0; evi < 2; evi++) { ON_SubDVertex* v = const_cast(edge->m_vertex[evi]); if (nullptr == v) continue; v->m_vertex_tag = ON_SubDVertexTag::Unset; v->ClearSavedSubdivisionPoints(); } } if (0 == changed_edge_count) return 0; ClearEvaluationCache(); ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { if (ON_SubDVertexTag::Unset != v->m_vertex_tag) continue; unsigned crease_count = 0; const unsigned vertex_edge_count = v->EdgeCount(); for (unsigned vei = 0; vei < vertex_edge_count; vei++) { const ON_SubDEdge* e = v->Edge(vei); if (nullptr == e) continue; if (e->IsCrease()) { crease_count++; if (crease_count > 2) break; } } ON_SubDVertexTag vertex_tag; switch (crease_count) { case 0: vertex_tag = ON_SubDVertexTag::Smooth; break; case 1: vertex_tag = ON_SubDVertexTag::Dart; break; case 2: vertex_tag = ON_SubDVertexTag::Crease; break; default: vertex_tag = ON_SubDVertexTag::Corner; break; } if (v->m_vertex_tag != vertex_tag) { const_cast(v)->m_vertex_tag = vertex_tag; v->ClearSavedSubdivisionPoints(); } } ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { ON_SubDEdgeTag e_tag = e->m_edge_tag; if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) 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; e->UnsetSectorCoefficientsForExperts(); e->ClearSavedSubdivisionPoints(); } } UpdateAllTagsAndSectorCoefficients(false); return changed_edge_count; } unsigned int ON_SubD::RemoveAllCreases() { unsigned int changed_count = 0; ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { if ( false == e->IsCrease() || 2 != e->m_face_count) continue; 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_SubDVertexTag::Unset; } ++changed_count; } if (changed_count > 0) { this->DestroyRuntimeCache(true); this->UpdateAllTagsAndSectorCoefficients(true); } return changed_count; } const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor(ON_SubDEdgePtr starting_edge, ON_ChainDirection search_direction, ON_SubD::ChainType chain_type) { return ON_SubDEdgeChain::EdgeChainNeighbor( starting_edge, search_direction, chain_type, false, ON_ComponentStatus::NoneSet, ON_ComponentStatus::NoneSet ); } const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor( ON_SubDEdgePtr starting_edge, ON_ChainDirection search_direction, ON_SubD::ChainType chain_type, bool bEnableStatusCheck, ON_ComponentStatus status_pass, ON_ComponentStatus status_fail ) { for (;;) { if (ON_ChainDirection::Previous != search_direction && ON_ChainDirection::Next != search_direction) break; const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(starting_edge.m_ptr); if (nullptr == edge) break; bool bReverse = (ON_ChainDirection::Previous == search_direction); if (0 != ON_SUBD_EDGE_DIRECTION(starting_edge.m_ptr)) bReverse = !bReverse; const ON_SubDVertex* v = edge->m_vertex[bReverse ? 0 : 1]; if (nullptr == v) break; if (v->m_edge_count <= 1 || nullptr == v->m_edges) break; const bool bIsSmooth = edge->IsSmooth(); const bool bIsCrease = edge->IsCrease() || 2 != edge->m_face_count; if (bIsSmooth != (bIsCrease?false:true)) break; const unsigned short vertex_edge_count = ((ON_SubD::ChainType::EqualEdgeTagAndOrdinary == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTagAndOrdinary == chain_type) && (1 == edge->m_face_count || 2 == edge->m_face_count)) ? (edge->m_face_count + 2) : ((unsigned short)0); if (vertex_edge_count > 0 && vertex_edge_count != v->m_edge_count) break; if (ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTagAndOrdinary == chain_type) { if (bIsSmooth) { // edge is smooth so vertex must be smooth if (ON_SubDVertexTag::Smooth != v->m_vertex_tag) break; } else { // edge is crease so vertex must be crease if (ON_SubDVertexTag::Crease != v->m_vertex_tag) break; } } // Look for a single neighbor with same crease/smooth property and same face count // This lets chains turn the right way when there are both creases and smooth // edges. const ON_SubDEdge* nxt = nullptr; for (unsigned short vei = 0; vei < v->m_edge_count; vei++) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr); if (edge == e) continue; if (bIsSmooth != e->IsSmooth()) continue; if (bIsCrease != e->IsCrease()) continue; if (e->m_face_count != edge->m_face_count) continue; if (e->m_vertex[0] != v && e->m_vertex[1] != v) continue; // bogus edge if (nullptr == nxt) { nxt = e; continue; } // ambiguous options here nxt = nullptr; break; } const int nxt_connecting_vertex_index = (ON_ChainDirection::Next == search_direction) ? 0 : 1; if (nullptr != nxt) { if (false == bEnableStatusCheck || ON_ComponentStatus::StatusCheck(nxt->m_status, status_pass, status_fail)) return ON_SubDEdgePtr::Create(nxt, (v == nxt->m_vertex[nxt_connecting_vertex_index]) ? 0 : 1); } if (2 != edge->m_face_count || 4 != v->m_edge_count) break; const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(edge->m_face2[0].m_ptr),ON_SUBD_FACE_POINTER(edge->m_face2[1].m_ptr) }; if (nullptr == f[0] || nullptr == f[1] || f[0] == f[1]) break; for (unsigned short vei = 0; vei < v->m_edge_count; vei++) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr); if (edge == e) continue; if (2 != e->m_face_count) continue; if (e->m_vertex[0] != v && e->m_vertex[1] != v) continue; // bogus edge const ON_SubDFace* nxtf[2] = { ON_SUBD_FACE_POINTER(e->m_face2[0].m_ptr),ON_SUBD_FACE_POINTER(e->m_face2[1].m_ptr) }; if (nullptr == nxtf[0] || nullptr == nxtf[1] || nxtf[0] == nxtf[1]) continue; if (f[0] == nxtf[0] || f[1] == nxtf[0]) continue; if (f[0] == nxtf[1] || f[1] == nxtf[1]) continue; if (nullptr == nxt) { nxt = e; continue; } // ambiguous options here nxt = nullptr; break; } if (nullptr != nxt) { if (bIsSmooth != nxt->IsSmooth()) { // edge tag changed if ( ON_SubD::ChainType::EqualEdgeTag == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type || ON_SubD::ChainType::EqualEdgeTagAndOrdinary == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTagAndOrdinary == chain_type ) break; } if (false == bEnableStatusCheck || ON_ComponentStatus::StatusCheck(nxt->m_status, status_pass, status_fail)) return ON_SubDEdgePtr::Create(nxt, (v == nxt->m_vertex[nxt_connecting_vertex_index]) ? 0 : 1); } break; } return ON_SubDEdgePtr::Null; } void ON_UniqueTester::Block::DeleteBlock(Block* blk) { if (nullptr != blk) onfree(blk); } ON_UniqueTester::Block* ON_UniqueTester::Block::NewBlock() { size_t sz1 = sizeof(Block); while (0 != sz1 % 8) sz1++; size_t sz2 = ON_UniqueTester::Block::BlockCapacity * sizeof(m_a[0]); void* p = onmalloc(sz1 + sz2); Block* blk = new (p) Block(); blk->m_a = (ON__UINT_PTR*)(((char*)(p)) + sz1); return blk; } int ON_UniqueTester::Block::Compare(ON__UINT_PTR* lhs, ON__UINT_PTR* rhs) { if (*lhs < *rhs) return -1; if (*lhs > *rhs) return 1; return 0; } bool ON_UniqueTester::Block::InBlock(size_t sorted_count,ON__UINT_PTR x) const { if (nullptr != m_a && m_count > 0) { if ( sorted_count > 0 && x >= m_a[0] && x <= m_a[sorted_count - 1]) { if (nullptr != bsearch(&x, m_a, sorted_count, sizeof(m_a[0]), (int(*)(const void*, const void*))ON_UniqueTester::Block::Compare)) return true; } if (sorted_count < m_count) { const ON__UINT_PTR* p = m_a + sorted_count; const ON__UINT_PTR* p1 = m_a + m_count; while (p < p1) { if (x == *p++) return true; } } } return false; } void ON_UniqueTester::Block::SortBlock() { ON_qsort(m_a, m_count, sizeof(m_a[0]), (int(*)(const void*, const void*))ON_UniqueTester::Block::Compare); } void ON_UniqueTester::Internal_CopyFrom( const ON_UniqueTester & src ) { m_block_list = nullptr; m_sorted_count = 0; Block* first_blk = nullptr; for ( Block* src_blk = src.m_block_list; nullptr != src_blk; src_blk = src_blk->m_next) { Block* blk = Block::NewBlock(); memcpy( blk->m_a, src_blk->m_a, src_blk->m_count * sizeof(blk->m_a[0]) ); blk->m_count = src_blk->m_count; if (nullptr == first_blk) { first_blk = blk; } else { blk->m_next = m_block_list; m_block_list = blk; } } if (nullptr != first_blk) { if (src.m_sorted_count != first_blk->m_count) first_blk->SortBlock(); first_blk->m_next = m_block_list; m_block_list = first_blk; m_sorted_count = first_blk->m_count; } } void ON_UniqueTester::Internal_Destroy() { Block* nxt = m_block_list; m_block_list = nullptr; m_sorted_count = 0; for ( Block* blk = nxt; nullptr != blk; blk = nxt) { nxt = blk->m_next; Block::DeleteBlock(blk); } } ON_UniqueTester::~ON_UniqueTester() { Internal_Destroy(); } ON_UniqueTester::ON_UniqueTester(const ON_UniqueTester& src) { Internal_CopyFrom(src); } ON_UniqueTester& ON_UniqueTester::operator=(const ON_UniqueTester& src) { if (this != &src) { Internal_Destroy(); Internal_CopyFrom(src); } return *this; } bool ON_UniqueTester::InList(ON__UINT_PTR x) const { size_t sorted_count = m_sorted_count; for ( const Block* blk = m_block_list; nullptr != blk; blk = blk->m_next) { if (blk->InBlock(sorted_count, x)) return true; sorted_count = ON_UniqueTester::Block::BlockCapacity; } return false; } bool ON_UniqueTester::AddToList(ON__UINT_PTR x) { if (nullptr != m_block_list && m_sorted_count + 50 == m_block_list->m_count) { m_block_list->SortBlock(); m_sorted_count = m_block_list->m_count; } if (InList(x)) return false; Internal_AddValue(x); return true; } void ON_UniqueTester::ExpertAddNewToList(ON__UINT_PTR x) { Internal_AddValue(x); } void ON_UniqueTester::Internal_AddValue(ON__UINT_PTR x) { if (nullptr == m_block_list || ON_UniqueTester::Block::BlockCapacity == m_block_list->m_count) { if (nullptr != m_block_list && m_sorted_count < ON_UniqueTester::Block::BlockCapacity) m_block_list->SortBlock(); ON_UniqueTester::Block* blk = ON_UniqueTester::Block::NewBlock(); blk->m_next = m_block_list; m_block_list = blk; m_sorted_count = 0; } m_block_list->m_a[m_block_list->m_count++] = x; if ( 1 == m_block_list->m_count || (m_sorted_count+1 == m_block_list->m_count && x > m_block_list->m_a[m_sorted_count-1]) ) ++m_sorted_count; } void ON_UniqueTester::ClearList() { Internal_Destroy(); } unsigned int ON_UniqueTester::Count() const { size_t count = 0; for ( const Block* blk = m_block_list; nullptr != blk; blk = blk->m_next) { count += blk->m_count; } return (unsigned int)count; } bool ON_SubDEdgeChain::GetSideComponents( unsigned relative_side, ON_SimpleArray& side_components ) const { return ON_SubDEdgeChain::GetSideComponents(EdgeChain(), relative_side, side_components); } bool ON_SubDEdgeChain::GetSideComponents( const ON_SimpleArray& edge_chain, unsigned relative_side, ON_SimpleArray& side_components ) { side_components.SetCount(0); if (relative_side > 1U) return false; const unsigned edge_count = edge_chain.UnsignedCount(); if (edge_count < 1) return false; if (false == ON_SubDEdgeChain::IsValidEdgeChain(edge_chain, true)) return false; // bozo vaccine const bool bClosedLoop = edge_count >= 3 && edge_chain[0].RelativeVertex(0) == edge_chain[edge_count - 1].RelativeVertex(1); ON_SubDSectorIterator sit; const ON__INT_PTR sitdir = ((unsigned)1 - relative_side); ON_SubDEdgePtr chain_eptr1 = ON_SubDEdgePtr::Null; const ON_SubDFace* f1 = nullptr; const ON_SubDVertex* v = nullptr; if (false == bClosedLoop) { // first edge/face pair at the chain's initial vertex chain_eptr1 = edge_chain[0]; f1 = chain_eptr1.RelativeFace(relative_side); if (nullptr != f1) { v = chain_eptr1.RelativeVertex(0); if (nullptr != v && v == sit.Initialize(f1, sitdir, v)) { ON_SubDEdgePtr e = sit.CurrentEdgePtr(0); if (e.IsNotNull()) { side_components.Append(ON_SubDComponentPtr::Create(e)); side_components.Append(ON_SubDComponentPtr::Create(f1)); } } } } else { chain_eptr1 = edge_chain[edge_count-1]; f1 = chain_eptr1.RelativeFace(relative_side); } // first_side_components_edge is needed for closed loops. ON_SubDComponentPtr first_side_components_edge = ON_SubDComponentPtr::Null; for (unsigned j = bClosedLoop ? 0 : 1; j < edge_count; ++j) { const ON_SubDEdgePtr chain_eptr0 = chain_eptr1; chain_eptr1 = edge_chain[j]; v = chain_eptr1.RelativeVertex(0); const ON_SubDFace* f0 = f1; f1 = chain_eptr1.RelativeFace(relative_side); if (nullptr != f0 && f0 == f1) { // side_components[] = ...f0,v,f1. unsigned n = side_components.UnsignedCount(); if (0 == n || f0 != side_components[n-1].Face()) side_components.Append(ON_SubDComponentPtr::Create(f0)); side_components.Append(ON_SubDComponentPtr::Create(v)); side_components.Append(ON_SubDComponentPtr::Create(f1)); continue; } // sit_dex < sit_max prevents possiblity of an infinite loop if the chain or subd is corrupt const unsigned sit_max = v->FaceCount(); unsigned sit_dex = 1; const ON_SubDFace* f = nullptr; for (;;) { if (nullptr == f0) break; if (nullptr == v || v != sit.Initialize(f0, sitdir, v)) break; ON_SubDEdgePtr eptr = sit.CurrentEdgePtr(1); if (eptr.IsNull()) break; unsigned n = side_components.UnsignedCount(); if (0 == n || f0 != side_components[n - 1].Face()) side_components.Append(ON_SubDComponentPtr::Create(f0)); side_components.Append(ON_SubDComponentPtr::Create(eptr)); if (first_side_components_edge.IsNull()) first_side_components_edge = ON_SubDComponentPtr::Create(eptr); // needed for closed loops // The HasInteriorEdgeTopology() checks prevent getting garbage from // non-oriented SubDs. If people complain, they need to orient // their SubDs before asking "side" questions. // Some math nerd yokel is sure to stuff the boundary of // a mobius strip boundary into this function and then complain. // Too bad for them. [Dale Lear 2021] if (eptr.HasInteriorEdgeTopology(true)) { for (f = sit.NextFace(ON_SubDSectorIterator::StopAt::Boundary), sit_dex = 1; nullptr != f && sit_dex < sit_max; f = sit.NextFace(ON_SubDSectorIterator::StopAt::Boundary), ++sit_dex) { if (f == f0) break; eptr = sit.CurrentEdgePtr(1); side_components.Append(ON_SubDComponentPtr::Create(f)); if (f == f1) break; side_components.Append(ON_SubDComponentPtr::Create(eptr)); if (false == eptr.HasInteriorEdgeTopology(true)) break; } } break; } if (f == f1) continue; for (;;) { if (nullptr == f1) break; if (nullptr == v || v != sit.Initialize(f1, 1-sitdir, v)) break; const ON_SubDEdgePtr eptr1 = sit.CurrentEdgePtr(1); const unsigned count0 = side_components.UnsignedCount(); if (eptr1.HasInteriorEdgeTopology(true)) { const ON_SubDFace* f2 = nullptr; for (f = sit.NextFace(ON_SubDSectorIterator::StopAt::Boundary), sit_dex = 1; nullptr != f && sit_dex < sit_max; f = sit.NextFace(ON_SubDSectorIterator::StopAt::Boundary), ++sit_dex) { if (f == f0 || f == f1) { f = nullptr; break; } f2 = f; if (false == sit.CurrentEdgePtr(1).HasInteriorEdgeTopology(true)) break; } if (nullptr == f2) break; if (v != sit.Initialize(f2, sitdir, v)) break; for (f = sit.CurrentFace(), sit_dex = 1; nullptr != f && sit_dex < sit_max; f = sit.NextFace(ON_SubDSectorIterator::StopAt::Boundary), ++sit_dex) { ON_SubDEdgePtr eptr = sit.CurrentEdgePtr(0); if (f == f0 || eptr.IsNull()) { f = nullptr; break; } side_components.Append(ON_SubDComponentPtr::Create(eptr)); side_components.Append(ON_SubDComponentPtr::Create(f)); if (f == f1) break; eptr = sit.CurrentEdgePtr(1); if (false == eptr.HasInteriorEdgeTopology(true)) { f = nullptr; break; } } if (f != f1) { side_components.SetCount(count0); } } if ( count0 == side_components.UnsignedCount() && eptr1.IsNotNull() ) { side_components.Append(ON_SubDComponentPtr::Create(eptr1)); side_components.Append(ON_SubDComponentPtr::Create(f1)); } break; } } if (bClosedLoop) { const unsigned side_components_count = side_components.UnsignedCount(); if (side_components_count >= 6 && first_side_components_edge.IsEdge() && side_components[0].IsFace() && side_components[0].Face() == side_components[side_components_count-1].Face() ) { // rearrange side_components[] so it begins with first_side_components_eptr. // This elimnates the complexity of having to begin/end a SubD fillet // at a ... f,v,f ... location. const ON_SubDFace* f = side_components[0].Face(); if (nullptr == f) { ON_SUBD_ERROR("f should be non-null."); } else { // set j0 = current side_components[] index of the first edge unsigned j0 = side_components_count; for (unsigned j = 0; j < side_components_count; ++j) { if (first_side_components_edge.m_ptr == side_components[j].m_ptr) { j0 = j; break; } } if (j0 > 0 && j0 < side_components_count) { ON_SimpleArray tail; tail.Append((int)(j0-1), side_components.Array() + 1); for (unsigned k = 0; k + j0 < side_components_count; ++k) side_components[k] = side_components[j0 + k]; side_components.SetCount(side_components_count - j0); side_components.Append(tail.Count(), tail.Array()); side_components.Append(first_side_components_edge); } } } } else { // last edge at the chain's final vertex chain_eptr1 = edge_chain[edge_count - 1]; f1 = chain_eptr1.RelativeFace(relative_side); if (nullptr != f1) { v = chain_eptr1.RelativeVertex(1); if (nullptr != v && v == sit.Initialize(f1, sitdir, v)) { ON_SubDEdgePtr eptr = sit.CurrentEdgePtr(1); if (eptr.IsNotNull()) { unsigned n = side_components.UnsignedCount(); if (0 == n || f1 != side_components[n - 1].Face()) side_components.Append(ON_SubDComponentPtr::Create(f1)); side_components.Append(ON_SubDComponentPtr::Create(eptr)); } } } } return side_components.UnsignedCount() > 0; } const ON_SimpleArray& ON_SubDEdgeChain::EdgeChain() const { return m_edge_chain; } const ON_SubD& ON_SubDEdgeChain::SubD() const { return m_subd_ref.SubD(); } const ON_SubDRef ON_SubDEdgeChain::SubDRef() const { return m_subd_ref; } bool ON_SubDEdgeChain::InChain( const ON_SubDEdgePtr edge_ptr ) const { return InChain(ON_SUBD_EDGE_POINTER(edge_ptr.m_ptr)); } bool ON_SubDEdgeChain::InChain( const ON_SubDEdge* edge ) const { return (nullptr == edge) ? false : m_unique_tester.InList((ON__UINT_PTR)edge); } bool ON_SubDEdgeChain::InChain( const ON_SubDVertex* vertex ) const { return (nullptr == vertex) ? false : m_unique_tester.InList((ON__UINT_PTR)vertex); } bool ON_SubDEdgeChain::IsClosedLoop() const { const unsigned int count = m_edge_chain.UnsignedCount(); return (count >= 3 && m_edge_chain[0].RelativeVertex(0) == m_edge_chain[count - 1].RelativeVertex(1)); } bool ON_SubDEdgeChain::IsConvexLoop(bool bStrictlyConvex) const { if (false == IsClosedLoop()) return false; const unsigned int count = m_edge_chain.UnsignedCount(); ON_SimpleArray points(count); for (unsigned int i = 0; i < count; ++i) { const ON_SubDVertex* v = m_edge_chain[i].RelativeVertex(0); if (nullptr == v) return false; points.Append(v->ControlNetPoint()); } if ( false == (points[0] != points[count - 1]) ) return false; return ON_IsConvexPolyline(points, bStrictlyConvex); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_UUID persistent_subd_id, ON_SubDRef subd_ref, const ON_SubDEdge* initial_edge ) { return ON_SubDEdgeChain::BeginEdgeChain(persistent_subd_id, subd_ref, ON_SubDEdgePtr::Create(initial_edge, 0)); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_UUID persistent_subd_id, ON_SubDRef subd_ref, const ON_SimpleArray& initial_edge_chain ) { return BeginEdgeChain(persistent_subd_id, subd_ref, initial_edge_chain.Array(), initial_edge_chain.UnsignedCount()); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_UUID persistent_subd_id, ON_SubDRef subd_ref, const ON_SubDEdge*const* initial_edge_chain, size_t edge_count ) { ClearEdgeChain(); if ( edge_count <= 0 || subd_ref.SubD().IsEmpty() || subd_ref.SubD().EdgeCount() < (unsigned int)edge_count ) return 0; if ( 1 == edge_count) return ON_SubDEdgeChain::BeginEdgeChain(persistent_subd_id, subd_ref, ON_SubDEdgePtr::Create(initial_edge_chain[0], 0)); const ON_SubDEdge* e0 = initial_edge_chain[0]; if (nullptr == e0 || nullptr == e0->m_vertex[0] || nullptr == e0->m_vertex[1] ) return 0; const ON_SubDEdge* e1 = initial_edge_chain[1]; if (nullptr == e1 || nullptr == e1->m_vertex[0] || nullptr == e1->m_vertex[1] ) return 0; ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Create(e0, (e0->m_vertex[1] == e1->m_vertex[0] || e0->m_vertex[1] == e1->m_vertex[1]) ? 0 : 1); ON_SimpleArray eptr_chain(edge_count); eptr_chain.Append(eptr); const ON_SubDVertex* v = eptr.RelativeVertex(1); for (unsigned int i = 1; i < edge_count; i++) { e1 = initial_edge_chain[i]; if (nullptr == e1 || nullptr == e1->m_vertex[0] || nullptr == e1->m_vertex[1] ) return 0; if (v != e1->m_vertex[0] && v != e1->m_vertex[1]) return 0; eptr = ON_SubDEdgePtr::Create(e1, (v == e1->m_vertex[0]) ? 0 : 1); eptr_chain.Append(eptr); } return ON_SubDEdgeChain::BeginEdgeChain(persistent_subd_id, subd_ref,eptr_chain); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_UUID persistent_subd_id, ON_SubDRef subd_ref, ON_SubDEdgePtr eptr ) { return ON_SubDEdgeChain::BeginEdgeChain(persistent_subd_id, subd_ref, &eptr, 1); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_UUID persistent_subd_id, ON_SubDRef subd_ref, const ON_SimpleArray& initial_edge_chain ) { return ON_SubDEdgeChain::BeginEdgeChain(persistent_subd_id, subd_ref, initial_edge_chain.Array(), initial_edge_chain.UnsignedCount() ); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_UUID persistent_subd_id, ON_SubDRef subd_ref, const ON_SubDEdgePtr* initial_edge_chain, size_t edge_count ) { ClearEdgeChain(); m_persistent_subd_id = persistent_subd_id; m_subd_ref = subd_ref; if (edge_count <= 0 || m_subd_ref.SubD().IsEmpty()) return 0; if ( ((size_t)subd_ref.SubD().EdgeCount()) < edge_count ) return 0; m_edge_chain.Reserve(edge_count + 128); const ON_SubDVertex* first_vertex = nullptr; const ON_SubDVertex* last_vertex = nullptr; for (size_t i = 0; i < edge_count; i++) { const ON_SubDEdgePtr eptr = initial_edge_chain[i]; const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr); if (nullptr == e) continue; if (m_unique_tester.InList((ON__UINT_PTR)e)) continue; const ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(eptr.m_ptr); const ON_SubDVertex* v[2] = { e->m_vertex[edir], e->m_vertex[1 - edir] }; if (nullptr == v[0] || nullptr == v[1] || v[0] == v[1] ) continue; if (nullptr == first_vertex) { first_vertex = v[0]; last_vertex = v[1]; m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)first_vertex); m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)last_vertex); } else { if (last_vertex != v[0]) continue; if (v[1] != first_vertex) { if (false == m_unique_tester.AddToList((ON__UINT_PTR)v[1])) continue; } } m_edge_chain.Append(eptr); m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)e); last_vertex = v[1]; if (last_vertex == first_vertex) break; }; return m_edge_chain.UnsignedCount(); } const ON_UUID ON_SubDEdgeChain::PersistentSubDId() const { return m_persistent_subd_id; } bool ON_SubDEdgeChain::HasPersistentEdgeIds() const { const unsigned count = this->EdgeCount(); return count > 0 && count == m_persistent_edge_id.UnsignedCount() && count == m_persistent_edge_orientation.UnsignedCount() ; } bool ON_SubDEdgeChain::HasRuntimeEdgePtrs() const { const unsigned count = this->EdgeCount(); return count > 0 && count == this->m_edge_chain.UnsignedCount() && m_subd_ref.SubD().EdgeCount() > 0 ; } bool ON_SubDEdgeChain::SetPersistentEdgeIdsFromRuntimeEdgePtrs() const { m_persistent_edge_id.SetCount(0); m_persistent_edge_orientation.SetCount(0); const unsigned count = (m_subd_ref.SubD().EdgeCount() > 0) ? m_edge_chain.UnsignedCount() : 0; bool rc = count > 0; if (rc) { m_persistent_edge_id.Reserve(count); m_persistent_edge_orientation.Reserve(count); for (unsigned i = 0; i < count; ++i) { const ON_SubDEdgePtr eptr = m_edge_chain[i]; const unsigned edge_id = eptr.EdgeId(); if (edge_id <= 0) break; if (false == eptr.IsActive()) break; m_persistent_edge_id.Append(edge_id); m_persistent_edge_orientation.Append(0 == eptr.EdgeDirection() ? 0 : 1); } if (count != m_persistent_edge_id.UnsignedCount() || count != m_persistent_edge_orientation.UnsignedCount()) { rc = false; m_persistent_edge_id.SetCount(0); m_persistent_edge_orientation.SetCount(0); } } return rc; } bool ON_SubDEdgeChain::SetRuntimeEdgePtrsFromPersistentSubD( ON_UUID persistent_subd_id, ON_SubDRef persistent_subd_ref ) { bool rc = true; m_edge_chain.SetCount(0); const unsigned count = m_persistent_edge_id.UnsignedCount(); const ON_SubD& subd = persistent_subd_ref.SubD(); if (count > 0 && count == m_persistent_edge_orientation.UnsignedCount() && subd.EdgeCount() > 0) { rc = false; ON_SimpleArray local_edge_chain(count); for (unsigned i = 0; i < count; ++i) { const ON_SubDEdge* e = subd.EdgeFromId(m_persistent_edge_id[i]); if (nullptr == e) break; const ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Create(e, (1 == m_persistent_edge_orientation[i]) ? 1 : 0); local_edge_chain.Append(eptr); } if (count == local_edge_chain.UnsignedCount()) { if (ON_SubDEdgeChain::IsValidEdgeChain(local_edge_chain, false)) { m_edge_chain = local_edge_chain; rc = true; } } } if (persistent_subd_ref.SubD().RuntimeSerialNumber() != m_subd_ref.SubD().RuntimeSerialNumber()) m_subd_ref = persistent_subd_ref; if (ON_UuidIsNotNil(persistent_subd_id) && 0 != ON_UuidCompare(m_persistent_subd_id, persistent_subd_id)) m_persistent_subd_id = persistent_subd_id; return rc; } bool ON_SubDEdgeChain::Write(class ON_BinaryArchive& archive) const { // This write has to work if a read happened but the m_edge_chain[] was never set. if (m_edge_chain.UnsignedCount() > 0) SetPersistentEdgeIdsFromRuntimeEdgePtrs(); const unsigned count = m_persistent_edge_id.UnsignedCount(); if (false == archive.BeginWrite3dmAnonymousChunk(1)) return false; bool rc = false; for (;;) { if (false == archive.WriteUuid(m_persistent_subd_id)) break; if (false == archive.WriteInt(count)) break; if (count > 0) { if (false == archive.WriteArray(m_persistent_edge_id)) break; if (false == archive.WriteArray(m_persistent_edge_orientation)) break; } rc = true; break; } if (false == archive.EndWrite3dmChunk()) rc = false; return rc; } bool ON_SubDEdgeChain::Read(class ON_BinaryArchive& archive) { *this = ON_SubDEdgeChain::Empty; int chunk_version = 0; if (false == archive.BeginRead3dmAnonymousChunk(&chunk_version)) return false; bool rc = false; for (;;) { if (chunk_version < 1) break; if (false == archive.ReadUuid(m_persistent_subd_id)) break; unsigned int count = 0; if (false == archive.ReadInt(&count)) break; if (count > 0) { if (false == archive.ReadArray(m_persistent_edge_id)) break; if (false == archive.ReadArray(m_persistent_edge_orientation)) break; } if (count != m_persistent_edge_id.UnsignedCount() || count != m_persistent_edge_orientation.UnsignedCount()) { m_persistent_edge_id.SetCount(0); m_persistent_edge_orientation.SetCount(0); } rc = true; break; } if (false == archive.EndRead3dmChunk()) rc = false; // At some point, the caller will need to find the subd for this edge chain and call SetRuntimeEdgePtrsFromPersistentSubD(). return rc; } void ON_SubDEdgeChain::Dump(class ON_TextLog& text_log) const { const unsigned edge_count = EdgeCount(); const ON__UINT64 subd_sn = m_subd_ref.SubD().EdgeCount() > 0 ? m_subd_ref.SubD().RuntimeSerialNumber() : 0; const bool bSubDIdIsNotNil = ON_UuidIsNotNil(m_persistent_subd_id); if (edge_count > 0 && (0 != subd_sn || bSubDIdIsNotNil)) { if (0 != subd_sn) { text_log.Print(L"SubD[%" PRIu64 "]", subd_sn); if (bSubDIdIsNotNil) { 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(m_persistent_subd_id); } text_log.Print("%u edges.\n", edge_count); const bool bPrintEdgePtr = (subd_sn != 0 && edge_count == m_edge_chain.UnsignedCount()); const bool bPrintPersistentEdgeId = false == bPrintEdgePtr && edge_count == m_persistent_edge_id.UnsignedCount() && edge_count == m_persistent_edge_orientation.UnsignedCount(); ON_TextLogIndent indent1(text_log); if (bPrintEdgePtr || bPrintPersistentEdgeId) { const wchar_t plus_minus[3] = { '+', '-', '?' }; for (unsigned i = 0; i < edge_count; ++i) { if (0 != i) text_log.Print(L", "); unsigned sign_dex = 2; unsigned edge_id = 0; if (bPrintEdgePtr) { const ON_SubDEdgePtr eptr = m_edge_chain[i]; sign_dex = 0 != eptr.EdgeDirection() ? 1 : 0; edge_id = eptr.EdgeId(); } else if (bPrintPersistentEdgeId) { sign_dex = 0 != m_persistent_edge_orientation[i] ? 1 : 0; edge_id = m_persistent_edge_id[i]; } text_log.Print(L"%lce%u", plus_minus[sign_dex], edge_id); if (i == 5 && edge_count > 15) { text_log.Print(L", ..."); i = edge_count - 5; } } } else { text_log.Print("Corrupt edge list."); } } else if( 0 == edge_count) { text_log.Print("Empty SubD edge chain."); } else { text_log.Print("Corrupt SubD edge chain."); } text_log.PrintNewLine(); } void ON_SubDEdgeChain::ClearEdgeChain() { m_edge_chain.SetCount(0); m_unique_tester.ClearList(); } unsigned int ON_SubDEdgeChain::EdgeCount() const { const unsigned edge_count = m_edge_chain.UnsignedCount(); if (edge_count > 0) return edge_count; const unsigned id_count = (ON_UuidIsNotNil(this->m_persistent_subd_id)) ? m_persistent_edge_id.UnsignedCount() : 0; if (id_count > 0 && id_count == m_persistent_edge_orientation.UnsignedCount()) return id_count; return 0; } void ON_SubDEdgeChain::SetStatusCheck( bool bEnableStatusCheck, ON_ComponentStatus status_check_pass, ON_ComponentStatus status_check_fail ) { m_bEnableStatusCheck = bEnableStatusCheck ? true : false; m_status_check_pass = status_check_pass; m_status_check_fail = status_check_fail; } bool ON_SubDEdgeChain::StatusCheckEnabled() const { return m_bEnableStatusCheck; } void ON_SubDEdgeChain::Reverse() { ON_SubDEdgeChain::ReverseEdgeChain(m_edge_chain); } const ON_SubDEdgePtr ON_SubDEdgeChain::FirstEdgePtr() const { return m_edge_chain.UnsignedCount() > 0 ? m_edge_chain[0] : ON_SubDEdgePtr::Null; } const ON_SubDEdgePtr ON_SubDEdgeChain::LastEdgePtr() const { return m_edge_chain.UnsignedCount() > 0 ? *(m_edge_chain.Last()) : ON_SubDEdgePtr::Null; } const ON_SubDEdgePtr ON_SubDEdgeChain::EdgePtr(int edge_index) const { return (edge_index >= 0 && edge_index < m_edge_chain.Count()) ? m_edge_chain[edge_index] : ON_SubDEdgePtr::Null; } const ON_SubDEdge* ON_SubDEdgeChain::FirstEdge() const { return FirstEdgePtr().Edge(); } const ON_SubDEdge* ON_SubDEdgeChain::LastEdge() const { return LastEdgePtr().Edge(); } const ON_SubDEdge* ON_SubDEdgeChain::Edge(int edge_index) const { return EdgePtr(edge_index).Edge(); } const ON_SubDVertex* ON_SubDEdgeChain::FirstVertex() const { return Vertex(0); } const ON_SubDVertex* ON_SubDEdgeChain::LastVertex() const { return Vertex(m_edge_chain.Count()); } const ON_SubDVertex* ON_SubDEdgeChain::Vertex(int vertex_index) const { const int edge_count = m_edge_chain.Count(); if ( vertex_index >= 0 && vertex_index <= edge_count && edge_count > 0 ) { return (vertex_index == edge_count) ? m_edge_chain[edge_count - 1].RelativeVertex(1) : m_edge_chain[vertex_index].RelativeVertex(0); } return nullptr; } const ON_3dPoint ON_SubDEdgeChain::FirstControlNetPoint() const { const ON_SubDVertex* v = FirstVertex(); return (nullptr != v) ? v->ControlNetPoint() : ON_3dPoint::NanPoint; } const ON_3dPoint ON_SubDEdgeChain::LastControlNetPoint() const { const ON_SubDVertex* v = LastVertex(); return (nullptr != v) ? v->ControlNetPoint() : ON_3dPoint::NanPoint; } const ON_3dPoint ON_SubDEdgeChain::ControlNetPoint(int vertex_index) const { const ON_SubDVertex* v = Vertex(vertex_index); return (nullptr != v) ? v->ControlNetPoint() : ON_3dPoint::NanPoint; } unsigned int ON_SubDEdgeChain::AddOneNeighbor( ON_ChainDirection direction, ON_SubD::ChainType chain_type ) { const unsigned int count0 = m_edge_chain.UnsignedCount(); if (count0 <= 0 || IsClosedLoop() ) return 0; ON_SubDEdgePtr eptr; const ON_SubDEdge* e; const ON_SubDVertex* v; const ON_SubDVertex* chain_ends[2] = { FirstVertex() ,LastVertex() }; eptr = (ON_ChainDirection::Previous != direction) ? ON_SubDEdgeChain::EdgeChainNeighbor(LastEdgePtr(), ON_ChainDirection::Next, chain_type, m_bEnableStatusCheck, m_status_check_pass, m_status_check_fail) : ON_SubDEdgePtr::Null; e = eptr.Edge(); v = eptr.RelativeVertex(0); if ( nullptr != v && v == chain_ends[1] && false == InChain(e) ) { v = eptr.RelativeVertex(1); if (v == chain_ends[0] || m_unique_tester.AddToList((ON__UINT_PTR)v)) { m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)e); m_edge_chain.Append(eptr); } } eptr = (ON_ChainDirection::Next != direction) ? ON_SubDEdgeChain::EdgeChainNeighbor(FirstEdgePtr(), ON_ChainDirection::Previous, chain_type, m_bEnableStatusCheck, m_status_check_pass, m_status_check_fail) : ON_SubDEdgePtr::Null; e = eptr.Edge(); v = eptr.RelativeVertex(1); if ( nullptr != v && v == chain_ends[0] && false == InChain(e) ) { v = eptr.RelativeVertex(0); if (v == chain_ends[1] || m_unique_tester.AddToList((ON__UINT_PTR)v)) { m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)e); m_edge_chain.Insert(0, eptr); } } return m_edge_chain.UnsignedCount() - count0; } unsigned int ON_SubDEdgeChain::AddAllNeighbors( ON_ChainDirection direction, ON_SubD::ChainType chain_type ) { const unsigned int count0 = m_edge_chain.UnsignedCount(); if (count0 <= 0 || IsClosedLoop()) return 0; if (ON_ChainDirection::Previous != direction) while (1 == AddOneNeighbor(ON_ChainDirection::Next, chain_type)) {} if (ON_ChainDirection::Next != direction) while (1 == AddOneNeighbor(ON_ChainDirection::Previous, chain_type)) {} return m_edge_chain.UnsignedCount() - count0; } unsigned int ON_SubDEdgeChain::AddEdge( const ON_SubDEdge* edge ) { const unsigned int count0 = m_edge_chain.UnsignedCount(); if (count0 <= 0) return 0; if ( nullptr == edge || nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1] || edge->m_vertex[0] == edge->m_vertex[1] ) return 0; const ON_SubDVertex* v[2] = { FirstVertex(),LastVertex() }; if (v[0] == v[1]) return 0; if ( m_bEnableStatusCheck && false == ON_ComponentStatus::StatusCheck(edge->m_status, m_status_check_pass, m_status_check_fail)) return 0; ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Null; if (v[1] == edge->m_vertex[0]) eptr = ON_SubDEdgePtr::Create(edge, 0); else if (v[1] == edge->m_vertex[1]) eptr = ON_SubDEdgePtr::Create(edge, 1); else if (v[0] == edge->m_vertex[1]) eptr = ON_SubDEdgePtr::Create(edge, 0); else if (v[0] == edge->m_vertex[0]) eptr = ON_SubDEdgePtr::Create(edge, 1); else return 0; if (m_unique_tester.InList((ON__UINT_PTR)edge)) return 0; if (v[1] == eptr.RelativeVertex(0) ) { if (v[0] == eptr.RelativeVertex(1) || m_unique_tester.AddToList((ON__UINT_PTR)eptr.RelativeVertex(1))) { m_edge_chain.Append(eptr); m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)edge); } } else if (v[0] == eptr.RelativeVertex(1) ) { if (v[1] == eptr.RelativeVertex(0) || m_unique_tester.AddToList((ON__UINT_PTR)eptr.RelativeVertex(0))) { m_edge_chain.Insert(0, eptr); m_unique_tester.ExpertAddNewToList((ON__UINT_PTR)edge); } } return m_edge_chain.UnsignedCount() - count0; } unsigned int ON_SubDEdgeChain::RemoveEdges( const ON_SubDEdge* first_edge, const ON_SubDEdge* last_edge ) { unsigned int count0 = m_edge_chain.UnsignedCount(); unsigned int i0 = 0; unsigned int i1 = count0; if (nullptr != first_edge) { while (i0 < count0 && first_edge != m_edge_chain[i0].Edge()) ++i0; } if (nullptr != last_edge) { while (i1 > i0 && last_edge != m_edge_chain[i1 - 1].Edge()) --i1; } const unsigned int count1 = i1 - i0; if (count1 >= count0) return 0; if (i0 > 0) { for (unsigned int i = i0; i < i1; i++) m_edge_chain[i - i0] = m_edge_chain[i]; } m_edge_chain.SetCount(count1); m_unique_tester.ClearList(); for (unsigned int i = 0; i < count1; i++) { m_unique_tester.AddToList((ON__UINT_PTR)m_edge_chain[i].Edge()); m_unique_tester.AddToList((ON__UINT_PTR)m_edge_chain[i].RelativeVertex(0)); } if ( FirstVertex() != LastVertex() ) m_unique_tester.AddToList((ON__UINT_PTR)LastVertex()); return count0 - count1; } void ON_SubDEdgeChain::ReverseEdgeChain( ON_SimpleArray< ON_SubDEdgePtr >& edge_chain ) { ON_SubDEdgeChain::ReverseEdgeChain(edge_chain.Array(), edge_chain.UnsignedCount()); } void ON_SubDEdgeChain::ReverseEdgeChain( ON_SubDEdgePtr* edge_chain, size_t edge_count ) { if (edge_count <= 0 || nullptr == edge_chain) return; ON_SubDEdgePtr* p0 = edge_chain; ON_SubDEdgePtr* p1 = p0 + (edge_count - 1); while ( p0 < p1) { ON_SubDEdgePtr eptr = p0->Reversed(); *p0 = p1->Reversed(); *p1 = eptr; ++p0; --p1; } if (p0 == p1) *p0 = p0->Reversed(); } bool ON_SubDEdgeChain::IsValidEdgeChain( const ON_SimpleArray< ON_SubDEdgePtr >& edge_chain, bool bCheckForDuplicateEdges ) { return ON_SubDEdgeChain::IsValidEdgeChain(edge_chain.Array(), edge_chain.UnsignedCount(), bCheckForDuplicateEdges); } bool ON_SubDEdgeChain::IsValidEdgeChain( const ON_SubDEdgePtr* edge_chain, size_t edge_count, bool bCheckForDuplicateEdges ) { if (edge_count <= 0) return true; if (nullptr == edge_chain) return false; const ON_SubDVertex* first_vertex = edge_chain->RelativeVertex(0); if (nullptr == first_vertex) return false; const ON_SubDVertex* v = first_vertex; const ON_SubDEdgePtr* p0 = edge_chain; const ON_SubDEdgePtr* p1 = edge_chain+1; for (const ON_SubDEdgePtr* p = p0; p < p1; ++p) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(p->m_ptr); if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) return false; ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(p->m_ptr); const ON_SubDVertex* v0 = e->m_vertex[edir]; const ON_SubDVertex* v1 = e->m_vertex[1 - edir]; if (v0 != v || nullptr == v1 || v0 == v1) return false; v = v1; } if (bCheckForDuplicateEdges) { const ON_SubDVertex* last_vertex = v; ON_UniqueTester tester; for (const ON_SubDEdgePtr* p = p0; p < p1; ++p) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(p->m_ptr); if (false == tester.AddToList((ON__UINT_PTR)e)) return false; // duplicate edge if (false == tester.AddToList((ON__UINT_PTR)e->m_vertex[ON_SUBD_EDGE_DIRECTION(p->m_ptr)])) return false; // duplicate vertex } if (first_vertex != last_vertex) { if (false == tester.AddToList((ON__UINT_PTR)last_vertex)) return false; // duplicate vertex } } return true; } class ON_SubDMeshImpl* ON_SubDMesh::SubLimple() const { return m_impl_sp.get(); } unsigned int ON_SubDMesh::SubLimpleUseCount() const { return (unsigned int)(m_impl_sp.use_count()); } bool ON_SubD::IsSolid() const { bool bIsManifold = false; bool bIsOriented = false; bool bHasBoundary = false; int solid_orientation = 0; ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation); return (bIsManifold && bIsOriented && false == bHasBoundary); } int ON_SubD::SolidOrientation() const { bool bIsManifold = false; bool bIsOriented = false; bool bHasBoundary = false; int solid_orientation = 0; ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation); return solid_orientation; } bool ON_SubD::IsManifold( bool& bIsOriented, bool& bHasBoundary ) const { bool bIsManifold = false; bIsOriented = false; bHasBoundary = false; int solid_orientation = 0; ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation); return bIsManifold; } bool ON_SubD::IsManifold() const { bool bIsOriented = false; bool bHasBoundary = false; return IsManifold(bIsOriented, bHasBoundary); } #ifdef ON_DEBUG static bool Internal_UnrollChain(const ON_SimpleArray& chain) { const unsigned imax{chain.UnsignedCount()}; ON_SimpleArray reversed_dirs{(size_t)imax}; ON_SimpleArray edges{(size_t)imax}; ON_SimpleArray vertices{(size_t)2 * (size_t)imax}; ON_SimpleArray edge_ids{(size_t)imax}; ON_SimpleArray vertice_ids{(size_t)2 * (size_t)imax}; const unsigned int max_id = 0xFFFFFFFFU; for (unsigned i = 0; i < imax; ++i) { ON_SubDEdgePtr eptr = chain[i]; const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr); if (nullptr == e) { reversed_dirs.Append(false); edges.Append(nullptr); vertices.Append(nullptr); vertices.Append(nullptr); edge_ids.Append(max_id); vertice_ids.Append(max_id); vertice_ids.Append(max_id); continue; } edges.Append(e); edge_ids.Append(e->m_id); ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(eptr.m_ptr); reversed_dirs.Append(edir == 0 ? false : true); const ON_SubDVertex* v[2] = {e->m_vertex[edir], e->m_vertex[1 - edir]}; vertices.Append(v[0]); vertices.Append(v[1]); vertice_ids.Append(v[0] == nullptr ? max_id : v[0]->m_id); vertice_ids.Append(v[1] == nullptr ? max_id : v[1]->m_id); } return true; } static bool Internal_VerifyEdgeChain( const ON_SimpleArray& chain, ON_2udex& chaindex, unsigned* edge_count = nullptr) { bool valid_chain{true}; unsigned valid_edge_count{0}; const unsigned jmax{chain.UnsignedCount()}; ON_SubDEdgePtr eptr = chain[chaindex.i]; const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr); if (nullptr == e) { if (edge_count != nullptr) *edge_count = 0; return false; } ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(eptr.m_ptr); const ON_SubDVertex* v[2] = {e->m_vertex[edir], e->m_vertex[1 - edir]}; if (nullptr == v[0] || nullptr == v[1] || v[0] == v[1]) { if (edge_count != nullptr) *edge_count = 0; return false; } ++valid_edge_count; for (chaindex.j = chaindex.i + 1; chaindex.j < jmax; ++chaindex.j) { valid_chain = false; eptr = chain[chaindex.j]; e = ON_SUBD_EDGE_POINTER(eptr.m_ptr); if (nullptr == e) { // End of chain marker valid_chain = true; if (chaindex.j < jmax) ++chaindex.j; break; } if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] || e->m_vertex[0] == e->m_vertex[1]) { break; } edir = ON_SUBD_EDGE_DIRECTION(eptr.m_ptr); if (v[1] != e->m_vertex[edir]) { if (edge_count == nullptr) valid_chain = true; break; } v[1] = e->m_vertex[1 - edir]; ++valid_edge_count; valid_chain = true; } if (edge_count != nullptr && *edge_count != valid_edge_count) { *edge_count = valid_edge_count; return false; } return valid_edge_count > 0 && valid_chain; } static bool Internal_CountAndVerifyEdgeChains( const ON_SimpleArray& sorted_edges, unsigned* chain_count = nullptr) { if (chain_count != nullptr && chain_count == 0) return false; unsigned valid_chain_count{0}; bool valid_chains{false}; const unsigned edge_count{sorted_edges.UnsignedCount()}; ON_2udex chaindex{0, edge_count}; for (chaindex = {0, edge_count}; chaindex.i < edge_count; chaindex.i = (chaindex.j > chaindex.i) ? chaindex.j : (chaindex.i + 1)) { valid_chains = false; if (!Internal_VerifyEdgeChain(sorted_edges, chaindex)) break; ++valid_chain_count; valid_chains = true; } if (chain_count != nullptr && (*chain_count != valid_chain_count || chaindex.j != edge_count)) { *chain_count = valid_chain_count; return false; } return valid_chain_count > 0 && valid_chains; } #endif ////////////////////////// // // Better edge sorter // static unsigned Internal_MuchImprovedSortEdgesIntoChains( const ON__UINT_PTR* unsorted_edges, size_t unsorted_edges_count, ON_SimpleArray& sorted_edges ) { // NOTE: sorted_edges and unsorted_edges should not point to the same array by this point sorted_edges.SetCount(0); sorted_edges.Reserve(unsorted_edges_count + 128U); if (unsorted_edges_count < 1 || nullptr == unsorted_edges) return 0; const unsigned unsorted_count = (unsigned)unsorted_edges_count; unsigned chain_count = 0; // Set MarkBits = 0 for every vertex and for every edge attached to a vertex in the edge chain. for (unsigned i = 0; i < unsorted_count; ++i) { const ON_SubDEdge* e = (const ON_SubDEdge*)(ON_SUBD_COMPONENT_POINTER_MASK & unsorted_edges[i]); if (nullptr == e) continue; e->ClearMarkBits(); if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) continue; if (e->m_vertex[0] == e->m_vertex[1]) continue; for (unsigned evi = 0; evi < 2; ++evi) { const ON_SubDVertex* v = e->m_vertex[evi]; v->ClearMarkBits(); for (unsigned short vei = 0; vei < v->m_edge_count; ++vei) { const ON_SubDEdge* ve = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr); if (nullptr != ve) ve->ClearMarkBits(); } } } // Set e->MarkBits = 1 for every valid edge in the edge chain. for (unsigned i = 0; i < unsorted_count; ++i) { const ON_SubDEdge* e = (const ON_SubDEdge*)(ON_SUBD_COMPONENT_POINTER_MASK & unsorted_edges[i]); if (nullptr == e) continue; if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) continue; if (e->m_vertex[0] == e->m_vertex[1]) continue; e->SetMarkBits(1); } // Set v->MarkBits() = number of unsorted_edges[] that have v as a vertex with 3 meaning 3 or more. for (unsigned i = 0; i < unsorted_count; ++i) { const ON_SubDEdge* e = (const ON_SubDEdge*)(ON_SUBD_COMPONENT_POINTER_MASK & unsorted_edges[i]); if (nullptr == e) continue; if (0 == e->MarkBits()) continue; for (unsigned evi = 0; evi < 2; ++evi) { const ON_SubDVertex* v = e->m_vertex[evi]; const ON__UINT8 ecount = v->MarkBits(); if (ecount < 3) v->SetMarkBits(ecount + 1); } } // Go through the unsorted edges and use the MarkBits values to quickly build the edge chains. ON_SimpleArray chain((int)unsorted_edges_count); for (unsigned i = 0; i < unsorted_edges_count; ++i) { const ON_SubDEdge* seed_edge = (const ON_SubDEdge*)(ON_SUBD_COMPONENT_POINTER_MASK & unsorted_edges[i]); if (nullptr == seed_edge || 1 != seed_edge->MarkBits()) continue; // seed_edge is invalid or already assigned to an edge chain // this edge pointer direcion will be reversed when the for loop evi = 1. chain.SetCount(0); chain.Append(ON_SubDEdgePtr::Create(seed_edge, 1)); // dir=1 is correct. The direcion will be reversed in the for loop below when evi = 1. seed_edge->ClearMarkBits(); // seed_edge is now in a chain for (unsigned evi = 0; evi < 2; ++evi) { if (1 == evi) { ON_SubDEdgeChain::ReverseEdgeChain(chain); if (chain[0].RelativeVertex(0) == chain[chain.UnsignedCount() - 1].RelativeVertex(1)) break; // we found a closed loop } const ON_SubDVertex* v = chain[chain.UnsignedCount() - 1].RelativeVertex(1); if (nullptr == v || 2 != v->MarkBits()) continue; // edge chain cannot continue through v for (const ON_SubDVertex* v1 = nullptr; nullptr != v && 2 == v->MarkBits(); v = v1) { v->ClearMarkBits(); // clearing v->MarkBits indicates this v has been used. v1 = nullptr; for (unsigned short vei = 0; vei < v->m_edge_count; ++vei) { const ON_SubDEdge* ve = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr); if (nullptr == ve) continue; if (1 != ve->MarkBits()) continue; // this ve was not in unsorted_edges[] or has already been assigned to a chain. if (v == ve->m_vertex[0]) { ve->SetMarkBits(0); // MarkBits() = 0 indicates ve is now in a chain chain.Append(ON_SubDEdgePtr::Create(ve, 0)); v1 = ve->m_vertex[1]; } else if (v == ve->m_vertex[1]) { ve->SetMarkBits(0); // MarkBits() = 0 indicates ve is now in a chain chain.Append(ON_SubDEdgePtr::Create(ve, 1)); v1 = ve->m_vertex[0]; } else { ON_SUBD_ERROR("Corrupt edge/vertex topology."); } } v = v1; } } #ifdef ON_DEBUG unsigned temp_chain_edge_count{chain.UnsignedCount()}; ON_2udex temp_udex{0U, (unsigned)(chain.UnsignedCount() - 1)}; if (!Internal_VerifyEdgeChain(chain, temp_udex, &temp_chain_edge_count)) { temp_chain_edge_count = 0; } Internal_UnrollChain(chain); #endif sorted_edges.Append(chain.Count(), chain.Array()); sorted_edges.Append(ON_SubDEdgePtr::Null); // end of chain marker ++chain_count; } // clear all the mark bits that may still be set when the input is corrupt. for (size_t i = 0; i < unsorted_edges_count; ++i) { const ON_SubDEdge* e = (const ON_SubDEdge*)(ON_SUBD_COMPONENT_POINTER_MASK & unsorted_edges[i]); if (nullptr == e) continue; e->ClearMarkBits(); if (nullptr != e->m_vertex[0]) e->m_vertex[0]->ClearMarkBits(); if (nullptr != e->m_vertex[1]) e->m_vertex[1]->ClearMarkBits(); } #ifdef ON_DEBUG Internal_UnrollChain(sorted_edges); if (!Internal_CountAndVerifyEdgeChains(sorted_edges, &chain_count)) { return chain_count; } #endif return chain_count; } unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( const ON_SimpleArray< ON_SubDEdgePtr >& unsorted_edges, ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ) { unsigned int chain_count = 0; if (unsorted_edges.Array() == sorted_edges.Array()) { const ON_SimpleArray< ON_SubDEdgePtr > local_unsorted_edges(unsorted_edges); chain_count = Internal_MuchImprovedSortEdgesIntoChains( (const ON__UINT_PTR * )local_unsorted_edges.Array(), local_unsorted_edges.UnsignedCount(), sorted_edges ); } else { chain_count = Internal_MuchImprovedSortEdgesIntoChains( (const ON__UINT_PTR*)unsorted_edges.Array(), unsorted_edges.UnsignedCount(), sorted_edges ); } return chain_count; } unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( const ON_SimpleArray< const ON_SubDEdge* >& unsorted_edges, ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ) { unsigned int chain_count = 0; if ((const void*)unsorted_edges.Array() == (const void*)sorted_edges.Array()) { const ON_SimpleArray< const ON_SubDEdge* > local_unsorted_edges(unsorted_edges); chain_count = Internal_MuchImprovedSortEdgesIntoChains( (const ON__UINT_PTR*)local_unsorted_edges.Array(), local_unsorted_edges.UnsignedCount(), sorted_edges ); } else { chain_count = Internal_MuchImprovedSortEdgesIntoChains( (const ON__UINT_PTR*)unsorted_edges.Array(), unsorted_edges.UnsignedCount(), sorted_edges ); } return chain_count; } unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( const ON_SimpleArray< ON_SubDComponentPtr >& unsorted_edges, ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ) { unsigned int chain_count = 0; if ((const void*)unsorted_edges.Array() == (const void*)sorted_edges.Array()) { const ON_SimpleArray< ON_SubDComponentPtr > local_unsorted_edges(unsorted_edges); chain_count = Internal_MuchImprovedSortEdgesIntoChains( (const ON__UINT_PTR*)local_unsorted_edges.Array(), local_unsorted_edges.UnsignedCount(), sorted_edges ); } else { chain_count = Internal_MuchImprovedSortEdgesIntoChains( (const ON__UINT_PTR*)unsorted_edges.Array(), unsorted_edges.UnsignedCount(), sorted_edges ); } return chain_count; } unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( const ON_SubD& subd, const ON_SimpleArray< ON_COMPONENT_INDEX >& unsorted_edges, ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ) { const unsigned unsorted_count = unsorted_edges.UnsignedCount(); ON_SimpleArray< const ON_SubDEdge* > local_unsorted_edges(unsorted_count); for (unsigned i = 0; i < unsorted_count; ++i) { const ON_COMPONENT_INDEX ci = unsorted_edges[i]; if (ON_COMPONENT_INDEX::TYPE::subd_edge != ci.m_type) continue; if (0 == ci.m_index || -1 == ci.m_index) continue; const ON_SubDEdge* e = subd.EdgeFromId((unsigned)ci.m_index); if (nullptr == e) continue; local_unsorted_edges.Append(e); } return Internal_MuchImprovedSortEdgesIntoChains( (const ON__UINT_PTR*)local_unsorted_edges.Array(), unsorted_edges.UnsignedCount(), sorted_edges ); } bool ON_SubDEdgeChain::IsSingleEdgeChain( const ON_SimpleArray& edges, bool& bIsClosed, bool& bIsSorted ) { bIsSorted = false; bIsClosed = false; const unsigned count = edges.UnsignedCount(); if (count <= 1U) { const ON_SubDEdge* e = (1U == count) ? edges[0].Edge() : nullptr; if ( nullptr != e && nullptr != e->m_vertex[0] && nullptr != e->m_vertex[1] && e->m_vertex[0] != e->m_vertex[1] ) { // 1 valid edge bIsSorted = true; return true; } return false; } // save MarkBits() values on edges[] components so they can be restored after testing. // Internal_MuchImprovedSortEdgesIntoChains() uses MarkBits(). union { ON__UINT32 u32; ON__UINT8 u8[4]; } u; ON_SimpleArray saved_markbits(count); for (unsigned i = 0; i < count; ++i) { u.u32 = 0; const ON_SubDEdge* e = edges[i].Edge(); if (nullptr != e) { u.u8[0] = e->MarkBits(); if (e->m_vertex[0]) u.u8[1] = e->m_vertex[0]->MarkBits(); if (e->m_vertex[1]) u.u8[2] = e->m_vertex[1]->MarkBits(); } saved_markbits.Append(u.u32); } bool bIsSingleEdgeChain = false; for (;;) { ON_SimpleArray sorted_edges; const unsigned chain_count = Internal_MuchImprovedSortEdgesIntoChains( (const ON__UINT_PTR*)edges.Array(), count, sorted_edges ); if (1U != chain_count) break; // edges[] is not a contiguouse set of edges or it self intersects. if (count + 1U != sorted_edges.UnsignedCount()) break; // edges[] contained null edges // determine edges[] sorts into a closed edge chain. if (count >= 3 && sorted_edges[0].RelativeVertex(0) == sorted_edges[count - 1].RelativeVertex(1)) bIsClosed = true; // Determine edges[] is was already sorted. // Note that this test does not detect self interections and that's // why ON_SubDEdgeChain::SortEdgesIntoEdgeChains() is called above. const ON_SubDVertex* v0 = edges[0].RelativeVertex(0); if (nullptr != v0) { const ON_SubDVertex* edges_ends[2] = { edges[0].RelativeVertex(0), edges[count - 1].RelativeVertex(1) }; const ON_SubDVertex* sorted_ends[2] = { sorted_edges[0].RelativeVertex(0), sorted_edges[count - 1].RelativeVertex(1) }; if (bIsClosed) { if (edges_ends[0] == edges_ends[1] && sorted_ends[0] == sorted_ends[1]) bIsSorted = true; } else { // sorted_eges[] may be reversed. if (edges_ends[0] == sorted_ends[0] && edges_ends[1] == sorted_ends[1]) bIsSorted = true; else if (edges_ends[0] == sorted_ends[1] && edges_ends[1] == sorted_ends[0]) bIsSorted = true; } for (unsigned i = 0; bIsSorted && i < count; ++i) { const ON_SubDVertex* ev[2] = { edges[i].RelativeVertex(0), edges[i].RelativeVertex(1) }; if (v0 != ev[0] || nullptr == ev[1] || ev[0] == ev[1]) { bIsSorted = false; break; } v0 = ev[1]; } } bIsSingleEdgeChain = true; break; } // restore MarkBits() values on edges[] components. for (unsigned i = 0; i < count; ++i) { u.u32 = saved_markbits[i]; const ON_SubDEdge* e = edges[i].Edge(); if (nullptr != e) { e->SetMarkBits(u.u8[0]); if (e->m_vertex[0]) e->m_vertex[0]->SetMarkBits(u.u8[1]); if (e->m_vertex[1]) e->m_vertex[1]->SetMarkBits(u.u8[2]); } } return bIsSingleEdgeChain; } bool ON_SubDEdgeChain::IsSingleEdgeChain( const ON_SimpleArray& edges ) { bool bIsClosed = false; bool bIsSorted = false; return ON_SubDEdgeChain::IsSingleEdgeChain(edges, bIsClosed, bIsSorted); } class ON_SubDEdgePtrLink { public: ON_SubDEdgePtrLink() = default; ~ON_SubDEdgePtrLink() = default; ON_SubDEdgePtrLink(const ON_SubDEdgePtrLink&) = default; ON_SubDEdgePtrLink& operator=(const ON_SubDEdgePtrLink&) = default; public: static const ON_SubDEdgePtrLink Empty; public: ON_SubDEdgePtr m_ep = ON_SubDEdgePtr::Null; unsigned int m_index = 0; unsigned int m_nbr_index = 0; static int CompareVertex( const ON_SubDEdgePtrLink* lhs, const ON_SubDEdgePtrLink* rhs ) { const ON_SubDVertex* lhs_v = lhs->m_ep.RelativeVertex(0); const ON_SubDVertex* rhs_v = rhs->m_ep.RelativeVertex(0); if (lhs_v < rhs_v) return -1; if (lhs_v > rhs_v) return 1; return 0; } static int CompareIndex( const ON_SubDEdgePtrLink* lhs, const ON_SubDEdgePtrLink* rhs ) { const unsigned int lhs_i = lhs->m_index; const unsigned int rhs_i = rhs->m_index; if (lhs_i < rhs_i) return -1; if (lhs_i > rhs_i) return 1; return 0; } static void Resolve3OrMoreEdges( const unsigned int unset_nbr1_index, unsigned int count, const ON_SubDVertex* v, ON_SubDEdgePtrLink* links ); }; const ON_SubDEdgePtrLink ON_SubDEdgePtrLink::Empty; void ON_SubDEdgePtrLink::Resolve3OrMoreEdges( const unsigned int unset_nbr1_index, unsigned int count, const ON_SubDVertex* v, ON_SubDEdgePtrLink* links ) { // If the case can't be resolved by Resolve3OrMoreEdges(), // then the vertex will not appear in the middle of a chain. if (count < 3 || nullptr == v || count != (unsigned int)v->m_edge_count) return; switch (count) { case 3: if (false == v->IsCrease() && false == v->IsDart()) return; break; case 4: if (false == v->IsCrease() && false == v->IsSmooth()) return; break; default: if (false == v->IsCrease()) return; break; } const ON_SubDEdge* link_edges[4] = {}; const ON_SubDEdge* vertex_edges[4] = {}; unsigned int crease_edge_count = 0; unsigned int smooth_edge_count = 0; unsigned int smooth_edge_link_index[4] = {}; unsigned int crease_edge_link_index[4] = {}; for (unsigned int j = 0; j < count; j++) { const ON_SubDEdge* e = links[j].m_ep.Edge(); if (nullptr == e) return; const ON_SubDEdge* ve = v->Edge(j); if (nullptr == ve) return; if (j < 4) { link_edges[j] = e; vertex_edges[j] = ve; } if (e->IsSmooth() && 2 == e->m_face_count) { if ( smooth_edge_count < 4) smooth_edge_link_index[smooth_edge_count] = j; ++smooth_edge_count; } else if (e->IsCrease()) { if (crease_edge_count < 4) crease_edge_link_index[crease_edge_count] = j; ++crease_edge_count; } else return; } if ( 2 == crease_edge_count && v->IsCrease() ) { // Link the two creased edges. // The vertex will be interior in a chain and the edges // will be next to each other. links[crease_edge_link_index[0]].m_nbr_index = links[crease_edge_link_index[1]].m_index; links[crease_edge_link_index[1]].m_nbr_index = links[crease_edge_link_index[0]].m_index; if (1 == smooth_edge_count) { // this edge will be at the end of a chain. links[smooth_edge_link_index[0]].m_nbr_index = unset_nbr1_index; } } if (2 == smooth_edge_count) { // Link the two smooth edges. // The vertex will be interior in a chain and the edges // will be next to each other. links[smooth_edge_link_index[0]].m_nbr_index = links[smooth_edge_link_index[1]].m_index; links[smooth_edge_link_index[1]].m_nbr_index = links[smooth_edge_link_index[0]].m_index; if (1 == crease_edge_count) { // this edge will be at the end of a chain links[crease_edge_link_index[0]].m_nbr_index = unset_nbr1_index; } } if ( 4 != count || 4 != smooth_edge_count || 0 != crease_edge_count || 4 != v->m_face_count || 4 != v->m_edge_count || false == v->IsSmooth() ) return; // make sure vertex_edges[] and link_edges[] are the same list. unsigned int match_count = 0; for (unsigned int j = 0; j == match_count && j < count; j++) { for (unsigned int k = 0; k < count; k++) { if (vertex_edges[k] == link_edges[j]) { vertex_edges[k] = nullptr; match_count++; break; } } } if (match_count != count) return; // vertex has 4 faces and 4 smooth edges. Link opposite edges. const ON_SubDFace* edge_faces[4][2]; for (unsigned int j = 0; j < 4; j++) { edge_faces[j][0] = link_edges[j]->Face(0); edge_faces[j][1] = link_edges[j]->Face(1); if (nullptr == edge_faces[j][0] || nullptr == edge_faces[j][1]) return; } ON_2udex pairs[2]; unsigned int pair_count = 0; ON_2udex pair; for (pair.i = 0; pair.i < 4; ++pair.i) for (pair.j = pair.i+1; pair.j < 4; ++pair.j) { if ( edge_faces[pair.i][0] != edge_faces[pair.j][0] && edge_faces[pair.i][0] != edge_faces[pair.j][1] && edge_faces[pair.i][1] != edge_faces[pair.j][0] && edge_faces[pair.i][1] != edge_faces[pair.j][1] ) { // the associated edges share no faces. if ( pair_count < 2) pairs[pair_count] = pair; if (++pair_count > 2) break; } } if (2 == pair_count) { links[pairs[0].i].m_nbr_index = links[pairs[0].j].m_index; links[pairs[0].j].m_nbr_index = links[pairs[0].i].m_index; links[pairs[1].i].m_nbr_index = links[pairs[1].j].m_index; links[pairs[1].j].m_nbr_index = links[pairs[1].i].m_index; } return; } unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( const ON_SubD& subd, const ON_SimpleArray< ON_COMPONENT_INDEX >& unsorted_edges, unsigned int minimum_chain_length, ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ) { const unsigned count = unsorted_edges.UnsignedCount(); ON_SimpleArray< const ON_SubDEdge* > a(count); for (unsigned i = 0; i < count; ++i) { const ON_SubDEdge* e = subd.EdgeFromComponentIndex(unsorted_edges[i]); if (nullptr != e) a.Append(e); } return SortEdgesIntoEdgeChains(a, minimum_chain_length, sorted_edges); } unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( const ON_SimpleArray< ON_SubDComponentPtr >& unsorted_edges, unsigned int minimum_chain_length, ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ) { const unsigned count = unsorted_edges.UnsignedCount(); ON_SimpleArray< const ON_SubDEdge* > a(count); for (unsigned i = 0; i < count; ++i) a.Append(unsorted_edges[i].Edge()); return ON_SubDEdgeChain::SortEdgesIntoEdgeChains(a, minimum_chain_length, sorted_edges); } unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( const ON_SimpleArray< const ON_SubDEdge* >& unsorted_edges, unsigned int minimum_chain_length, ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ) { const unsigned int unsorted_edge_count = unsorted_edges.Count(); ON_SimpleArray< ON_SubDEdgePtr > unsorted_eptrs(unsorted_edge_count); for (unsigned i = 0; i < unsorted_edge_count; ++i) { const ON_SubDEdge* e = unsorted_edges[i]; if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] || e->m_vertex[0] == e->m_vertex[1]) continue; ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Create(e, 0); if (1 == e->m_face_count && 0 == ON_SUBD_FACE_DIRECTION(e->m_face2[0].m_ptr)) eptr = eptr.Reversed(); unsorted_eptrs.Append(eptr); } return ON_SubDEdgeChain::SortEdgesIntoEdgeChains(unsorted_eptrs, minimum_chain_length, sorted_edges); } unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( const ON_SimpleArray< ON_SubDEdgePtr >& unsorted_edges, unsigned int minimum_chain_length, ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ) { // NOTE: // unsorted_edges[] and sorted_edges[] may reference the same array. ////const ON_SubDEdge* ee[2] = {}; ////const ON_SubDVertex* vv[2] = {}; const unsigned int unsorted_edge_count = unsorted_edges.Count(); if (unsorted_edge_count <= 0) { sorted_edges.SetCount(0); return 0; } ON_SimpleArray< ON_SubDEdgePtrLink > links(2*unsorted_edge_count); const unsigned int unset_nbr1_index = 0xFFFFFFFEU; const unsigned int unset_nbrX_index = unset_nbr1_index+1; ON_SubDEdgePtrLink epl; epl.m_nbr_index = unset_nbrX_index; for (unsigned int i = 0; i < unsorted_edge_count; i++) { ON_SubDEdgePtr ep = unsorted_edges[i]; const ON_SubDEdge* e = ep.Edge(); if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] || e->m_vertex[0] == e->m_vertex[1]) continue; // unsorted_edges[i] has 2 links with m_index = 2*i and m_index = 2*+1. // links with even m_index have opposite orientation as unsorted_edges[]. // links[2*i].m_ep.RelativeVertex(0) = unsorted_edges[i].RelativeVertex(1) epl.m_ep = ep.Reversed(); links.Append(epl); ++epl.m_index; ////ee[0] = epl.m_ep.Edge(); ////vv[1] = epl.m_ep.RelativeVertex(0); // links with odd m_index have same orientation as unsorted_edges[]. // links[2*i+1].m_ep.RelativeVertex(0) = unsorted_edges[i].RelativeVertex(0) epl.m_ep = ep; links.Append(epl); ++epl.m_index; ////ee[1] = epl.m_ep.Edge(); ////vv[0] = epl.m_ep.RelativeVertex(0); ////if (e != ee[0] || e != ee[1] || vv[0] == vv[1]) //// return false; } // NOTE: // unsorted_edges[] and sorted_edges[] may reference the same array. // At this point, I'm finished with unsorted_edges[] so it's ok to // modify sorted_edges[] here. sorted_edges.SetCount(0); sorted_edges.Reserve(unsorted_edge_count); // sort links by ON_SubDEdgePtr.RelativeVertex(0) and set m_nbr_index links.QuickSort(ON_SubDEdgePtrLink::CompareVertex); // link_count = even number const unsigned int link_count = links.UnsignedCount(); unsigned int i1 = link_count; for (unsigned int i0 = 0; i0 < link_count; i0 = i1) { ON_SubDEdgePtrLink& epl0 = links[i0]; const ON_SubDVertex* v = epl0.m_ep.RelativeVertex(0); for (i1 = i0 + 1; i1 < link_count; ++i1) { if (0 != ON_SubDEdgePtrLink::CompareVertex(&epl0, &links[i1])) break; } if (nullptr == v) { ON_SUBD_ERROR("Bug in code that creates the links[] array."); continue; } if (v->IsCorner()) { // These edges will be at the ends of chains. while (i0 < i1) links[i0++].m_nbr_index = unset_nbr1_index; continue; } if (i0 + 1 == i1) { // The vertex is referenced by exactly 1 edge in unsorted_edges[] // This edge will appear in sorted_edges[] at the start or end of a chain. epl0.m_nbr_index = unset_nbr1_index; continue; } if (i0 + 2 == i1) { // This vertex is referenced by exactly 2 edges in unsorted_edges[]. // The vertex will be in the interior of a chain and the edges will // appear in sorted_edges[] next to each other in the same chain. ON_SubDEdgePtrLink& epl1 = links[i0 + 1]; epl0.m_nbr_index = epl1.m_index; epl1.m_nbr_index = epl0.m_index; continue; } // The vertex referenced by 3 or more edges in unsorted_edges[]. // If the case cannot be resolved by Resolve3OrMoreEdges(), // then this vertex will not be in the interior of a chain. ON_SubDEdgePtrLink::Resolve3OrMoreEdges( unset_nbr1_index, i1 - i0, v, links.Array() + i0 ); } // Sort links[] by m_index valut to restore links[] to its original order. links.QuickSort(ON_SubDEdgePtrLink::CompareIndex); ON_SubDEdgePtrLink* links_array = links.Array(); unsigned chain_count = 0; ON_SimpleArray chain(unsorted_edge_count); for (unsigned int i = 0; i < link_count; ++i) { // epl0 and epl1 are the links for edges[i/2] const ON_SubDEdgePtrLink epl0 = links_array[i]; links_array[i].m_ep = ON_SubDEdgePtr::Null; const ON_SubDEdgePtrLink epl1 = links_array[++i]; links_array[i].m_ep = ON_SubDEdgePtr::Null; if (nullptr == epl0.m_ep.Edge()) continue; // this edge has already been inserted in sorted_edges[]. chain.SetCount(0); // Add edges that come "before" edges[i/2] to chain[] epl = epl1; for (;;) { if (epl.m_nbr_index >= unset_nbr1_index) break; unsigned int j = epl.m_nbr_index; unsigned int j1 = (0 == (j % 2)) ? (j + 1) : (j - 1); // epl = "previous" link epl = links_array[j1]; links_array[j].m_ep = ON_SubDEdgePtr::Null; links_array[j1].m_ep = ON_SubDEdgePtr::Null; if (nullptr == epl.m_ep.Edge()) break; ////ee[0] = epl.m_ep.Edge(); ////vv[0] = epl.m_ep.RelativeVertex(0); ////vv[1] = epl.m_ep.RelativeVertex(1); ////if (vv[0] == vv[1] || nullptr == ee[0]) //// return false; chain.Append(epl.m_ep); } const bool bClosedChain = chain.UnsignedCount() > 0 && (epl.m_index == epl1.m_index); const bool bFirstEdgeIsReversed = !bClosedChain && unset_nbr1_index <= epl.m_nbr_index && (0 == (epl.m_index % 2)); bool bLastEdgeIsReversed{false}; if (false == bClosedChain) { chain.Reverse(); } ////ee[0] = epl1.m_ep.Edge(); ////vv[0] = epl1.m_ep.RelativeVertex(0); ////vv[1] = epl1.m_ep.RelativeVertex(1); ////if (vv[0] == vv[1] || nullptr == ee[0]) //// return false; chain.Append(epl1.m_ep); // matches input edge orientation if (bClosedChain) { // put edges[i/2] at the start of the closed chain. chain.Reverse(); } else { // Add edges that come "after" edges[i/2] to chain[] epl = epl0; for (;;) { if (epl.m_nbr_index >= unset_nbr1_index) break; unsigned int j = epl.m_nbr_index; unsigned int j1 = (0 == (j % 2)) ? (j + 1) : (j - 1); // epl = "next" link epl = links_array[j1]; links_array[j].m_ep = ON_SubDEdgePtr::Null; links_array[j1].m_ep = ON_SubDEdgePtr::Null; if (nullptr == epl.m_ep.Edge()) break; ////ee[0] = epl.m_ep.Edge(); ////vv[0] = epl.m_ep.RelativeVertex(1); ////vv[1] = epl.m_ep.RelativeVertex(0); ////if (vv[0] == vv[1] || nullptr == ee[0]) //// return false; chain.Append(epl.m_ep.Reversed()); } // No need to update bClosedChain here: if it was closed, then the circle // was found with the edges "before" edges[i/2] // First and last edge are reversed: reverse chain direction to keep first // edge direction the same as in unsorted_edges bLastEdgeIsReversed = epl.m_index % 2 == 0; if (bFirstEdgeIsReversed && bLastEdgeIsReversed) { ReverseEdgeChain(chain); } } const unsigned int chain_edge_count = chain.UnsignedCount(); if (chain_edge_count > 0) { for (;;) { if (chain_edge_count < 3) break; const ON_SubDVertex* c0 = chain[0].RelativeVertex(0); if (nullptr == c0) break; if (c0->IsCorner()) break; const ON_SubDVertex* c1 = chain[chain_edge_count-1].RelativeVertex(1); if (c0 != c1) break; const ON_SubDEdge* e0 = chain[0].Edge(); if (nullptr == e0) break; const ON_SubDEdge* e1 = chain[chain_edge_count-1].Edge(); if (nullptr == e1) break; const bool bSmooth = e0->IsSmooth(); if (bSmooth != e1->IsSmooth()) break; if (bSmooth && c0->IsCrease()) break; // Check for an embedded crease vertex. for (unsigned int k = 1; k < chain_edge_count; ++k) { const ON_SubDVertex* v = chain[k].RelativeVertex(0); if (nullptr == v) break; const ON_SubDEdge* e = chain[k].Edge(); if (nullptr == e) break; if ( bSmooth != e->IsSmooth() || (bSmooth && v->IsCreaseOrCorner()) ) { // shift chain[] so it begins at chain[k]; ON_SimpleArray tail; tail.Append(k, chain.Array()); for (unsigned n = k; n < chain_edge_count; ++n) chain[n - k] = chain[n]; chain.SetCount(chain_edge_count - k); chain.Append(tail.Count(), tail.Array()); break; } } break; } #ifdef ON_DEBUG unsigned temp_chain_edge_count{chain_edge_count}; ON_2udex temp_udex{0U, (unsigned)(chain_edge_count - 1)}; if (!Internal_VerifyEdgeChain(chain, temp_udex, &temp_chain_edge_count)) { temp_chain_edge_count = 0; } Internal_UnrollChain(chain); #endif if (chain.UnsignedCount() >= minimum_chain_length) { const ON_SubDVertex* c0{chain[0].RelativeVertex(0)}; const ON_SubDVertex* s1{sorted_edges.Last() == nullptr ? nullptr : sorted_edges.Last()->RelativeVertex(1)}; if (c0 != nullptr && c0 == s1) { // If the first vertex of the chain is a corner, or a vertex with more // than 2 edges referencing it, we might have artificially split the // chain at that vertex and the last vertex of the previous chain is // the same as ours. Try a few tricks to put a gap between these two // vertices. const ON_SubDVertex* c1{ chain[chain_edge_count - 1].RelativeVertex(1)}; const ON_SubDVertex* s0{ sorted_edges.First() == nullptr ? nullptr : sorted_edges.First()->RelativeVertex(0)}; // By order of importance, try to: // - Keep the orientation of the first edge the same as in unsorted_edges // - Not use ReverseEdgeChain() // - Append rather than Prepend to sorted_edges if (c1 != s0) { ++chain_count; sorted_edges.Prepend(chain.Count(), chain.Array()); } else if (c1 != s1 && !bClosedChain && ((bFirstEdgeIsReversed && !bLastEdgeIsReversed) || (!bFirstEdgeIsReversed && bLastEdgeIsReversed))) { ReverseEdgeChain(chain); ++chain_count; sorted_edges.Append(chain.Count(), chain.Array()); } else if (c0 != s0 && !bClosedChain && ((bFirstEdgeIsReversed && !bLastEdgeIsReversed) || (!bFirstEdgeIsReversed && bLastEdgeIsReversed))) { ReverseEdgeChain(chain); ++chain_count; sorted_edges.Prepend(chain.Count(), chain.Array()); } else if (c1 != s1 && !bClosedChain) { ReverseEdgeChain(chain); ++chain_count; sorted_edges.Append(chain.Count(), chain.Array()); } else if (c0 != s0 && !bClosedChain) { ReverseEdgeChain(chain); ++chain_count; sorted_edges.Prepend(chain.Count(), chain.Array()); } else { // We tried, but nothing works easily enough: append the chain as // usual, this means the two chains are merged and chain_count does // not increase. sorted_edges.Append(chain.Count(), chain.Array()); } } else { ++chain_count; sorted_edges.Append(chain.Count(), chain.Array()); } } } if ( link_count == 2*sorted_edges.UnsignedCount() ) break; // we've used all the links - no need to "skip over the rest". } #ifdef ON_DEBUG Internal_UnrollChain(sorted_edges); if (!Internal_CountAndVerifyEdgeChains(sorted_edges, &chain_count)) { return chain_count; } #endif return chain_count; } const ON_SHA1_Hash ON_SubDEdgeChain::Hash() const { ON_SHA1 sha1; const unsigned edge_count = this->EdgeCount(); for (unsigned i = 0; i < edge_count; ++i) { const ON_SubDEdgePtr eptr = EdgePtr(i); sha1.AccumulateInteger32(eptr.EdgeId()); sha1.AccumulateInteger32(eptr.RelativeVertexId(0)); sha1.AccumulateInteger32(eptr.RelativeVertexId(1)); } return sha1.Hash(); } unsigned int ON_SubDEdgeChain::OrientEdgesIntoEdgeChains( const ON_SubD& subd, const ON_SimpleArray< ON_COMPONENT_INDEX >& edges, ON_SimpleArray< ON_SubDEdgePtr >& edge_chain ) { const unsigned count = edges.UnsignedCount(); ON_SimpleArray< const ON_SubDEdge* > a(count); for (unsigned i = 0; i < count; ++i) a.Append(subd.EdgeFromComponentIndex(edges[i])); return ON_SubDEdgeChain::OrientEdgesIntoEdgeChains(a, edge_chain); } unsigned int ON_SubDEdgeChain::OrientEdgesIntoEdgeChains( const ON_SimpleArray< ON_SubDComponentPtr >& edges, ON_SimpleArray< ON_SubDEdgePtr >& edge_chain ) { const unsigned count = edges.UnsignedCount(); ON_SimpleArray< const ON_SubDEdge* > a(count); for (unsigned i = 0; i < count; ++i) a.Append(edges[i].Edge()); return ON_SubDEdgeChain::OrientEdgesIntoEdgeChains(a, edge_chain); } unsigned int ON_SubDEdgeChain::OrientEdgesIntoEdgeChains( const ON_SimpleArray< const ON_SubDEdge* >& edges, ON_SimpleArray< ON_SubDEdgePtr >& edge_chains ) { const unsigned count = edges.UnsignedCount(); edge_chains.SetCount(0); edge_chains.Reserve(count); unsigned int chain_count = 0; unsigned chain_length = 0; ON_SubDEdgePtr* prev_eptr = nullptr; for (unsigned i = 0; i < count; ++i) { const ON_SubDEdge* e = edges[i]; if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] || e->m_vertex[0] == e->m_vertex[1]) continue; ON_SubDEdgePtr& eptr = edge_chains.AppendNew(); eptr = ON_SubDEdgePtr::Create(e); if (nullptr != prev_eptr && prev_eptr->RelativeVertex(1) != eptr.RelativeVertex(0) ) { const ON_SubDVertex* prev_v[2] = { prev_eptr->RelativeVertex(0), prev_eptr->RelativeVertex(1) }; const ON_SubDVertex* v[2] = { eptr.RelativeVertex(0), eptr.RelativeVertex(1) }; if (prev_v[1] == v[1]) eptr = eptr.Reversed(); else if (1 == chain_length) { if (prev_v[0] == v[0]) *prev_eptr = prev_eptr->Reversed(); else if (prev_v[0] == v[1]) { *prev_eptr = prev_eptr->Reversed(); eptr = eptr.Reversed(); } else prev_eptr = nullptr; } else prev_eptr = nullptr; } if (nullptr == prev_eptr) { chain_count = 1; chain_length = 0; } prev_eptr = &eptr; ++chain_length; } return chain_count; } ON_SubDComponentList::ON_SubDComponentList(const ON_SubDComponentList& src) : m_subd_runtime_serial_number(src.m_subd_runtime_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) , m_component_list(src.m_component_list) { m_subd.ShareDimple(src.m_subd); } ON_SubDComponentList& ON_SubDComponentList::operator=(const ON_SubDComponentList& src) { if (this != &src) { m_subd_runtime_serial_number = src.m_subd_runtime_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; m_component_list = src.m_component_list; m_subd.ShareDimple(src.m_subd); } return *this; } ON__UINT64 ON_SubDComponentList::SubDRuntimeSerialNumber() const { return m_subd_runtime_serial_number; } ON__UINT64 ON_SubDComponentList::SubDGeometryContentSerialNumber() const { 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 { return m_component_list.UnsignedCount(); } const ON_SubDComponentPtr ON_SubDComponentList::operator[](int i) const { return i >= 0 && i < m_component_list.Count() ? m_component_list[i] : ON_SubDComponentPtr::Null; } const ON_SubDComponentPtr ON_SubDComponentList::operator[](unsigned int i) const { return i < m_component_list.UnsignedCount() ? m_component_list[i] : ON_SubDComponentPtr::Null; } const ON_SubDComponentPtr ON_SubDComponentList::operator[](ON__INT64 i) const { return i >= 0 && i < ((ON__INT64)m_component_list.Count()) ? m_component_list[i] : ON_SubDComponentPtr::Null; } const ON_SubDComponentPtr ON_SubDComponentList::operator[](ON__UINT64 i) const { return i < ((ON__UINT64)m_component_list.UnsignedCount()) ? m_component_list[i] : ON_SubDComponentPtr::Null; } #if defined(ON_RUNTIME_APPLE) const ON_SubDComponentPtr ON_SubDComponentList::operator[](size_t i) const { return i >= 0 && i < m_component_list.Count() ? m_component_list[i] : ON_SubDComponentPtr::Null; } #endif const ON_SimpleArray< ON_SubDComponentPtr >& ON_SubDComponentList::ComponentList() const { return this->m_component_list; } const ON_SubD& ON_SubDComponentList::SubD() const { return m_subd; } void ON_SubDComponentList::UpdateContentSerialNumbers() { 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) { const unsigned count0 = Count(); if (subd.RuntimeSerialNumber() == m_subd.RuntimeSerialNumber()) return count0; // the components in this list are in subd. // Use the component ids to update the list to reference components in subd. unsigned count1 = 0; for (unsigned i = 0; i < count0; ++i) { ON_SubDComponentPtr cptr0 = m_component_list[i]; const ON_SubDComponentBase* c0 = cptr0.ComponentBase(); if (nullptr == c0) continue; if (false == bUpdateDeletedComponents && false == c0->IsActive()) continue; ON_COMPONENT_INDEX ci = cptr0.ComponentIndex(); if (0 == ci.m_index) continue; ON_SubDComponentPtr cptr1 = subd.ComponentPtrFromComponentIndex(ci); if (cptr1.IsNull()) continue; if (0 != cptr0.ComponentDirection()) cptr1.SetComponentDirection(); m_component_list[count1++] = cptr1; } m_component_list.SetCount(count1); m_subd.ShareDimple(subd); m_subd_runtime_serial_number = m_subd.RuntimeSerialNumber(); m_subd_geometry_content_serial_number = m_subd.GeometryContentSerialNumber(); m_subd_render_content_serial_number = m_subd.RenderContentSerialNumber(); return Count(); } unsigned ON_SubDComponentList::CreateFromComponentList(const ON_SubD& subd, const ON_SimpleArray& component_list) { ON_SubDComponentMarksClearAndRestore saved_marks(subd); const unsigned count = component_list.UnsignedCount(); unsigned marked_count = 0; for (unsigned i = 0; i < count; ++i) { const ON_COMPONENT_INDEX ci = component_list[i]; if (ON_COMPONENT_INDEX::TYPE::subd_vertex != ci.m_type) continue; const unsigned vertex_id = (unsigned)ci.m_index; const ON_SubDVertex* v = subd.VertexFromId(vertex_id); if (nullptr == v) continue; if (v->m_status.RuntimeMark()) continue; v->m_status.SetRuntimeMark(); ++marked_count; } return Internal_Create(subd, true, true, true, true, marked_count); } unsigned ON_SubDComponentList::CreateFromComponentList(const ON_SubD& subd, const ON_SimpleArray& component_list) { ON_SubDComponentMarksClearAndRestore saved_marks(subd); const unsigned count = component_list.UnsignedCount(); unsigned marked_count = 0; for (unsigned i = 0; i < count; ++i) { const ON_SubDComponentBase* c = component_list[i].ComponentBase(); if (nullptr == c) continue; if (c->m_status.RuntimeMark()) continue; c->m_status.SetRuntimeMark(); ++marked_count; } return Internal_Create(subd, true, true, true, true, marked_count); } unsigned ON_SubDComponentList::CreateFromVertexIdList(const ON_SubD& subd, const ON_SimpleArray& vertex_id_list) { ON_SubDComponentMarksClearAndRestore saved_marks(subd); const unsigned count = vertex_id_list.UnsignedCount(); unsigned marked_count = 0; for (unsigned i = 0; i < count; ++i) { const unsigned vertex_id = vertex_id_list[i]; if (vertex_id <= 0 || vertex_id >= ON_UNSET_UINT_INDEX) continue; const ON_SubDVertex* v = subd.VertexFromId(vertex_id); if (nullptr == v) continue; if (v->m_status.RuntimeMark()) continue; v->m_status.SetRuntimeMark(); ++marked_count; } return Internal_Create(subd, true, false, false, true, marked_count); } unsigned ON_SubDComponentList::CreateFromVertexList(const ON_SubD& subd, const ON_SimpleArray& vertex_list) { ON_SubDComponentMarksClearAndRestore saved_marks(subd); const unsigned count = vertex_list.UnsignedCount(); unsigned marked_count = 0; for (unsigned i = 0; i < count; ++i) { const ON_SubDVertex* v = vertex_list[i].Vertex(); if (nullptr == v) continue; if (v->m_status.RuntimeMark()) continue; v->m_status.SetRuntimeMark(); ++marked_count; } return Internal_Create(subd, true, false, false, true, marked_count); } unsigned ON_SubDComponentList::CreateFromVertexList(const ON_SubD& subd, const ON_SimpleArray& vertex_list) { ON_SubDComponentMarksClearAndRestore saved_marks(subd); const unsigned count = vertex_list.UnsignedCount(); unsigned marked_count = 0; for (unsigned i = 0; i < count; ++i) { const ON_SubDVertex* v = vertex_list[i]; if (nullptr == v) continue; if (v->m_status.RuntimeMark()) continue; v->m_status.SetRuntimeMark(); ++marked_count; } return Internal_Create(subd, true, false, false, true, marked_count); } unsigned ON_SubDComponentList::CreateFromMarkedComponents(const ON_SubD& subd, bool bComponentInListMark) { unsigned marked_count = 0; ON_SubDComponentIterator cit(subd); if (bComponentInListMark) bComponentInListMark = true; // avoid other byte values. for (ON_SubDComponentPtr c = cit.FirstComponent(); c.IsNotNull(); c = cit.NextComponent()) { if (bComponentInListMark != c.Mark()) continue; ++marked_count; } return Internal_Create(subd, true, true, true, bComponentInListMark, marked_count); } unsigned ON_SubDComponentList::CreateFromMarkedVertices(const ON_SubD& subd, bool bVertexInListMark) { unsigned marked_count = 0; ON_SubDVertexIterator vit(subd); if (bVertexInListMark) bVertexInListMark = true; // avoid other byte values. for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { if (bVertexInListMark != v->m_status.RuntimeMark()) continue; ++marked_count; } return Internal_Create(subd, true, false, false, bVertexInListMark, marked_count); } unsigned ON_SubDComponentList::CreateFromMarkedEdges(const ON_SubD& subd, bool bEdgeInListMark) { unsigned marked_count = 0; ON_SubDEdgeIterator eit(subd); if (bEdgeInListMark) bEdgeInListMark = true; // avoid other byte values. for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { if (bEdgeInListMark != e->m_status.RuntimeMark()) continue; ++marked_count; } return Internal_Create(subd, false, true, false, bEdgeInListMark, marked_count); } unsigned ON_SubDComponentList::CreateFromMarkedFaces(const ON_SubD& subd, bool bFaceInListMark) { unsigned marked_count = 0; ON_SubDFaceIterator fit(subd); if (bFaceInListMark) bFaceInListMark = true; // avoid other byte values. for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { if (bFaceInListMark != f->m_status.RuntimeMark()) continue; ++marked_count; } return Internal_Create(subd, false, false, true, bFaceInListMark, marked_count); } unsigned ON_SubDComponentList::Internal_Create(const ON_SubD & subd, bool bAddVertices, bool bAddEdges, bool bAddFaces, bool bComponentInListMark, unsigned marked_component_count) { Destroy(); if (0 == marked_component_count) return 0; const unsigned face_count = bAddFaces ? subd.FaceCount() : 0U; const unsigned edge_count = bAddEdges ? subd.EdgeCount() : 0U; const unsigned vertex_count = bAddVertices ? subd.VertexCount() : 0U; if (0 == vertex_count && 0 == edge_count && 0 == face_count) return 0; if (marked_component_count > vertex_count + edge_count + face_count) return 0; bComponentInListMark = bComponentInListMark ? true : false; m_component_list.Reserve(marked_component_count); m_component_list.SetCount(0); if (vertex_count > 0) { ON_SubDVertexIterator vit(subd); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) { if (bComponentInListMark != v->m_status.RuntimeMark()) continue; m_component_list.Append(v->ComponentPtr()); } } if (edge_count > 0) { ON_SubDEdgeIterator eit(subd); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { if (bComponentInListMark != e->m_status.RuntimeMark()) continue; m_component_list.Append(e->ComponentPtr()); } } if (face_count > 0) { ON_SubDFaceIterator fit(subd); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { if (bComponentInListMark != f->m_status.RuntimeMark()) continue; m_component_list.Append(f->ComponentPtr()); } } if (m_component_list.UnsignedCount() > 0) { m_subd.ShareDimple(subd); m_subd_runtime_serial_number = subd.RuntimeSerialNumber(); m_subd_geometry_content_serial_number = subd.GeometryContentSerialNumber(); m_subd_render_content_serial_number = subd.RenderContentSerialNumber(); } return m_component_list.UnsignedCount(); } unsigned int ON_SubDComponentList::RemoveAllComponents() { const unsigned count0 = Count(); m_component_list.SetCount(0); return count0; } unsigned int ON_SubDComponentList::RemoveAllVertices() { return Internal_RemoveComponents(true, false, false); } unsigned int ON_SubDComponentList::RemoveAllEdges() { return Internal_RemoveComponents(false, true, false); } unsigned int ON_SubDComponentList::RemoveAllFaces() { return Internal_RemoveComponents(false, false, true); } unsigned ON_SubDComponentList::Internal_RemoveComponents( bool bRemoveVertices, bool bRemoveEdges, bool bRemoveFaces ) { unsigned int count0 = Count(); if (bRemoveVertices || bRemoveEdges || bRemoveFaces) { unsigned count1 = 0; for (unsigned i = 0; i < count0; ++i) { const ON_SubDComponentPtr cptr = m_component_list[i]; bool bRemove = false; switch (cptr.ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: bRemove = bRemoveVertices; break; case ON_SubDComponentPtr::Type::Edge: bRemove = bRemoveEdges; break; case ON_SubDComponentPtr::Type::Face: bRemove = bRemoveFaces; break; default: bRemove = true; } if (bRemove) continue; m_component_list[count1++] = cptr; } m_component_list.SetCount(count1); } return count0 - Count(); } void ON_SubDComponentList::Destroy() { m_subd_runtime_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); } const ON_SubDComponentFilter ON_SubDComponentFilter::Create( bool bAcceptVertices, bool bAcceptEdges, bool bAcceptFaces ) { ON_SubDComponentFilter f; if (false == bAcceptVertices) f.m_bRejectVertices = true; if (false == bAcceptEdges) f.m_bRejectEdges = true; if (false == bAcceptFaces) f.m_bRejectFaces = true; return f; } bool ON_SubDComponentFilter::AcceptComponent(ON_COMPONENT_INDEX component_index, const class ON_Geometry* geometry) const { if (false == component_index.IsSubDComponentIndex()) return false; const ON_SubDComponentRef* cref = ON_SubDComponentRef::Cast(geometry); if (nullptr == cref) return false; const ON_SubDComponentPtr cptr = cref->ComponentPtr(); if (component_index.m_index != (int)cptr.ComponentId()) return false; switch (component_index.m_type) { case ON_COMPONENT_INDEX::TYPE::subd_vertex: if (ON_SubDComponentPtr::Type::Vertex != cptr.ComponentType()) return false; break; case ON_COMPONENT_INDEX::TYPE::subd_edge: if (ON_SubDComponentPtr::Type::Edge != cptr.ComponentType()) return false; break; case ON_COMPONENT_INDEX::TYPE::subd_face: if (ON_SubDComponentPtr::Type::Face != cptr.ComponentType()) return false; break; } return AcceptComponent(cptr); } bool ON_SubDComponentFilter::AcceptComponent(const class ON_Geometry* geometry) const { return AcceptComponent(ON_SubDComponentRef::Cast(geometry)); } bool ON_SubDComponentFilter::AcceptComponent(const class ON_SubDComponentRef* cref) const { return (nullptr != cref) ? AcceptComponent(cref->ComponentPtr()) : false; } bool ON_SubDComponentFilter::AcceptComponent(ON_SubDComponentPtr cptr) const { switch (cptr.ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: return AcceptVertex(cptr.Vertex()); break; case ON_SubDComponentPtr::Type::Edge: return AcceptEdge(cptr.Edge()); break; case ON_SubDComponentPtr::Type::Face: return AcceptFace(cptr.Face()); break; } return false; } bool ON_SubDComponentFilter::AcceptVertex(ON_SubDVertexPtr vptr) const { return AcceptVertex(vptr.Vertex()); } bool ON_SubDComponentFilter::AcceptEdge(ON_SubDEdgePtr eptr) const { return AcceptEdge(eptr.Edge()); } bool ON_SubDComponentFilter::AcceptFace(ON_SubDFacePtr fptr) const { return AcceptFace(fptr.Face()); } bool ON_SubDComponentFilter::AcceptVertex(const ON_SubDVertex * v) const { if (m_bRejectVertices) return false; if (nullptr == v) return false; if (false == AcceptVertexTag(v->m_vertex_tag)) return false; if (ON_SubDComponentFilter::Topology::Unset != m_vertex_topology_filter) { // check boundary/interior/nonmanifold if (v->HasBoundaryVertexTopology()) { if (0 == (static_cast(ON_SubDComponentFilter::Topology::Boundary)& static_cast(m_vertex_topology_filter))) return false; } else if (v->HasInteriorVertexTopology()) { if (0 == (static_cast(ON_SubDComponentFilter::Topology::Interior)& static_cast(m_vertex_topology_filter))) return false; } else { if (0 == (static_cast(ON_SubDComponentFilter::Topology::Nonmanifold)& static_cast(m_vertex_topology_filter))) return false; } } return true; } bool ON_SubDComponentFilter::AcceptEdge(const ON_SubDEdge * e) const { if (m_bRejectEdges) return false; if (nullptr == e) return false; if (false == AcceptEdgeTag(e->m_edge_tag)) return false; if (ON_SubDComponentFilter::Topology::Unset != m_edge_topology_filter) { // check boundary/interior/nonmanifold if (1 == e->m_face_count) { if (0 == (static_cast(ON_SubDComponentFilter::Topology::Boundary)& static_cast(m_edge_topology_filter))) return false; } else if (2 == e->m_face_count) { if (0 == (static_cast(ON_SubDComponentFilter::Topology::Interior)& static_cast(m_edge_topology_filter))) return false; } else { if (0 == (static_cast(ON_SubDComponentFilter::Topology::Nonmanifold)& static_cast(m_edge_topology_filter))) return false; } } return true; } bool ON_SubDComponentFilter::AcceptFace(const ON_SubDFace * f) const { if (m_bRejectFaces) return false; if (nullptr == f) return false; if (m_maximum_face_edge_count > 0U) { const unsigned face_edge_count = f->m_edge_count; if (face_edge_count < m_minimum_face_edge_count || face_edge_count > m_maximum_face_edge_count) return false; } if (ON_SubDComponentFilter::Topology::Unset != m_edge_topology_filter) { 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 = eptr->Edge(); if (nullptr == e) continue; if (1 == e->m_face_count) { if (0 == (static_cast(ON_SubDComponentFilter::Topology::Boundary)& static_cast(m_edge_topology_filter))) return false; } else if (2 == e->m_face_count) { if (0 == (static_cast(ON_SubDComponentFilter::Topology::Interior)& static_cast(m_edge_topology_filter))) return false; } else { if (0 == (static_cast(ON_SubDComponentFilter::Topology::Nonmanifold)& static_cast(m_edge_topology_filter))) return false; } } } return true; } void ON_SubDComponentFilter::SetAcceptVertices(bool bAcceptVertices) { m_bRejectVertices = bAcceptVertices ? false : true; } bool ON_SubDComponentFilter::AcceptVertices() const { return false == m_bRejectVertices; } void ON_SubDComponentFilter::SetAcceptEdges(bool bAcceptEdges) { m_bRejectEdges = bAcceptEdges ? false : true; } bool ON_SubDComponentFilter::AcceptEdges() const { return false == m_bRejectEdges; } void ON_SubDComponentFilter::SetAcceptFaces(bool bAcceptFaces) { m_bRejectFaces = bAcceptFaces ? false : true; } bool ON_SubDComponentFilter::AcceptFaces() const { return false == m_bRejectFaces; } void ON_SubDComponentFilter::SetVertexTopologyFilter(ON_SubDComponentFilter::Topology vertex_topology_filter) { m_vertex_topology_filter = vertex_topology_filter; } void ON_SubDComponentFilter::ClearVertexTopologyFilter() { m_vertex_topology_filter = ON_SubDComponentFilter::Topology::Unset; } ON_SubDComponentFilter::Topology ON_SubDComponentFilter::VertexTopologyFilter() const { return m_vertex_topology_filter; } void ON_SubDComponentFilter::SetEdgeTopologyFilter(ON_SubDComponentFilter::Topology edge_topology_filter) { m_edge_topology_filter = edge_topology_filter; } ON_SubDComponentFilter::Topology ON_SubDComponentFilter::EdgeTopologyFilter() const { return m_edge_topology_filter; } void ON_SubDComponentFilter::ClearEdgeTopologyFilter() { m_edge_topology_filter = ON_SubDComponentFilter::Topology::Unset; } void ON_SubDComponentFilter::SetFaceTopologyFilter(ON_SubDComponentFilter::Topology face_topology_filter) { m_face_topology_filter = face_topology_filter; } ON_SubDComponentFilter::Topology ON_SubDComponentFilter::FaceTopologyFilter() const { return m_face_topology_filter; } void ON_SubDComponentFilter::ClearFaceTopologyFilter() { m_face_topology_filter = ON_SubDComponentFilter::Topology::Unset; } bool ON_SubDComponentFilter::AcceptVertexTag(ON_SubDVertexTag vertex_tag) const { 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_SubDVertexTag::Unset == m_vertex_tag_filter[i]) break; if (m_vertex_tag_filter[i] != vertex_tag) continue; return true; } return false; } 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_SubDVertexTag::Unset == m_vertex_tag_filter[i]) { m_vertex_tag_filter[i] = vertex_tag; break; } } } 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_SubDVertexTag::Unset; } bool ON_SubDComponentFilter::AcceptEdgeTag(ON_SubDEdgeTag edge_tag) const { 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_SubDEdgeTag::Unset == m_edge_tag_filter[i]) break; if (m_edge_tag_filter[i] != edge_tag) continue; return true; } return false; } 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_SubDEdgeTag::Unset == m_edge_tag_filter[i]) { m_edge_tag_filter[i] = edge_tag; break; } } } 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_SubDEdgeTag::Unset; } bool ON_SubDComponentFilter::AcceptFaceEdgeCount( unsigned face_edge_count ) const { return (m_maximum_face_edge_count >= 3U) ? (face_edge_count >= m_minimum_face_edge_count && face_edge_count <= m_maximum_face_edge_count) : false; } void ON_SubDComponentFilter::SetFaceEdgeCountFilter( unsigned minimum_face_edge_count, unsigned maximum_face_edge_count ) { if (minimum_face_edge_count <= maximum_face_edge_count && maximum_face_edge_count >= 3U) { m_minimum_face_edge_count = minimum_face_edge_count; m_maximum_face_edge_count = maximum_face_edge_count; } } void ON_SubDComponentFilter::ClearFaceEdgeCountFilter() { m_minimum_face_edge_count = 0U; m_maximum_face_edge_count = 0U; } double ON_SubD::SurfacePointRadiusFromControlPointRadius(unsigned int polygon_count, double polgon_radius) { for (;;) { if (polygon_count < 3) break; if (false == ON_IsValid(polgon_radius)) break; const double a = ON_2PI / ((double)polygon_count); ON_2dPoint cv[4] = { ON_2dPoint(1,0), ON_2dPoint(cos(a),sin(a)), ON_2dPoint(cos(2 * a),sin(2 * a)), ON_2dPoint(cos(3 * a),sin(3 * a)) }; double k[6] = { -2,-1,0,1,2,3 }; ON_NurbsCurve c; c.m_dim = 2; c.m_order = 4; c.m_cv_count = 4; c.m_cv = &cv[0].x; c.m_cv_stride = (int)(&cv[1].x - &cv[0].x); c.m_knot = k; const ON_3dPoint p = c.PointAt(0.0); const double r = ON_2dPoint(p.x, p.y).DistanceTo(ON_2dPoint::Origin); if (r > 0.0) return polgon_radius * r; break; } return ON_DBL_QNAN; } double ON_SubD::ControlPointRadiusFromSurfacePointRadius(unsigned int polygon_count, double surface_radius) { for (;;) { if (false == ON_IsValid(surface_radius)) break; const double r = ON_SubD::SurfacePointRadiusFromControlPointRadius(polygon_count, 1.0); if (r > 0.0) return surface_radius / r; break; } return ON_DBL_QNAN; } ON_SubDFace* ON_SubD::FindOrAddFace( ON_SubDEdgeTag new_edge_tag, const ON_SubDVertex* face_vertices[], size_t vertex_count ) { if (nullptr == face_vertices) return ON_SUBD_RETURN_ERROR(nullptr); if (vertex_count < 3) return ON_SUBD_RETURN_ERROR(nullptr); if (vertex_count > (size_t)ON_SubDFace::MaximumEdgeCount) return ON_SUBD_RETURN_ERROR(nullptr); // Mkae sure v[] has vertex_count unique non-null vertices. for (unsigned i = 0; i < vertex_count; ++i) { if (nullptr == face_vertices[i]) return ON_SUBD_RETURN_ERROR(nullptr); for (unsigned j = i + 1; j < vertex_count; ++j) { if (face_vertices[i] == face_vertices[j]) return ON_SUBD_RETURN_ERROR(nullptr); } } ON_SimpleArray eptrs(vertex_count); ON_SimpleArray faces(4); ON_SimpleArray faces_to_keep(4); const ON_SubDVertex* ev[2] = { nullptr, face_vertices[0] }; for (unsigned i = 0; i < vertex_count; ++i) { ev[0] = ev[1]; ev[1] = face_vertices[(i + 1) % vertex_count]; ON_SubDEdgePtr eptr = ON_SubDEdge::FromVertices(ev[0], ev[1]); if (eptr.IsNull()) { // need to create this edge ON_SubDEdge* e = this->AddEdge(new_edge_tag, const_cast(ev[0]), const_cast(ev[1])); if (nullptr == e) return ON_SUBD_RETURN_ERROR(nullptr); eptr = ON_SubDEdgePtr::Create(e, 0); faces.SetCount(0); } else { const ON_SubDEdge* e = eptr.Edge(); if (nullptr == e) return ON_SUBD_RETURN_ERROR(nullptr); if (0 == e->m_face_count) faces.SetCount(0); else if (0 == i || faces.Count() > 0) { faces_to_keep.SetCount(0); const ON_SubDFacePtr* fptr = e->m_face2; for (unsigned short efi = 0; efi < e->m_face_count; ++efi, ++fptr) { if (2 == efi) { fptr = e->m_facex; if (nullptr == fptr) break; } const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr->m_ptr); if (nullptr == f) continue; if (0 == i) faces_to_keep.Append(f); else { for (unsigned j = 0; j < faces.UnsignedCount(); ++j) { if (f != faces[j]) continue; // every edge so far is attached to f faces_to_keep.Append(f); break; } } } faces = faces_to_keep; } } eptrs.Append(eptr); } if (eptrs.UnsignedCount() != vertex_count) return ON_SUBD_RETURN_ERROR(nullptr); ON_SubDFace* new_face = this->AddFace(eptrs); return new_face; } ON_SubD* ON_SubD::CreateCylinder( const ON_Cylinder& cylinder, unsigned circumference_face_count, unsigned height_face_count, ON_SubDEndCapStyle end_cap_style, ON_SubDEdgeTag end_cap_edge_tag, ON_SubDComponentLocation radius_location, ON_SubD* destination_subd ) { if (nullptr != destination_subd) *destination_subd = ON_SubD::Empty; if (false == cylinder.IsValid()) { ON_SUBD_ERROR("Invalid cylinder parameter."); return nullptr; } if (circumference_face_count < 3) { ON_SUBD_ERROR("Invalid circumference_face_count parameter."); return nullptr; } if (height_face_count < 1) { ON_SUBD_ERROR("Invalid height_face_count parameter."); return nullptr; } const double r = cylinder.circle.Radius(); if (false == (r > 0.0 && r < ON_UNSET_POSITIVE_VALUE)) { ON_SUBD_ERROR("Invalid cylinder radius parameter."); return nullptr; } //////////////////////////////////////////// // Validate and sanitize end_cap_style parameter // switch (end_cap_style) { case ON_SubDEndCapStyle::Unset: end_cap_style = ON_SubDEndCapStyle::None; break; case ON_SubDEndCapStyle::None: break; case ON_SubDEndCapStyle::Triangles: if (circumference_face_count < 2) end_cap_style = ON_SubDEndCapStyle::None; else if (circumference_face_count <= 3) end_cap_style = ON_SubDEndCapStyle::Ngon; // single triangle break; case ON_SubDEndCapStyle::Quads: if (circumference_face_count < 2) end_cap_style = ON_SubDEndCapStyle::None; else if (circumference_face_count <= 4) end_cap_style = ON_SubDEndCapStyle::Ngon; // single quad or single triangle else if (0 != (circumference_face_count%2)) end_cap_style = ON_SubDEndCapStyle::Triangles; // must have even number of sized for a multi-quad cap. break; case ON_SubDEndCapStyle::Ngon: if (circumference_face_count < 2) end_cap_style = ON_SubDEndCapStyle::None; break; default: end_cap_style = ON_SubDEndCapStyle::None; break; } const bool bCapEnds = ON_SubDEndCapStyle::None != end_cap_style; /////////////////////////////////////////////// // If cylinder in infinite, choose a height that makes the faces squarish. // const double height = cylinder.IsFinite() ? cylinder.Height() : (ON_2PI*r/((double)circumference_face_count))*((double)height_face_count); if ( false == (ON_IsValid(height) && 0.0 != height) ) { ON_SUBD_ERROR("Invalid cylinder or count parameters."); return nullptr; } ///////////////////////////////////////////// // H = vector that translates a ring of vertices / edges from one circumference to the next. const ON_3dVector H = (height / ((double)height_face_count)) * cylinder.Axis().UnitVector(); ///////////////////////////////////////////// // Adjust radius so result has surface and control net in the correct location. // ON_Circle point_generator(cylinder.IsFinite() ? cylinder.CircleAt(cylinder.height[0]) : cylinder.circle); point_generator.radius = (ON_SubDComponentLocation::Surface == radius_location) ? ON_SubD::ControlPointRadiusFromSurfacePointRadius(circumference_face_count, r) : r; ////////////////////////////////////////////// // circumference_points[] = ring of control point locations around the cylinder's base. // ON_SimpleArray circumference_points(circumference_face_count); for (unsigned i = 0; i < circumference_face_count; ++i) { const double a = ON_Interval::ZeroToTwoPi.ParameterAt(((double)i) / ((double)circumference_face_count)); const ON_3dPoint P = point_generator.PointAt(a); circumference_points.Append(P); } ///////////////////////////////////////////// // center[2] = cap centers (if needed). const ON_3dPoint center[2] = { point_generator.Center(), point_generator.Center() + (((double)height_face_count) * H) }; ON_SubD* subd = (nullptr != destination_subd) ? destination_subd : new ON_SubD(); // v00 = 1st vertex in the previous ring of vertices / edges ON_SubDVertex* v00 = nullptr; for (unsigned j = 0; j <= height_face_count; ++j) { // add a new ring of vertices / edges // v0 = 1st vertex in this ring of vertices / edges ON_SubDVertex* v0 = nullptr; ON_SubDVertex* ev[2] = {}; const ON_SubDVertexTag vtag = (false == bCapEnds || ON_SubDEdgeTag::Crease == end_cap_edge_tag) && (0 == j || j == height_face_count) ? ON_SubDVertexTag::Crease : ON_SubDVertexTag::Smooth; const ON_SubDEdgeTag etag = (ON_SubDVertexTag::Crease == vtag) ? ON_SubDEdgeTag::Crease : ON_SubDEdgeTag::Smooth; for (unsigned i = 0; i < circumference_face_count; ++i) { const ON_3dPoint P = circumference_points[i]; circumference_points[i] = P + H; // move circumference_points[i] up to next ring ev[0] = ev[1]; ev[1] = subd->AddVertex(vtag, P); if (0 == i) v0 = ev[1]; else subd->AddEdge(etag, ev[0], ev[1]); } subd->AddEdge(etag, ev[1], v0); if (j > 0 && nullptr != v00 && nullptr != v0) { // add a new ring of faces const ON_SubDVertex* fv[4] = { nullptr, v00, // 1st vertex in bottom face edge ring v0, // 1st vertex in top face edge ring nullptr }; for (unsigned i = 0; i < circumference_face_count; ++i) { const bool bLastFaceInThisRing = (i+1 == circumference_face_count); // shift to next face's corners fv[0] = fv[1]; fv[3] = fv[2]; fv[1] = bLastFaceInThisRing ? v00 : fv[0]->m_next_vertex; fv[2] = bLastFaceInThisRing ? v0 : fv[3]->m_next_vertex; // make a new face subd->FindOrAddFace(ON_SubDEdgeTag::Smooth, fv, 4); } } v00 = v0; } if (bCapEnds) { ON_SimpleArray bdry(circumference_face_count); const ON_SubDVertex* last_wall_vertex = subd->LastVertex(); const ON_SubDEdge* last_wall_edge = subd->LastEdge(); const bool bCapHasCenterVertex = ON_SubDEndCapStyle::Triangles == end_cap_style || ON_SubDEndCapStyle::Quads == end_cap_style; for (unsigned enddex = 0; enddex < 2; ++enddex) { bdry.SetCount(0); const ON_SubDVertex* v0 = (0 == enddex) ? const_cast(subd->FirstVertex()) : v00; const ON_SubDVertex* ev[2] = { nullptr, v0 }; for (unsigned i = 0; i < circumference_face_count; ++i) { ev[0] = ev[1]; ev[1] = (i + 1 < circumference_face_count) ? const_cast(ev[1]->m_next_vertex) : v0; const ON_SubDEdgePtr eptr = ON_SubDEdge::FromVertices(ev[0], ev[1]); if (eptr.IsNull()) break; bdry.Append(eptr); } if (circumference_face_count != bdry.UnsignedCount()) break; if (0 == enddex) ON_SubDEdgeChain::ReverseEdgeChain(bdry); const ON_SubDVertex* fv[4] = {}; if (bCapHasCenterVertex) { // fv[0] = vertex at the center of the cap fv[0] = subd->AddVertex(ON_SubDVertexTag::Smooth, center[enddex]); if (nullptr == fv[0]) break; } switch (end_cap_style) { case ON_SubDEndCapStyle::Unset: break; case ON_SubDEndCapStyle::None: break; case ON_SubDEndCapStyle::Triangles: if (nullptr != fv[0]) { // radial triangles around the center vertex fv[2] = bdry[0].RelativeVertex(0); for (unsigned i = 0; i < circumference_face_count; ++i) { fv[1] = fv[2]; fv[2] = bdry[i].RelativeVertex(1); subd->FindOrAddFace(ON_SubDEdgeTag::Smooth, fv, 3); } } break; case ON_SubDEndCapStyle::Quads: if (nullptr != fv[0]) { // radial quads around the center vertex fv[3] = bdry[0].RelativeVertex(0); for (unsigned i = 0; i < circumference_face_count; i += 2) { fv[1] = fv[3]; fv[2] = bdry[i].RelativeVertex(1); fv[3] = bdry[(i+1)% circumference_face_count].RelativeVertex(1); subd->FindOrAddFace(ON_SubDEdgeTag::Smooth, fv, 4); } } break; case ON_SubDEndCapStyle::Ngon: // cap = single n-gon subd->AddFace(bdry); break; default: break; } } if (bCapHasCenterVertex) { // vertices and edges added inside the caps are smooth. for (const ON_SubDVertex* v = (nullptr != last_wall_vertex) ? last_wall_vertex->m_next_vertex : nullptr; nullptr != v; v = v->m_next_vertex) const_cast(v)->m_vertex_tag = ON_SubDVertexTag::Smooth; for (const ON_SubDEdge* e = (nullptr != last_wall_edge) ? last_wall_edge->m_next_edge : nullptr; nullptr != e; e = e->m_next_edge) const_cast(e)->m_edge_tag = ON_SubDEdgeTag::Smooth; } } if (nullptr != subd) subd->UpdateAllTagsAndSectorCoefficients(true); return subd; } bool ON_Symmetry::SetSymmetricObject(const ON_SubD* subd) const { const ON_SubDimple* subdimple = (nullptr != subd) ? subd->SubDimple() : nullptr; return SetSymmetricObject(subdimple); } bool ON_Symmetry::SetSymmetricObject(const ON_SubDimple* subdimple) const { bool rc; if (nullptr != subdimple && this->IsSet()) { m_symmetric_object_content_serial_number = subdimple->GeometryContentSerialNumber(); m_symmetric_object_topology_hash = subdimple->SubDHash(ON_SubDHashType::Topology, false).SubDHash(); m_symmetric_object_geometry_hash = subdimple->SubDHash(ON_SubDHashType::Geometry, false).SubDHash(); rc = true; } else { ClearSymmetricObject(); rc = false; } return rc; } double ON_SubDExpandEdgesParameters::ConstantOffset() const { return this->m_constant_offset; } void ON_SubDExpandEdgesParameters::SetConstantOffset(double offset) { offset = ON_SubDExpandEdgesParameters::CleanupOffset(offset); if (ON_SubDExpandEdgesParameters::IsValidConstantOffset(offset)) this->m_constant_offset = offset; } bool ON_SubDExpandEdgesParameters::IsValidForHalfOffset( const ON_SimpleArray& edge_chain ) { const unsigned count = edge_chain.UnsignedCount(); for (unsigned i = 0; i < count; ++i) { if (false == edge_chain[i].HasInteriorEdgeTopology(true)) return false; } return ON_SubDEdgeChain::IsSingleEdgeChain(edge_chain); } bool ON_SubDExpandEdgesParameters::IsValidForVariableOffset( const ON_SimpleArray& edge_chain ) { bool bIsClosed = false; bool bIsSorted = false; const bool IsSingleEdgeChain = ON_SubDEdgeChain::IsSingleEdgeChain(edge_chain, bIsClosed, bIsSorted); return IsSingleEdgeChain && false == bIsClosed; } const ON_Interval ON_SubDExpandEdgesParameters::VariableOffset() const { return this->m_variable_offsets; } void ON_SubDExpandEdgesParameters::SetVariableOffset(ON_Interval variable_offsets) { variable_offsets[0] = ON_SubDExpandEdgesParameters::CleanupOffset(variable_offsets[0]); variable_offsets[1] = ON_SubDExpandEdgesParameters::CleanupOffset(variable_offsets[1]); if (ON_SubDExpandEdgesParameters::IsValidVariableOffset(variable_offsets)) { this->m_variable_offsets = variable_offsets; } else { // invalid input ClearVariableOffset(); if (ON_SubDExpandEdgesParameters::IsValidConstantOffset(variable_offsets[0]) && fabs(variable_offsets[0] - variable_offsets[1]) <= ON_SubDExpandEdgesParameters::OffsetTolerance ) { SetConstantOffset(variable_offsets[0]); } } } void ON_SubDExpandEdgesParameters::ClearVariableOffset() { this->m_variable_offsets = ON_Interval::Nan; } bool ON_SubDExpandEdgesParameters::IsVariableOffset() const { return ON_SubDExpandEdgesParameters::IsValidVariableOffset(this->m_variable_offsets); } bool ON_SubDExpandEdgesParameters::IsValidConstantOffset( double constant_offset_candidate ) { return constant_offset_candidate >= ON_SubDExpandEdgesParameters::MinimumOffset && constant_offset_candidate <= ON_SubDExpandEdgesParameters::MaximumOffset; } bool ON_SubDExpandEdgesParameters::IsValidVariableOffset( ON_Interval variable_offset_candidate ) { for (int i = 0; i < 2; i++) { const double x[2] = { variable_offset_candidate[i], variable_offset_candidate[1 - i] }; if (false == ON_SubDExpandEdgesParameters::IsValidConstantOffset(x[0])) { if (0.0 == x[0]) return (x[1] >= ON_SubDExpandEdgesParameters::MinimumOffset && x[1] <= 1.0); if (1.0 == x[0]) return (x[1] >= 0.0 && x[1] <= ON_SubDExpandEdgesParameters::MaximumOffset); return false; } } return fabs(variable_offset_candidate[0] - variable_offset_candidate[1]) > ON_SubDExpandEdgesParameters::OffsetTolerance; } ON_SubDExpandEdgesParameters::Style ON_SubDExpandEdgesParameters::FaceStyle() const { return this->m_face_style; } void ON_SubDExpandEdgesParameters::SetFaceStyle(ON_SubDExpandEdgesParameters::Style face_style) { this->m_face_style = face_style; } bool ON_SubDExpandEdgesParameters::IsHalfFaceStyle() const { return ON_SubDExpandEdgesParameters::Style::HalfLeft == this->m_face_style || ON_SubDExpandEdgesParameters::Style::HalfRight == this->m_face_style ; } const ON_Color ON_SubDExpandEdgesParameters::FaceColor() const { return this->m_face_color; } void ON_SubDExpandEdgesParameters::SetFaceColor(ON_Color face_color) { this->m_face_color = face_color; } const ON_ComponentStatus ON_SubDExpandEdgesParameters::FaceStatus() const { return this->m_face_status; } void ON_SubDExpandEdgesParameters::SetFaceStatus(ON_ComponentStatus face_status) { this->m_face_status = ON_ComponentStatus::NoneSet; this->m_face_status.SetRuntimeMark(face_status.SetRuntimeMark()); this->m_face_status.SetMarkBits(face_status.MarkBits()); if (face_status.IsSelectedPersistent()) this->m_face_status.SetSelectedState(ON_ComponentState::SelectedPersistent, face_status.IsHighlighted()); else if (face_status.IsSelected()) this->m_face_status.SetSelectedState(ON_ComponentState::Selected, face_status.IsHighlighted()); else if (face_status.IsHighlighted()) this->m_face_status.SetHighlightedState(true); } const ON_SHA1_Hash ON_SubDExpandEdgesParameters::Hash() const { ON_SHA1 sha1; sha1.AccumulateDouble(ConstantOffset()); sha1.AccumulateDouble(VariableOffset().m_t[0]); sha1.AccumulateDouble(VariableOffset().m_t[1]); sha1.AccumulateInteger32((unsigned int)FaceColor()); sha1.AccumulateBytes(&m_face_status, sizeof(m_face_status)); sha1.AccumulateInteger32((unsigned int)FaceStyle()); return sha1.Hash(); } bool operator==(const ON_SubDExpandEdgesParameters& lhs, const ON_SubDExpandEdgesParameters& rhs) { return lhs.Hash() == rhs.Hash(); } bool operator!=(const ON_SubDExpandEdgesParameters& lhs, const ON_SubDExpandEdgesParameters& rhs) { return lhs.Hash() != rhs.Hash(); } double ON_SubDExpandEdgesParameters::CleanupOffset(double x) { const double r[] = { 0.0, 1.0, ON_SubDExpandEdgesParameters::SmallOffset, ON_SubDExpandEdgesParameters::MediumOffset, ON_SubDExpandEdgesParameters::LargeOffset, ON_SubDExpandEdgesParameters::MinimumOffset, ON_SubDExpandEdgesParameters::MaximumOffset }; const size_t c = sizeof(r) / sizeof(r[0]); for (size_t i = 0; i < c; ++i) { if (fabs(x - r[i]) <= ON_SubDExpandEdgesParameters::OffsetTolerance) return r[i]; } if (x > 0.0 && x < ON_SubDExpandEdgesParameters::MinimumOffset) x = ON_SubDExpandEdgesParameters::MinimumOffset; else if (x < 1.0 && x > ON_SubDExpandEdgesParameters::MaximumOffset) x = ON_SubDExpandEdgesParameters::MaximumOffset; if (x >= 0.0 && x <= 1.0) return x; return ON_DBL_QNAN; } #if defined(ON_SUBD_CENSUS) ////////////////////////////////////////////////////////////////////////// // // ON_CensusCounter // class ON_PointerHashElement { public: ON__UINT_PTR m_sn = 0; ON__UINT_PTR m_ptr = 0; ON_PointerHashElement* m_next = nullptr; }; class ON_PointerHashTable { public: void Add(ON_CensusCounter::Class c, ON__UINT_PTR ptr); void Remove(ON_CensusCounter::Class c, ON__UINT_PTR ptr); ON__UINT_PTR SerialNumber(ON_CensusCounter::Class c, ON__UINT_PTR ptr) const; enum { hash_count = 1024 }; ON_PointerHashElement* m_table[(unsigned int)ON_CensusCounter::Class::count][hash_count]; unsigned int m_count = 0; static bool TheOneExists(); static ON_PointerHashTable* TheOne(); static void DestroyTheOne(); private: ON_PointerHashTable(); ~ON_PointerHashTable(); ON_PointerHashTable(const ON_PointerHashTable&) = delete; ON_PointerHashTable& operator=(const ON_PointerHashTable&) = delete; static unsigned int PointerHash(ON__UINT_PTR ptr); ON_FixedSizePool m_fsp; static ON_PointerHashTable* m_the_one; }; ON_PointerHashTable* ON_PointerHashTable::m_the_one = nullptr; ON_PointerHashTable::ON_PointerHashTable() { memset(m_table,0,sizeof(m_table)); m_fsp.Create(sizeof(ON_PointerHashElement),0,0); } ON_PointerHashTable::~ON_PointerHashTable() { memset(m_table,0,sizeof(m_table)); m_count = 0; m_fsp.Destroy(); } bool ON_PointerHashTable::TheOneExists() { return (nullptr != ON_PointerHashTable::m_the_one); } ON_PointerHashTable* ON_PointerHashTable::TheOne() { if (nullptr == ON_PointerHashTable::m_the_one) { ON_PointerHashTable::m_the_one = new ON_PointerHashTable(); } return ON_PointerHashTable::m_the_one; } void ON_PointerHashTable::DestroyTheOne() { if (nullptr != ON_PointerHashTable::m_the_one) { delete ON_PointerHashTable::m_the_one; ON_PointerHashTable::m_the_one = nullptr; } } unsigned int ON_PointerHashTable::PointerHash(ON__UINT_PTR ptr) { return (ON_CRC32(0, sizeof(ptr),&ptr) % ON_PointerHashTable::hash_count); } void ON_PointerHashTable::Add(ON_CensusCounter::Class c, ON__UINT_PTR ptr) { static ON__UINT_PTR sn = 0; // not thread safe - a crude debugging tool ON_PointerHashElement* phe = (ON_PointerHashElement*)m_fsp.AllocateDirtyElement(); phe->m_sn = ++sn; phe->m_ptr = ptr; const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr); phe->m_next = m_table[(unsigned int)c][hash_dex]; m_table[(unsigned int)c][hash_dex] = phe; m_count++; } void ON_PointerHashTable::Remove(ON_CensusCounter::Class c, ON__UINT_PTR ptr) { const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr); ON_PointerHashElement* phe = m_table[(unsigned int)c][hash_dex]; for (ON_PointerHashElement* phe0 = nullptr; nullptr != phe; phe = phe->m_next) { if (ptr == phe->m_ptr) { if ( nullptr == phe0 ) m_table[(unsigned int)c][hash_dex] = phe->m_next; else phe0 = phe->m_next; m_fsp.ReturnElement(phe); m_count--; return; } phe0 = phe; } // pointer not in the table ON_SubDIncrementErrorCount(); return; } ON__UINT_PTR ON_PointerHashTable::SerialNumber(ON_CensusCounter::Class c, ON__UINT_PTR ptr) const { const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr); for ( ON_PointerHashElement* phe = m_table[(unsigned int)c][hash_dex]; nullptr != phe; phe = phe->m_next) { if (ptr == phe->m_ptr) return phe->m_sn; } // pointer not in the table return 0; } void ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class c, ON__UINT_PTR ptr) { ON_PointerHashTable::TheOne()->Add(c,ptr); } void ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class c, ON__UINT_PTR ptr) { ON_PointerHashTable::TheOne()->Remove(c,ptr); } void ON_CensusCounter::Clear() { ON_PointerHashTable::DestroyTheOne(); } void ON_CensusCounter::CensusReport( class ON_TextLog& text_log ) { ON_PointerHashTable* ht = ON_PointerHashTable::TheOneExists() ? ON_PointerHashTable::TheOne() : nullptr; if ( nullptr == ht ) { text_log.Print("No class census information exists.\n"); return; } text_log.Print("%d items exist.\n",ht->m_count); for (unsigned int i = 0; i < (unsigned int)ON_CensusCounter::Class::count; i++) { bool bPrintClassName = true; const char* sClassName = nullptr; const ON_CensusCounter::Class c = (ON_CensusCounter::Class)i; switch (c) { case ON_CensusCounter::Class::subd: sClassName = "ON_SubD"; break; case ON_CensusCounter::Class::subd_impl: sClassName = "ON_SubDimple"; break; case ON_CensusCounter::Class::subd_limit_mesh: sClassName = "ON_SubDMesh"; break; case ON_CensusCounter::Class::subd_limit_mesh_impl: sClassName = "ON_SubDMeshImpl"; break; case ON_CensusCounter::Class::subd_ref: sClassName = "ON_SubDef"; break; default: sClassName = "Bug in ON_CensusCounter"; break; } for (unsigned int j = 0; j < ON_PointerHashTable::hash_count; j++) { for (ON_PointerHashElement* e = ht->m_table[i][j]; nullptr != e; e = e->m_next) { const unsigned int sn = (unsigned int)e->m_sn; if (bPrintClassName) { text_log.Print("\n\n%s census:\n",sClassName); bPrintClassName = false; } switch (c) { case ON_CensusCounter::Class::subd: { const ON_SubD* subd = (const ON_SubD*)e->m_ptr; if ( subd == &ON_SubD::Empty ) text_log.Print("ON_SubD::Empty (%u) ", sn); else text_log.Print("ON_SubD(%u) ", sn); const ON_SubDimple* dimple = subd->SubDimple(); if ( nullptr == dimple ) text_log.Print(" ON_SubDimple(nullptr)\n"); else { unsigned int dimple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_impl, (ON__UINT_PTR)dimple); text_log.Print(" ON_SubDimple(%u)x%u\n", dimple_sn, subd->SubDimpleUseCount()); } } break; case ON_CensusCounter::Class::subd_impl: { text_log.Print("ON_SubDimple(%u)\n", sn); } break; case ON_CensusCounter::Class::subd_limit_mesh: { const ON_SubDMesh* subd_limit_mesh = (const ON_SubDMesh*)e->m_ptr; if ( subd_limit_mesh == &ON_SubDMesh::Empty ) text_log.Print("ON_SubDMesh::Empty (%u) ", sn); else text_log.Print("ON_SubDMesh(%u) ", sn); const class ON_SubDMeshImpl* limple = subd_limit_mesh->SubLimple(); if ( nullptr == limple ) text_log.Print(" ON_SubDMeshImpl(nullptr)\n"); else { unsigned int limple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_limit_mesh_impl, (ON__UINT_PTR)limple); text_log.Print(" ON_SubDMeshImpl(%u)x%u\n", limple_sn, subd_limit_mesh->SubLimpleUseCount()); } } break; case ON_CensusCounter::Class::subd_limit_mesh_impl: { const ON_SubDMeshImpl* subd_limple = (const ON_SubDMeshImpl*)e->m_ptr; text_log.Print("ON_SubDMeshImpl(%u)", sn); std::shared_ptr limple_sp(subd_limple->m_subdimple_wp.lock()); const ON_SubDimple* dimple = limple_sp.get(); if ( nullptr == dimple ) text_log.Print(" ON_SubDimple(nullpr)\n"); else { unsigned int dimple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_impl, (ON__UINT_PTR)dimple); text_log.Print(" ON_SubDimple(%u)x%u+1\n", dimple_sn, (unsigned int)(limple_sp.use_count()-1)); } } break; case ON_CensusCounter::Class::subd_ref: { const ON_SubDRef* subd_ref = (const ON_SubDRef*)e->m_ptr; const ON_SubD* subd = &subd_ref->SubD(); ON__UINT_PTR subd_sn = ht->SerialNumber(ON_CensusCounter::Class::subd, (ON__UINT_PTR)subd); text_log.Print("ON_SubDRef(%u) ON_SubD(%u)x%u\n", sn, subd_sn, subd_ref->ReferenceCount()); } break; } } } } } static ON__UINT_PTR ON_SubDCensusCounter_This(ON_SubDCensusCounter* p) { const ON_SubD* pSubD = (const ON_SubD*)((unsigned char*)p - sizeof(ON_Geometry)); return (ON__UINT_PTR)pSubD; } ON_SubDCensusCounter::ON_SubDCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this)); } ON_SubDCensusCounter::~ON_SubDCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this)); } ON_SubDCensusCounter::ON_SubDCensusCounter(const ON_SubDCensusCounter&) ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this)); } ON_SubDRefCensusCounter::ON_SubDRefCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this); } ON_SubDRefCensusCounter::~ON_SubDRefCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this); } ON_SubDRefCensusCounter::ON_SubDRefCensusCounter(const ON_SubDRefCensusCounter&) ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this); } ON_SubDImpleCensusCounter::ON_SubDImpleCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this); } ON_SubDImpleCensusCounter::~ON_SubDImpleCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this); } ON_SubDImpleCensusCounter::ON_SubDImpleCensusCounter(const ON_SubDImpleCensusCounter&) ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this); } ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); } ON_SubDMeshCensusCounter::~ON_SubDMeshCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); } ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); } ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); } ON_SubDMeshCensusCounter::~ON_SubDMeshCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); } ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); } #endif