#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" /* $NoKeywords: $ */ /* // // 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_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_SubD::VertexTag ON_SubD::VertexTagFromUnsigned( unsigned int vertex_tag_as_unsigned ) { switch (vertex_tag_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Smooth); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Crease); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Corner); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexTag::Dart); } return ON_SUBD_RETURN_ERROR(ON_SubD::VertexTag::Unset); } bool ON_SubD::VertexTagIsSet( ON_SubD::VertexTag vertex_tag ) { return ( ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag ); } ON_SubD::EdgeTag ON_SubD::EdgeTagFromUnsigned( unsigned int edge_tag_as_unsigned ) { switch (edge_tag_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Smooth); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Crease); //ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Sharp); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::SmoothX); } return ON_SUBD_RETURN_ERROR(ON_SubD::EdgeTag::Unset); } bool ON_SubD::EdgeTagIsSet( ON_SubD::EdgeTag edge_tag ) { return ( ON_SubD::EdgeTag::Smooth == edge_tag || ON_SubD::EdgeTag::Crease == edge_tag //|| ON_SubD::EdgeTag::Sharp == edge_tag || ON_SubD::EdgeTag::SmoothX == edge_tag ); } ON_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_SubD::VertexTag vertex_tag, unsigned int sector_edge_count ) { if (sector_edge_count >= ON_SubDSectorType::MinimumSectorEdgeCount(vertex_tag) && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount) { if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) { // interior vertex return (2 * sector_edge_count + 1); } if (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag) { // boundary vertex return (2 * sector_edge_count); } } return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDSectorType::SectorPointRingCountFromFaceCount( ON_SubD::VertexTag 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_SubD::VertexTag vertex_tag, unsigned int sector_edge_count ) { if (sector_edge_count >= 2 && sector_edge_count <= ON_SubDVertex::MaximumEdgeCount) { unsigned int sector_face_count = (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag) ? sector_edge_count-1 : sector_edge_count; return sector_face_count; } return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDSectorType::SectorEdgeCountFromFaceCount( ON_SubD::VertexTag vertex_tag, unsigned int sector_face_count ) { if (sector_face_count > 0 && sector_face_count <= ON_SubDVertex::MaximumFaceCount) { unsigned int sector_edge_count = (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag) ? sector_face_count+1 : sector_face_count; return sector_edge_count; } return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDSectorType::MinimumSectorEdgeCount( ON_SubD::VertexTag vertex_tag ) { if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) return ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag); if (ON_SubD::VertexTag::Corner == vertex_tag || ON_SubD::VertexTag::Crease == vertex_tag) return ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag)+1; return ON_UNSET_UINT_INDEX; } unsigned int ON_SubDSectorType::MinimumSectorFaceCount( ON_SubD::VertexTag vertex_tag ) { unsigned int minimum_sector_face_count; switch (vertex_tag) { case ON_SubD::VertexTag::Unset: ON_SUBD_ERROR("Unset tag."); minimum_sector_face_count = ON_UNSET_UINT_INDEX; break; case ON_SubD::VertexTag::Smooth: // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() // for more details on how this case is handled. minimum_sector_face_count = 2; // 3 without special case handling break; case ON_SubD::VertexTag::Crease: // A "wire" crease can have zero faces - this is the minimum when faces exist minimum_sector_face_count = 1; break; case ON_SubD::VertexTag::Corner: // A "wire" corner can have zero faces - this is the minimum when faces exist minimum_sector_face_count = 1; break; case ON_SubD::VertexTag::Dart: // 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_SubD::VertexTag 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_SubD::VertexTag 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)); } 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); } 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; } ON__UINT_PTR ON_SubDEdgePtr::EdgeDirection() const { return ON_SUBD_EDGE_DIRECTION(m_ptr); } 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; } 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 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()); } ////////////////////////////////////////////////////////////////////////// // // 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); } 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::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 (0 == (ON_SUBD_COMPONENT_POINTER_MASK && m_ptr)); } bool ON_SubDComponentPtr::IsNotNull() const { if (nullptr != ON_SUBD_COMPONENT_POINTER(m_ptr)) { 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: return true; } } return false; } unsigned int ON_SubDComponentPtr::ComponentId() const { const ON_SubDComponentBase* c = this->ComponentBase(); return (nullptr != c) ? c->m_id : 0U; } 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); } 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; } 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; } 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; } 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__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::ToggleComponentDirection() 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: return ((class ON_SubDComponentBase*)ON_SUBD_COMPONENT_POINTER(m_ptr)); } 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()); if ( 0 == element_type ) return ON_SubDVertexPtr::Null; return ON_SUBD_RETURN_ERROR(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()); if ( 0 == element_type ) return ON_SubDEdgePtr::Null; return ON_SUBD_RETURN_ERROR(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()); if ( 0 == element_type ) return ON_SubDFacePtr::Null; return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null); } 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; } 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_SubDComponentPtr ON_SubDComponentPtrPair::First() const { return m_pair[0]; } const ON_SubDComponentPtr ON_SubDComponentPtrPair::Second() const { return m_pair[1]; } ////////////////////////////////////////////////////////////////////////// // // ON_ToSubDParameters // ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::ConvexCornerOptionFromUnsigned( unsigned int convex_corner_option_as_unsigned ) { switch (convex_corner_option_as_unsigned) { case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::Unset: return ON_ToSubDParameters::ConvexCornerOption::Unset; case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::None: return ON_ToSubDParameters::ConvexCornerOption::None; case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner: return ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner; default: break; } return ON_ToSubDParameters::ConvexCornerOption::Unset; } void ON_ToSubDParameters::SetConvexCornerOption( ON_ToSubDParameters::ConvexCornerOption convex_corner_option ) { m_convex_corner_option = ON_ToSubDParameters::ConvexCornerOptionFromUnsigned((unsigned int)convex_corner_option); } ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::ConvexCornerTest() const { switch (m_convex_corner_option) { case ON_ToSubDParameters::ConvexCornerOption::Unset: case ON_ToSubDParameters::ConvexCornerOption::None: return m_convex_corner_option; case ON_ToSubDParameters::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_ToSubDParameters::ConvexCornerOption::Unset; } void ON_ToSubDParameters::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_ToSubDParameters::MaximumConvexCornerEdgeCount() const { return m_maximum_convex_corner_edge_count; } void ON_ToSubDParameters::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_ToSubDParameters::MaximumConvexCornerAngleRadians() const { return m_maximum_convex_corner_angle_radians; } ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::CopyConvexCornerTest( ON_ToSubDParameters source_parameters ) { SetConvexCornerOption(source_parameters.ConvexCornerTest()); SetMaximumConvexCornerEdgeCount(source_parameters.MaximumConvexCornerEdgeCount()); SetMaximumConvexCornerAngleRadians(source_parameters.MaximumConvexCornerAngleRadians()); return ConvexCornerTest(); } bool ON_ToSubDParameters::InterpolateMeshVertices() const { return m_bInterpolateMeshVertices; } void ON_ToSubDParameters::SetInterpolateMeshVertices( bool bInterpolateMeshVertices ) { // Not supported in free opennurbs m_bInterpolateMeshVertices = false; } bool ON_ToSubDParameters::MergeColinearBoundaryEdges() const { // clear bit means true, set bit means false return (0 == (ON_ToSubDParameters::MergeColinearBoundaryEdgesMask & m_merge_edges_bits)); } void ON_ToSubDParameters::SetMergeColinearBoundaryEdges( bool bAllowColinearBoundaryEdges ) { const unsigned char mask = ON_ToSubDParameters::MergeColinearBoundaryEdgesMask; if (false == bAllowColinearBoundaryEdges) m_merge_edges_bits |= mask; // set bit else m_merge_edges_bits &= ~mask; // clear bit } bool ON_ToSubDParameters::MergeColinearInteriorEdges() const { // clear bit means true, set bit means false return (0 == (ON_ToSubDParameters::MergeColinearInteriorEdgesMask & m_merge_edges_bits)); } void ON_ToSubDParameters::SetMergeColinearInteriorEdges( bool bAllowColinearInteriorEdges ) { const unsigned char mask = ON_ToSubDParameters::MergeColinearInteriorEdgesMask; if (false == bAllowColinearInteriorEdges) m_merge_edges_bits |= mask; // set bit else m_merge_edges_bits &= ~mask; // clear bit } void ON_ToSubDParameters::SetInteriorCreaseOption( ON_ToSubDParameters::InteriorCreaseOption interior_crease_option ) { m_interior_crease_option = ON_ToSubDParameters::InteriorCreaseOptionFromUnsigned((unsigned int)interior_crease_option); } ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::InteriorCreaseTest() const { return m_interior_crease_option; } void ON_ToSubDParameters::SetMinimumCreaseAngleRadians( double minimum_crease_angle_radians ) { if (minimum_crease_angle_radians >= 0.0 && minimum_crease_angle_radians < ON_PI) m_minimum_crease_angle_radians = minimum_crease_angle_radians; } double ON_ToSubDParameters::MinimumCreaseAngleRadians() const { return m_minimum_crease_angle_radians; } ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::CopyInteriorCreaseTest( ON_ToSubDParameters source_parameters ) { SetInteriorCreaseOption(source_parameters.InteriorCreaseTest()); SetMinimumCreaseAngleRadians(source_parameters.MinimumCreaseAngleRadians()); return InteriorCreaseTest(); } ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::InteriorCreaseOptionFromUnsigned( unsigned int interior_crease_option_as_unsigned ) { switch (interior_crease_option_as_unsigned) { case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::Unset: return ON_ToSubDParameters::InteriorCreaseOption::Unset; break; case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::None: return ON_ToSubDParameters::InteriorCreaseOption::None; break; case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease: return ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease; break; case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge: return ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge; break; default: break; } return ON_ToSubDParameters::InteriorCreaseOption::Unset; } ////////////////////////////////////////////////////////////////////////// // // 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_SubD::EdgeTag 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; } 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; } 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_SubD::VertexTag::Smooth == m_vertex_tag); } bool ON_SubDVertex::IsCrease() const { return (ON_SubD::VertexTag::Crease == m_vertex_tag); } bool ON_SubDVertex::IsCorner() const { return (ON_SubD::VertexTag::Corner == m_vertex_tag); } bool ON_SubDVertex::IsDart() const { return (ON_SubD::VertexTag::Dart == m_vertex_tag); } bool ON_SubDVertex::IsCreaseOrCorner() const { return (ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag); } bool ON_SubDVertex::IsDartOrCreaseOrCorner() const { return ( ON_SubD::VertexTag::Dart == m_vertex_tag || ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag ); } bool ON_SubDVertex::IsSmoothOrDart() const { return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag); } bool ON_SubDVertex::IsSmoothOrCrease() const { return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Crease == m_vertex_tag); } const ON_SubDVertexEdgeProperties ON_SubDVertex::EdgeProperties() const { ON_SubDVertexEdgeProperties ep; bool bFirstEdge = true; const unsigned short edge_count = m_edge_count; for (unsigned short vei = 0; vei < 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_SubD::EdgeTag::Crease == m_edge_tag) ? true : false; } bool ON_SubDEdge::IsHardCrease() const { return ( ON_SubD::EdgeTag::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_SubD::EdgeTag::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_SubD::VertexTag::Dart == m_vertex[0]->m_vertex_tag) dart_count++; if (nullptr != m_vertex[1] && ON_SubD::VertexTag::Dart == m_vertex[1]->m_vertex_tag) dart_count++; return dart_count; } bool ON_SubDEdge::IsSmooth() const { return (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::SmoothX == m_edge_tag) ? true : false; } bool ON_SubDEdge::IsSmoothNotX() const { return (ON_SubD::EdgeTag::Smooth == m_edge_tag) ? true : false; } bool ON_SubDEdge::IsSmoothX() const { return (ON_SubD::EdgeTag::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_SubD::EdgeTag::Crease == e->m_edge_tag) { if (2 == e->m_face_count) { ++interior_crease_count; if (ON_SubD::VertexTag::Dart == m_vertex_tag && 1 == interior_crease_count) 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_SubD::VertexTag::Dart == m_vertex_tag) { if (1 == interior_crease_count && 1+interior_smooth_count == m_edge_count) return true; } else if (ON_SubD::VertexTag::Smooth == m_vertex_tag) { 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; } 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_SubD::VertexTag::Unset: return false; break; case ON_SubD::VertexTag::Smooth: if (edge_count != face_count) return false; break; case ON_SubD::VertexTag::Crease: if (edge_count != face_count+1) return false; crease_edge_face_count = 1; bTaggedVertex = true; break; case ON_SubD::VertexTag::Corner: if (edge_count != face_count+1) return false; crease_edge_face_count = 1; bTaggedVertex = true; break; case ON_SubD::VertexTag::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_SubD::EdgeTag::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_SubD::VertexTag::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_SubD::EdgeTag::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 ) { if ( nullptr == src ) src = &ON_SubDComponentBase::Unset; *this = *src; m_subd_point1 = nullptr; Internal_ClearSurfacePointFlag(); } void ON_SubDEdge::CopyFrom( const ON_SubDEdge* src, bool bReverseEdge, bool bCopyVertexArray, bool bCopyFaceArray ) { if (nullptr == src) src = &ON_SubDEdge::Empty; CopyBaseFrom(src); 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 int ON_SubDEdge::FaceCount() const { return m_face_count; } bool ON_SubDEdge::IsInteriorEdge() const { for (;;) { if (2 != m_face_count) break; if (ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr)) break; if (nullptr == ON_SUBD_FACE_POINTER(m_face2[0].m_ptr)) break; if (nullptr == ON_SUBD_FACE_POINTER(m_face2[1].m_ptr)) break; } 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_SubD::EdgeTag::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_SubD::EdgeTag::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 class ON_SubDVertex* ON_SubDEdge::Vertex( unsigned int i ) const { return (i <= 1) ? m_vertex[i] : nullptr; } const ON_SubDVertex* ON_SubDEdge::OtherEndVertex( const ON_SubDVertex* vertex ) const { 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_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_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_SubD::EdgeTag::Smooth != e->m_edge_tag) // return false; // ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(eptr); // const ON_SubDVertex* v = e->m_vertex[edir]; // if (nullptr == v || false == v->IsOrdinary(subdivision_type,ON_SubD::VertexTag::Unset,bTestFaces)) // return false; // } // return true; //} void ON_SubDFace::CopyFrom( const ON_SubDFace* src, bool bCopyEdgeArray ) { if (nullptr == src) src = &ON_SubDFace::Empty; CopyBaseFrom(src); m_next_face = nullptr; m_zero_face_id = src->m_zero_face_id; m_parent_face_id = src->m_parent_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; } } 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; } 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; } 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_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 - 4].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 < 4 && j < count ) m_edge4[i++] = m_edge4[j++]; if (count > 4) { m_edge4[3] = m_edgex[0]; i = 0; j = 1; count -= 4; while (j < count ) m_edgex[i++] = m_edgex[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; } ////////////////////////////////////////////////////////////////////////// // // 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::ContentSerialNumber() const { ON_SubDimple* subdimple = m_subdimple_sp.get(); return (nullptr != subdimple) ? subdimple->ContentSerialNumber() : 0; } ON__UINT64 ON_SubD::ChangeContentSerialNumberForExperts() { if (this == &ON_SubD::Empty) return 0; ON_SubDimple* subdimple = m_subdimple_sp.get(); return (nullptr != subdimple) ? subdimple->ChangeContentSerialNumber() : 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 EdgeVertexWeightIsSet( double edge_vertex_weight ) { return (0.0 < edge_vertex_weight && edge_vertex_weight < 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::UnsetSectorWeight == 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_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX if (ON_SubD::VertexTag::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_SubD::EdgeTag::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_vertex_weight = st.SectorWeight(); if (false == (expected_vertex_weight == edge->m_sector_coefficient[end_index])) return ON_SubDIsNotValid(bSilentError); if (false == EdgeVertexWeightIsSet(expected_vertex_weight)) return ON_SubDIsNotValid(bSilentError); } } else if(ON_SubD::EdgeTag::Crease == edge->m_edge_tag) { // crease edge if (!(0.0 == edge->m_sector_coefficient[end_index])) return ON_SubDIsNotValid(bSilentError); if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag) return ON_SubDIsNotValid(bSilentError); if (ON_SubD::VertexTag::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_SubD::VertexTag::Unset: return ON_SubDIsNotValid(bSilentError); break; case ON_SubD::VertexTag::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_SubD::VertexTag::Crease: if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); if ( 2 != crease_edge_count ) { return ON_SubDIsNotValid(bSilentError); } break; case ON_SubD::VertexTag::Corner: if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); if ( crease_edge_count < 2 ) { return ON_SubDIsNotValid(bSilentError); } break; case ON_SubD::VertexTag::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_SubD::VertexTag vtag[2] = { ON_SubD::VertexTag::Unset,ON_SubD::VertexTag::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_SubD::VertexTag::Smooth: ++smooth_vertex_count; break; case ON_SubD::VertexTag::Crease: ++crease_vertex_count; break; case ON_SubD::VertexTag::Corner: ++corner_vertex_count; break; case ON_SubD::VertexTag::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_SubD::EdgeTag::Unset: return ON_SubDIsNotValid(bSilentError); break; case ON_SubD::EdgeTag::Smooth: if ( 2 != edge->m_face_count) return ON_SubDIsNotValid(bSilentError); if ( smooth_vertex_count < 1) return ON_SubDIsNotValid(bSilentError); break; case ON_SubD::EdgeTag::Crease: if ( 0 != smooth_vertex_count ) return ON_SubDIsNotValid(bSilentError); break; case ON_SubD::EdgeTag::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_SubD::VertexTag::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_SubD::VertexTag::Smooth: // interior vertex if (vertex->m_edge_count != vertex->m_face_count) return ON_SubDIsNotValid(bSilentError); break; case ON_SubD::VertexTag::Crease: if ( vertex->m_edge_count < 2 ) return ON_SubDIsNotValid(bSilentError); break; case ON_SubD::VertexTag::Corner: if ( vertex->m_edge_count < 1 ) return ON_SubDIsNotValid(bSilentError); break; case ON_SubD::VertexTag::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_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX if ( 2 != edge->m_face_count) return ON_SubDIsNotValid(bSilentError); } else if (ON_SubD::EdgeTag::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] > m_max_vertex_id ) 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] > m_max_edge_id ) return ON_SubDIsNotValid(bSilentError); // 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] > m_max_face_id ) 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 { 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; } if (false == m_heap.IsValid()) { // id values are not increasing in the heap blocks. 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); } if (MaximumVertexId() < m_heap.MaximumVertexId()) { if (nullptr != text_log) text_log->Print("MaximumVertexId() = %u < m_heap.MaximumVertexId() = %u\n", MaximumVertexId(), m_heap.MaximumVertexId()); return ON_SubDIsNotValid(bSilentError); } if (MaximumEdgeId() < m_heap.MaximumEdgeId()) { if (nullptr != text_log) text_log->Print("MaximumEdgeId() = %u < m_heap.MaximumEdgeId() = %u\n", MaximumEdgeId(), m_heap.MaximumEdgeId()); return ON_SubDIsNotValid(bSilentError); } if (MaximumFaceId() Print("MaximumFaceId() = %u < m_heap.MaximumFaceId() = %u\n", MaximumFaceId(), m_heap.MaximumFaceId()); return ON_SubDIsNotValid(bSilentError); } 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 { unsigned int component_sample_count = 16; // dump the first 16 vertices, edges, faces 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); } 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 ON__UINT64 runtime_sn = (text_log.IsTextHash()) ? 0 : RuntimeSerialNumber(); // TextHash ignores settings that don't depend on 3dm file content. const ON_wString subd_texture_domain = ON_SubD::TextureDomainTypeToString(this->TextureDomainType()); if (level_count > 1) text_log.Print(L"SubD[%" PRIu64 "]: %u levels. Level %u is active. texture domain type = %ls.\n", runtime_sn, level_count, active_level_index, static_cast(subd_texture_domain) ); else text_log.Print(L"SubD[%" PRIu64 "]: texture domain type = %ls.\n", runtime_sn, static_cast(subd_texture_domain)); ON_SubDLevelIterator lit(subdimple->LevelIterator()); const ON_2udex empty_id_range(ON_UNSET_UINT_INDEX, 0); 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( subdimple->MaximumVertexId(), subdimple->MaximumEdgeId(), subdimple->MaximumFaceId(), level_vertex_id_range, level_edge_id_range, level_face_id_range, text_log); } return error_count; } unsigned int ON_SubDLevel::DumpTopology( 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_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 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; /////////////////////////////////////////////////////////////////// // // Vertex Topology // vN (x, y, z) // vEdges[vertex_edge_count] = { +eA, -eB, ... } // vFaces[vertex_edge_count] = { fP, fQ, fR, ... } // 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; 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++; if (bVertexIdTest) { 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; 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()); ON_wString vtag; switch (v->m_vertex_tag) { case ON_SubD::VertexTag::Unset: vtag = L"Unset"; break; case ON_SubD::VertexTag::Smooth: vtag = L"Smooth"; break; case ON_SubD::VertexTag::Crease: vtag = L"Crease"; break; case ON_SubD::VertexTag::Corner: vtag = L"Corner"; break; case ON_SubD::VertexTag::Dart: vtag = L"Dart"; break; default: vtag = L"INVALID"; break; } text_log.Print( "v%u: %ls (%g, %g, %g)\n", v->m_id, static_cast(vtag), P0.x, P0.y, P0.z ); text_log.PushIndent(); ON_3dVector D(ON_3dVector::NanVector); if (v->GetSubdivisionDisplacement(&D.x) && D.IsValid()) text_log.Print( "v.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z ); 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] = ON_String::Space; prefix[1] = error_code_point; prefix[2] = 'e'; prefix[3] = 0; for (unsigned int vei = 0; vei < vertex_edge_count; vei++) { prefix[1] = error_code_point; if (1 == vei) { prefix[0] = ','; } const ON_SubDEdge* e = v->Edge(vei); unsigned int eid = 0; if (nullptr != e) { if (v == e->m_vertex[0] && v != e->m_vertex[1]) prefix[1] = '+'; else if (v != e->m_vertex[0] && v == e->m_vertex[1]) prefix[1] = '-'; eid = e->m_id; } text_log.Print("%s%u", prefix, eid); if (error_code_point == prefix[1]) 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] = ON_String::Space; prefix[1] = ON_String::Space; prefix[2] = 'f'; prefix[3] = 0; for (unsigned int vfi = 0; vfi < vertex_face_count; vfi++) { prefix[1] = error_code_point; if (1 == vfi) { prefix[0] = ','; } const ON_SubDFace* f = v->Face(vfi); unsigned int fid = 0; if (nullptr != f) { if (f->VertexIndex(v) < ON_UNSET_UINT_INDEX) prefix[1] = ON_String::Space; fid = f->m_id; } text_log.Print("%s%u", prefix, fid); if (error_code_point == prefix[1]) 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, ... } // 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; 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++; if (bEdgeIdTest) { 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; continue; } } if (0 == edge_dump_count) text_log.Print(L"Edges:\n"); edge_dump_count++; ON_TextLogIndent eindent(text_log); ON_wString etag; switch (e->m_edge_tag) { case ON_SubD::EdgeTag::Unset: etag = L"Unset"; break; case ON_SubD::EdgeTag::Smooth: etag = L"Smooth"; break; case ON_SubD::EdgeTag::Crease: etag = L"Crease"; break; case ON_SubD::EdgeTag::SmoothX: etag = L"SmmothX"; break; default: etag = L"INVALID"; break; } text_log.Print( "e%u: %ls (", e->m_id, static_cast(etag) ); prefix[0] = ON_String::Space; prefix[1] = error_code_point; prefix[2] = 'v'; prefix[3] = 0; for (unsigned int evi = 0; evi < 2; evi++) { if (1 == evi) text_log.Print(" to"); prefix[1] = error_code_point; const ON_SubDVertex* v = e->m_vertex[evi]; unsigned int vid = 0; if (nullptr != v) { vid = v->m_id; if (v->EdgeArrayIndex(e) < ON_UNSET_INT_INDEX) prefix[1] = ON_String::Space; } if (error_code_point == prefix[1]) edge_error_count++; text_log.Print("%s%u", prefix, vid); } text_log.Print(")\n"); text_log.PushIndent(); ON_3dVector D(ON_3dVector::NanVector); if (e->GetSubdivisionDisplacement(&D.x) && D.IsValid()) text_log.Print( "e.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z ); 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] = ON_String::Space; prefix[1] = error_code_point; prefix[2] = 'f'; prefix[3] = 0; for (unsigned int efi = 0; efi < edge_face_count; efi++) { prefix[1] = error_code_point; if (1 == efi) { prefix[0] = ','; } ON_SubDFacePtr fptr = e->FacePtr(efi); const ON_SubDFace* f = fptr.Face(); const ON__UINT_PTR edge_fdir = fptr.FaceDirection(); unsigned int fid = 0; if (nullptr != f) { fid = f->m_id; ON_SubDEdgePtr eptr = f->EdgePtrFromEdge(e); if (eptr.Edge() == e && eptr.EdgeDirection() == edge_fdir) { prefix[1] = (0 == edge_fdir) ? '+' : '-'; } } if (error_code_point == prefix[1]) edge_error_count++; text_log.Print("%s%u", prefix, fid); } 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, ... } // face_count = 0; unsigned int face_dump_count = 0; ON_2udex skipped_face_id = ON_2udex::Zero; unsigned int max_face_id = 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; if (f->m_id > max_face_id) max_face_id = f->m_id; face_count++; if (bFaceIdTest) { 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; continue; } } if (0 == face_dump_count) text_log.Print(L"Faces:\n"); face_dump_count++; ON_TextLogIndent eindent(text_log); text_log.Print( "f%u:\n", f->m_id ); text_log.PushIndent(); ON_3dVector D(ON_3dVector::NanVector); if (f->GetSubdivisionDisplacement(&D.x) && D.IsValid()) text_log.Print( "f.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z ); 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] = ON_String::Space; prefix[1] = error_code_point; prefix[2] = 'e'; prefix[3] = 0; for (unsigned int fei = 0; fei < face_edge_count; fei++) { prefix[1] = error_code_point; if (1 == fei) { prefix[0] = ','; } const ON_SubDEdgePtr eptr = f->EdgePtr(fei); const ON_SubDEdge* e = eptr.Edge(); const ON__UINT_PTR face_edir = eptr.EdgeDirection(); unsigned int eid = 0; if (nullptr != e) { eid = e->m_id; ON_SubDFacePtr fptr = e->FacePtrFromFace(f); if (fptr.Face() == f && fptr.FaceDirection() == face_edir) { prefix[1] = (0 == face_edir) ? '+' : '-'; } } if (error_code_point == prefix[1]) face_error_count++; text_log.Print("%s%u", prefix, eid); } text_log.Print(" }\n"); const ON_SubDEdgePtr last_eptr = f->EdgePtr(face_edge_count - 1); const ON_SubDEdge* last_edge = last_eptr.Edge(); const ON_SubDVertex* v1 = (nullptr != last_edge) ? last_edge->m_vertex[0 == last_eptr.EdgeDirection() ? 1 : 0] : nullptr; text_log.Print("f.Vertices[%u] = {", face_edge_count); prefix[0] = ON_String::Space; prefix[1] = error_code_point; prefix[2] = 'v'; prefix[3] = 0; for (unsigned int fei = 0; fei < face_edge_count; fei++) { prefix[1] = error_code_point; if (1 == fei) { prefix[0] = ','; } const ON_SubDEdgePtr eptr = f->EdgePtr(fei); const ON_SubDEdge* e = eptr.Edge(); const ON__UINT_PTR face_edir = eptr.EdgeDirection(); unsigned int vid = 0; if (nullptr == e) { v1 = nullptr; } else { const ON_SubDVertex* v0 = e->m_vertex[0 == face_edir ? 0 : 1]; if (nullptr != v0) { vid = v0->m_id; if (v1 == v0) prefix[1] = ON_String::Space; } v1 = e->m_vertex[0 == face_edir ? 1 : 0]; } if (error_code_point == prefix[1]) face_error_count++; text_log.Print("%s%u", prefix, vid); } text_log.Print(" }\n"); if (f->TextureDomainIsSet()) { const ON_wString s = ON_SubD::TextureDomainTypeToString(f->TextureDomainType()); const bool bGridOrder = true; const bool bNormalized = false; ON_2dPoint corners[4] = { f->TextureDomainCorner(bGridOrder,bNormalized,0), f->TextureDomainCorner(bGridOrder,bNormalized,1), f->TextureDomainCorner(bGridOrder,bNormalized,2), f->TextureDomainCorner(bGridOrder,bNormalized,3) }; text_log.Print("%ls texture domain. Grid corners: (%g,%g), (%g,%g), (%g,%g), (%g,%g)\n", static_cast(s), corners[0].x, corners[0].y, corners[1].x, corners[1].y, corners[2].x, corners[2].y, corners[3].x, corners[3].y ); ////const ON_SubDMeshFragment *fragments = f->MeshFragments(); ////if (nullptr != fragments) ////{ ////ON_TextLogIndent indent1(text_log); //// unsigned short fragment_count = 0; //// const unsigned short expected_fragment_count = (unsigned short)((4 == face_edge_count) ? 1 : face_edge_count); //// ON_3dPoint fragmentT[4]; //// bool bDamagedOrIncompleteFragments = false; //// for (unsigned short fragdex = 0; fragdex <= expected_fragment_count && nullptr != fragments; fragdex++, fragments = fragments->m_next_fragment) //// { //// ++fragment_count; //// if (expected_fragment_count != fragments->m_face_fragment_count) //// { //// bDamagedOrIncompleteFragments = true; //// break; //// } //// if (fragdex != fragments->m_face_fragment_index) //// { //// bDamagedOrIncompleteFragments = true; //// break; //// } //// } //// if (expected_fragment_count == fragment_count) //// { //// fragments = f->MeshFragments(); //// if (1 == fragment_count) //// { //// if (fragments->GetTextureCoordinteCorners(bGridOrder, fragmentT)) //// { //// text_log.Print("Quad face fragment texture corners = (%g,%g,%g), (%g,%g,%g), (%g,%g,%g), (%g,%g,%g)\n", //// fragmentT[0].x, fragmentT[0].y, fragmentT[0].z, //// fragmentT[1].x, fragmentT[1].y, fragmentT[1].z, //// fragmentT[2].x, fragmentT[2].y, fragmentT[2].z, //// fragmentT[3].x, fragmentT[3].y, fragmentT[3].z //// ); //// } //// else //// { //// text_log.Print("Quad face fragment texture corners not available.\n"); //// } //// } //// else //// { //// text_log.Print("%u face fragments.\n", (unsigned)expected_fragment_count); //// ON_TextLogIndent indent2(text_log); //// for (unsigned short fragdex = 0; fragdex <= expected_fragment_count && nullptr != fragments; fragdex++, fragments = fragments->m_next_fragment) //// { //// if (fragments->GetTextureCoordinteCorners(bGridOrder, fragmentT)) //// { //// text_log.Print("fragment[%u] texture corners = (%g,%g,%g), (%g,%g,%g), (%g,%g,%g), (%g,%g,%g)\n", //// (unsigned)fragdex, //// fragmentT[0].x, fragmentT[0].y, fragmentT[0].z, //// fragmentT[1].x, fragmentT[1].y, fragmentT[1].z, //// fragmentT[2].x, fragmentT[2].y, fragmentT[2].z, //// fragmentT[3].x, fragmentT[3].y, fragmentT[3].z //// ); //// } //// else //// { //// text_log.Print("fragment[%u] texture corners not available.\n", (unsigned)fragdex); //// } //// } //// } //// } //// else if (nullptr == fragments) //// { //// bDamagedOrIncompleteFragments = true; //// } //// if (bDamagedOrIncompleteFragments ) //// text_log.Print("Damaged or incomplete mesh fragments.\n"); ////} } 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; } //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; } //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->AggregateComponentStatus().MarkAsNotCurrent(); } } dimple->ChangeContentSerialNumber(); } 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; } ////////////////////////////////////////////////////////////// // // // 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(); } void ON_SubD::ClearHigherSubdivisionLevels( unsigned int max_level_index ) { ON_SubDimple* subdimple = SubDimple(false); if ( subdimple ) subdimple->ClearHigherSubdivisionLevels(max_level_index); } void ON_SubD::ClearLowerSubdivisionLevels( unsigned int min_level_index ) { ON_SubDimple* subdimple = SubDimple(false); if ( subdimple ) subdimple->ClearLowerSubdivisionLevels(min_level_index); } void ON_SubD::Destroy() { m_subdimple_sp.reset(); } class ON_SubDVertex* ON_SubD::AddVertex( const double* P ) { return AddVertex(ON_SubD::VertexTag::Unset, P); } class ON_SubDVertex* ON_SubD::AddVertex( ON_SubD::VertexTag 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; } class ON_SubDEdge* ON_SubDimple::AddEdge( ON_SubD::EdgeTag edge_tag, ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, double v1_sector_weight ) { if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v0_sector_weight,true) ) return ON_SUBD_RETURN_ERROR(nullptr); if ( false == ON_SubDSectorType::IsValidSectorWeightValue(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::IgnoredSectorWeight != v0_sector_weight && ON_SubDSectorType::UnsetSectorWeight != v0_sector_weight && nullptr != v0 && ON_SubD::VertexTag::Smooth == v0->m_vertex_tag ) { // minimizes checking when building subds because constant crease weights can be passed in v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; } if ( bEdgeTagSet && ON_SubDSectorType::IgnoredSectorWeight != v1_sector_weight && ON_SubDSectorType::UnsetSectorWeight != v1_sector_weight && nullptr != v1 && ON_SubD::VertexTag::Smooth == v1->m_vertex_tag ) { // minimizes checking when building subds because constant crease weights can be passed in v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; } class ON_SubDEdge* e = AllocateEdge(edge_tag); 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); return e; } ON_SubD::EdgeTag 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_SubD::EdgeTag::Unset; } ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext( unsigned int edge_face_count, const ON_SubD::VertexTag v0_tag, const ON_SubD::VertexTag v1_tag ) { ON_SubD::EdgeTag edge_tag = ON_SubD::EdgeTag::Unset; for(;;) { if (edge_face_count > 0x7FFFU) break; if (1 == edge_face_count || edge_face_count >= 3 ) { edge_tag = ON_SubD::EdgeTag::Crease; break; } const bool bSmooth0 = ON_SubD::VertexTag::Smooth == v0_tag; const bool bSmooth1 = ON_SubD::VertexTag::Smooth == v1_tag; if ( bSmooth0 || bSmooth1 ) { if ( 2 == edge_face_count && bSmooth0 && bSmooth1) edge_tag = ON_SubD::EdgeTag::Smooth; break; } if ( ON_SubD::VertexTagIsSet(v0_tag) && ON_SubD::VertexTagIsSet(v1_tag) ) { if (2 == edge_face_count) edge_tag = ON_SubD::EdgeTag::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_SubD::EdgeTag::Unset, v0, v1); } class ON_SubDEdge* ON_SubD::AddEdge( ON_SubD::EdgeTag 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::UnsetSectorWeight, v1, ON_SubDSectorType::UnsetSectorWeight ); } ON_SubDEdge* ON_SubD::AddEdgeWithSectorCoefficients( ON_SubD::EdgeTag 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_SubDFace* ON_SubDimple::AddFace( unsigned int edge_count, const ON_SubDEdgePtr* edge ) { return AddFace(nullptr, edge_count, edge); } class ON_SubDFace* ON_SubDimple::AddFace( const ON_SubDFace* candidate_face, 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); 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; } bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCoefficientsOnly) { 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::IgnoredSectorWeight; m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; if (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::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).SectorWeight(); } else if (2 == tagged_end_index) { if (ON_SubD::EdgeTag::Smooth == m_edge_tag && 2 == m_face_count ) m_edge_tag = ON_SubD::EdgeTag::SmoothX; if (ON_SubD::EdgeTag::Smooth == m_edge_tag) m_edge_tag = ON_SubD::EdgeTag::Crease; else if (ON_SubD::EdgeTag::SmoothX == m_edge_tag) { m_sector_coefficient[0] = ON_SubDSectorType::Create( this, 0).SectorWeight(); m_sector_coefficient[1] = ON_SubDSectorType::Create( this, 1).SectorWeight(); } } } 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 ) { 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].IsNull() && (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::AddFace( const ON_SubDFace* candidate_face, const ON_SubDEdgePtr* edge, unsigned int edge_count ) { ON_SubDimple* subdimple = SubDimple(true); return (nullptr != subdimple) ? subdimple->AddFace(candidate_face, edge_count, edge) : nullptr; } 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 ) { 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; 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 face_point[3], bool bDamagedState ) { if (nullptr == face || nullptr == face_point) return ON_SUBD_RETURN_ERROR(false); // caller passed a null pointer - edge is not necessarily damaged 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); double displacementV[3] = { 0 }; const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV); 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); 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); 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; if (bApplyDisplacement) { subdivision_point[0] += displacementV[0]; subdivision_point[1] += displacementV[1]; subdivision_point[2] += displacementV[2]; } 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; if (bApplyDisplacement) { subdivision_point[0] += displacementV[0]; subdivision_point[1] += displacementV[1]; subdivision_point[2] += displacementV[2]; } 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); 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); 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; if (bApplyDisplacement) { subdivision_point[0] += displacementV[0]; subdivision_point[1] += displacementV[1]; subdivision_point[2] += displacementV[2]; } 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::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); } 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_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++) { if (4 == efi) { eptr = face->m_edgex; if ( nullptr == eptr) 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) vertex->ClearSavedSubdivisionPoints(); } } eptr++; } } void ON_SubDVertex::VertexModifiedNofification() const { 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) { edge->ClearSavedSubdivisionPoints(); edge->UnsetSectorCoefficientsForExperts(); } } // 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 = this->m_faces[vfi]; if (nullptr != face) Internal_ClearFaceNeighborhoodCache(face); } } } void ON_SubDEdge::EdgeModifiedNofification() const { ClearSavedSubdivisionPoints(); UnsetSectorCoefficientsForExperts(); for (unsigned int evi = 0; evi < 2; evi++) { const_cast(this)->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorWeight; if (nullptr != m_vertex[evi]) m_vertex[evi]->VertexModifiedNofification(); } // If the topology pointers are complete and accurate, then the following // is not required. It's here because this SubD may be under construction // and we cannot assume the topology pointers are complete and accurate. const ON_SubDFacePtr* fptr = m_face2; for (unsigned int efi = 0; efi < 2; efi++) { if (2 == efi) { fptr = m_facex; if ( nullptr == fptr) break; } const ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr->m_ptr); if ( nullptr != face ) Internal_ClearFaceNeighborhoodCache(face); fptr++; } } void ON_SubDEdge::UnsetSectorCoefficientsForExperts() const { const_cast(this)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; const_cast(this)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; } 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::UnsetSectorWeight; else { e->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; e->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; } } } void ON_SubDFace::FaceModifiedNofification() const { Internal_ClearFaceNeighborhoodCache(this); // This is needed to clear cached information in the Catmull-Clark // ring that is not immediately adjacent to this face but whose values // this face affects. const ON_SubDEdgePtr* eptr = m_edge4; for (unsigned int efi = 0; efi < m_edge_count; efi++) { if (4 == efi) { eptr = m_edgex; if ( nullptr == eptr) break; } const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr != edge) { const ON_SubDFacePtr* fptr = edge->m_face2; for (unsigned short fei = 0; fei < edge->m_face_count; fei++) { if (2 == fei) { fptr = edge->m_facex; if (nullptr == fptr) break; } const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr->m_ptr); if (nullptr != f && f != this) Internal_ClearFaceNeighborhoodCache(f); } } eptr++; } } 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); ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_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)); } bool ON_SubDComponentBase::Internal_ControlNetFragmentFlag() const { return (0 != ON_SUBD_CACHE_CTRLNETFRAG_FLAG(m_saved_points_flags)); } void ON_SubDComponentBase::Internal_ClearSurfacePointFlag() const { ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(m_saved_points_flags); ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(m_saved_points_flags); } void ON_SubDComponentBase::Internal_ClearControlNetFragmentFlag() const { ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_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_SubDComponentBase::SubdivisionDisplacementIsNonzero() const { return (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) ? ( (0.0 != m_displacement_V[0] || 0.0 != m_displacement_V[1] || 0.0 != m_displacement_V[2]) && ON_IS_VALID(m_displacement_V[0]) && ON_IS_VALID(m_displacement_V[1]) && ON_IS_VALID(m_displacement_V[2]) ) : false; } bool ON_SubDComponentBase::SetSubdivisionDisplacement( const double displacement[3] ) { if ( nullptr == displacement || (0.0 == displacement[0] && 0.0 == displacement[1] && 0.0 == displacement[2]) ) { ClearSubdivisionDisplacement(); return true; } if ( ON_IsValid(displacement[0]) && ON_IsValid(displacement[1]) && ON_IsValid(displacement[2]) ) { m_saved_points_flags |= ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT; m_displacement_V[0] = displacement[0]; m_displacement_V[1] = displacement[1]; m_displacement_V[2] = displacement[2]; return true; } ClearSubdivisionDisplacement(); return ON_SUBD_RETURN_ERROR(false); } void ON_SubDComponentBase::ClearSubdivisionDisplacement() const { if (0 != (m_saved_points_flags & ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT)) { ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags); ON_SUBD_CACHE_CLEAR_DISPLACEMENT_FLAG(m_saved_points_flags); } } bool ON_SubDComponentBase::GetSubdivisionDisplacement( double displacement[3] ) const { const bool rc = (0 != (ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT & m_saved_points_flags)); if (nullptr != displacement) { if (rc) { displacement[0] = m_displacement_V[0]; displacement[1] = m_displacement_V[1]; displacement[2] = m_displacement_V[2]; } else { displacement[0] = 0.0; displacement[1] = 0.0; displacement[2] = 0.0; } } return rc; } const ON_3dVector ON_SubDComponentBase::SubdivisionDisplacement() const { ON_3dVector v(ON_3dVector::ZeroVector); return GetSubdivisionDisplacement(&v.x) ? v : 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_SetSavedControlNetFragmentFlag(bool bSavedControlNetFragmentFlag) const { if (bSavedControlNetFragmentFlag) m_saved_points_flags |= ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT; else Internal_ClearSurfacePointFlag(); } 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); double displacementV[3] = { 0 }; const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV); const double* edgeP[2] = { edge_vertex[0]->m_P, edge_vertex[1]->m_P }; 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_SubD::VertexTag::Smooth != edge_vertex[0]->m_vertex_tag) ? 0 : ((ON_SubD::VertexTag::Smooth != edge_vertex[1]->m_vertex_tag) ? 1 : ON_UNSET_UINT_INDEX); double edgePsum[3]; if ( ON_UNSET_UINT_INDEX == tagged_end || 0.5 == m_sector_coefficient[tagged_end] || (ON_SubD::EdgeTag::SmoothX == m_edge_tag) ) { // ignore edge weights edgePsum[0] = 0.375*(edgeP[0][0] + edgeP[1][0]); edgePsum[1] = 0.375*(edgeP[0][1] + edgeP[1][1]); edgePsum[2] = 0.375*(edgeP[0][2] + edgeP[1][2]); } else if (ON_SubD::VertexTag::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]; edgePsum[0] = 0.75*(w[0] * edgeP[0][0] + w[1] * edgeP[1][0]); edgePsum[1] = 0.75*(w[0] * edgeP[0][1] + w[1] * edgeP[1][1]); edgePsum[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] = edgePsum[0] + 0.0625*(facePsum[0][0] + facePsum[1][0]); subdivision_point[1] = edgePsum[1] + 0.0625*(facePsum[0][1] + facePsum[1][1]); subdivision_point[2] = edgePsum[2] + 0.0625*(facePsum[0][2] + facePsum[1][2]); if (bApplyDisplacement) { subdivision_point[0] += displacementV[0]; subdivision_point[1] += displacementV[1]; subdivision_point[2] += displacementV[2]; } return true; } if (3 == face_edge_count[0] && 3 == face_edge_count[1]) { // common case when both neighboring faces are triangles subdivision_point[0] = edgePsum[0] + 0.125*(facePsum[0][0] + facePsum[1][0]); subdivision_point[1] = edgePsum[1] + 0.125*(facePsum[0][1] + facePsum[1][1]); subdivision_point[2] = edgePsum[2] + 0.125*(facePsum[0][2] + facePsum[1][2]); if (bApplyDisplacement) { subdivision_point[0] += displacementV[0]; subdivision_point[1] += displacementV[1]; subdivision_point[2] += displacementV[2]; } return true; } // general formula works for all cases including face_edge_count[0] != face_count[2] 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] = edgePsum[0] + f0 * facePsum[0][0] + f1 * facePsum[1][0]; subdivision_point[1] = edgePsum[1] + f0 * facePsum[0][1] + f1 * facePsum[1][1]; subdivision_point[2] = edgePsum[2] + f0 * facePsum[0][2] + f1 * facePsum[1][2]; if (bApplyDisplacement) { subdivision_point[0] += displacementV[0]; subdivision_point[1] += displacementV[1]; subdivision_point[2] += displacementV[2]; } return true; } if ( IsCrease() ) { subdivision_point[0] = 0.5*(edgeP[0][0] + edgeP[1][0]); subdivision_point[1] = 0.5*(edgeP[0][1] + edgeP[1][1]); subdivision_point[2] = 0.5*(edgeP[0][2] + edgeP[1][2]); if (bApplyDisplacement) { subdivision_point[0] += displacementV[0]; subdivision_point[1] += displacementV[1]; subdivision_point[2] += displacementV[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_SubD::EdgeTag::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; } if (false == bUndefinedNormalIsPossible) { 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 (!(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 (!(fabs(y - 1.0) <= 1e-4)) return false; } return true; } void ON_SubDVertex::CopyFrom( const ON_SubDVertex* src, bool bCopyEdgeArray, bool bCopyFaceArray, bool bCopyLimitPointList ) { if (nullptr == src) src = &ON_SubDVertex::Empty; ClearSavedSubdivisionPoints(); CopyBaseFrom(src); 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_SubD::VertexTag::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_SubD::VertexTag::Smooth == vertex->m_vertex_tag || ON_SubD::VertexTag::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); double displacementV[3] = { 0 }; const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV); 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_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag) { return ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint(this, subdivision_point); } if (ON_SubD::VertexTag::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_SubD::EdgeTag::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; if (bApplyDisplacement) { subdivision_point[0] += displacementV[0]; subdivision_point[1] += displacementV[1]; subdivision_point[2] += displacementV[2]; } return true; } return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); } if (ON_SubD::VertexTag::Corner == m_vertex_tag) { vertexP = m_P; subdivision_point[0] = vertexP[0]; subdivision_point[1] = vertexP[1]; subdivision_point[2] = vertexP[2]; if (bApplyDisplacement) { subdivision_point[0] += displacementV[0]; subdivision_point[1] += displacementV[1]; subdivision_point[2] += displacementV[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); 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 const ON_ComponentStatus fstatus = f->m_status; const ON_SubDFace* candidate_face = f; 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_SubD::VertexTag::Smooth, level0_index, &P.x); AddVertexToLevel(center); radial_edges.SetCount(0); radial_edges.Reserve(e_count /2); for (unsigned fei = 0; fei < e_count; fei += 2) { ON_SubDEdge* r = AddEdge(ON_SubD::EdgeTag::Smooth, center, ON_SubDSectorType::UnsetSectorWeight, const_cast(eptrs[fei].RelativeVertex(1)), ON_SubDSectorType::UnsetSectorWeight); 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, 4, qbdry); candidate_face = nullptr; q->m_status = fstatus; } } level0.ClearRuntimeMarks(true, true, false); level0.ClearBoundingBox(); level0.ClearEvaluationCache(); level0.ClearEdgeFlags(); 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; // 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_SubD::VertexTag::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_SubD::EdgeTag::SmoothX is a smooth vertex.) const ON_SubD::VertexTag vertex_tag = ON_SubD::EdgeTag::Crease == e0->m_edge_tag ? ON_SubD::VertexTag::Crease : ON_SubD::VertexTag::Smooth; 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_SubD::EdgeTag edge_tag = e0->m_edge_tag; if (ON_SubD::EdgeTag::SmoothX == edge_tag && 2 == e0->m_face_count) { if ( nullptr != mid_vertex && ON_SubD::VertexTag::Smooth == mid_vertex->m_vertex_tag ) edge_tag = ON_SubD::EdgeTag::Smooth; } 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 unsigned int parent_face_id = f0->m_id; const unsigned int zero_face_id = (0 == f0->SubdivisionLevel()) ? parent_face_id : f0->m_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_SubD::VertexTag::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::CreaseSectorWeight(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_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the // case when it's value is "crease", the resulting edge end weight // will be 0.5 because the edge has two adjacent faces and "theta" // will be pi/2. // The resulting quad edge weight is 0.5 = 1/2 + 1/3*cos(pi/2). w = (ON_SubD::VertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_2facesector : 0.0; E1[3] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast(f0->m_subd_point1), 0.0, const_cast(E0[0]->m_subd_point1), w); 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_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the // case when it's value is "crease", the resulting edge end weight // will be zero because the edge has two adjacent faces and "theta" // will be pi/2. The resulting edge weight is 0.5. w = (ON_SubD::VertexTag::Crease == E0[1]->m_subd_point1->m_vertex_tag) ? w_2facesector : 0.0; E1[2] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast(f0->m_subd_point1), 0.0, const_cast(E0[1]->m_subd_point1), w); } 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->m_zero_face_id = zero_face_id; f1->m_parent_face_id = parent_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); UpdateAllTagsAndSectorCoefficients(true); 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::MergeEdges( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ) { if ( false == ON_SubD::EdgesCanBeMerged(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_SubD::EdgeTag merged_edge_tag = (e[0]->IsSmooth() || e[1]->IsSmooth()) ? ON_SubD::EdgeTag::Smooth : ON_SubD::EdgeTag::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_SubD::EdgeTag::Smooth == merged_edge_tag || false == bTagged[0] || false == bTagged[1]) { e[0]->m_edge_tag = (bTagged[0] && bTagged[1]) ? ON_SubD::EdgeTag::SmoothX : ON_SubD::EdgeTag::Smooth; if ( false == bTagged[0]) e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; 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::UnsetSectorWeight; if ( false == bTagged[1]) e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; 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::UnsetSectorWeight; } else { e[0]->m_edge_tag = ON_SubD::EdgeTag::Crease; e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; } ReturnEdge(e[1]); return eptr0; } ON_SubDEdgePtr ON_SubD::MergeEdges( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ) { ON_SubDimple* subdimple = SubDimple(false); return (nullptr != subdimple) ? subdimple->MergeEdges(eptr0, eptr1) : ON_SubDEdgePtr::Null; } static bool EdgesAreMergableTest( 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::EdgesCanBeMerged( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ) { ON_SubDEdgePtr eptr[2] = { eptr0,eptr1 }; return EdgesAreMergableTest(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 == EdgesAreMergableTest(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 ( EdgesAreMergableTest(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 != MergeEdges(eptr[0], eptr[1]).m_ptr) { ON_SUBD_ERROR("Bug in 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 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 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 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_id1) 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); faces_array.SetCount(face_id1 - face_id0); faces_array.Zero(); 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_SubD::VertexTag::Smooth, P); if (nullptr == v0) return ON_SUBD_RETURN_ERROR(nullptr); for (unsigned i = 0; i < edge_count; ++i, ++eptr) { if (nullptr == AddEdge(ON_SubD::EdgeTag::Smooth, v0, const_cast(edges[i].RelativeVertex(0)))) { 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_SubD::EdgeTag edge_tag = edge->m_edge_tag; ON_SubD::VertexTag vertex_tag; switch (edge->m_edge_tag) { case ON_SubD::EdgeTag::Smooth: vertex_tag = ON_SubD::VertexTag::Smooth; break; case ON_SubD::EdgeTag::Crease: vertex_tag = ON_SubD::VertexTag::Crease; break; case ON_SubD::EdgeTag::SmoothX: vertex_tag = ON_SubD::VertexTag::Smooth; edge_tag = ON_SubD::EdgeTag::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(vertex_tag, edge->SubdivisionLevel(), static_cast(vertex_location), 2, edge->m_face_count); if (nullptr == new_vertex) break; new_edge = AllocateEdge(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::IgnoredSectorWeight; new_edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; 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_SubD::EdgeTag::Smooth : ON_SubD::EdgeTag::SmoothX), v[0], ON_SubDSectorType::UnsetSectorWeight, v[1], ON_SubDSectorType::UnsetSectorWeight); 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_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); } 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; ON_AggregateComponentStatus 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; 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_SubD::VertexTag::Corner != v->m_vertex_tag) continue; // corner sector coefficients depend on the subtended angle of the sector's crease boundary. // All other sector coefficients are independent of vertex location. const_cast(edge)->m_sector_coefficient[evi] = ON_SubDSectorType::Create(edge, evi).SectorWeight(); } } } for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) { face->ClearSavedSubdivisionPoints(); } } 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_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); } 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); const ON_SubD::EdgeTag edge_tag0 = edge->m_edge_tag; if (bUnsetEdgeTagsOnly && ON_SubD::EdgeTag::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_SubD::EdgeTag::Crease; edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; } else { edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; const bool bBothVertexTagsAreSet = ON_SubD::VertexTag::Unset != edge->m_vertex[0]->m_vertex_tag && ON_SubD::VertexTag::Unset != edge->m_vertex[1]->m_vertex_tag ; 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::IgnoredSectorWeight; switch (edge_tag0) { case ON_SubD::EdgeTag::Unset: if (2 == tagged_end_index) { edge->m_edge_tag = ON_SubD::EdgeTag::SmoothX; } else if ( bBothVertexTagsAreSet ) { edge->m_edge_tag = ON_SubD::EdgeTag::Smooth; if (3 == tagged_end_index) { edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; } } break; case ON_SubD::EdgeTag::Smooth: if (2 == tagged_end_index) { edge->m_edge_tag = ON_SubD::EdgeTag::SmoothX; } else if (3 == tagged_end_index && bBothVertexTagsAreSet) { edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; } break; case ON_SubD::EdgeTag::Crease: edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; break; //case ON_SubD::EdgeTag::Sharp: // ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp is not valid in this version of opennurbs."); // break; case ON_SubD::EdgeTag::SmoothX: if ( 2 != tagged_end_index && bBothVertexTagsAreSet) edge->m_edge_tag = ON_SubD::EdgeTag::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_SubD::VertexTag vertex_tag0 = vertex->m_vertex_tag; if (bUnsetVertexTagsOnly && ON_SubD::VertexTag::Unset != vertex_tag0 ) { continue; } const unsigned int edge_count = vertex->m_edge_count; unsigned int creased_edge_count = 0; unsigned int sharp_edge_count = 0; for (unsigned int vei = 0; vei < edge_count; vei++) { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); if (nullptr == edge) { ON_SUBD_ERROR("nullptr vertex->m_edges[] values"); continue; } if ( ON_SubD::EdgeTag::Crease == edge->m_edge_tag || 2 != edge->m_face_count ) creased_edge_count++; else if (((ON_SubD::EdgeTag)3) == edge->m_edge_tag) // ON_SubD::EdgeTag::Sharp { ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp is not valid in this version of opennurbs."); sharp_edge_count++; } // NOTE: // edges tagged as ON_SubD::EdgeTag::Unset with two faces // ending at a vertex with 3 or more edges // will be tagged as smooth in subsequent passes // once this vertex is tagged as smooth. } ON_SubD::VertexTag vertex_tag1 = vertex_tag0; if ( (creased_edge_count+sharp_edge_count) >= 2 ) { if ( ON_SubD::VertexTag::Corner != vertex_tag0 ) vertex_tag1 = ON_SubD::VertexTag::Crease; } else if ( edge_count >= 2 ) { if ( 1 == creased_edge_count && 0 == sharp_edge_count ) vertex_tag1 = ON_SubD::VertexTag::Dart; else vertex_tag1 = ON_SubD::VertexTag::Smooth; } 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_SubD::EdgeTag etag = (2 == edge->TaggedEndIndex()) ? ON_SubD::EdgeTag::SmoothX : ON_SubD::EdgeTag::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 ) { ON_SubDLevel* level = ActiveLevelPointer(); 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_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); bool bDelete = (ON_ComponentStatus::AllSet == edge->m_status || (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); bool bDelete = (ON_ComponentStatus::AllSet == vertex->m_status || (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_face2; 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_SubD::EdgeTag::Crease; edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; } } // 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]; } if ( crease_count > 2 ) vertex->m_vertex_tag = ON_SubD::VertexTag::Corner; else if (false == bInteriorVertex || crease_count > 1) { if (false == vertex->IsCreaseOrCorner()) vertex->m_vertex_tag = ON_SubD::VertexTag::Crease; } 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++; } } 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); } } ChangeContentSerialNumber(); 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::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; 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 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( ON_SubD& subd ) { m_subd.ShareContentsFrom(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; 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); ++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->m_status.RuntimeMark()) continue; if (bTransform) { e->m_status.SetRuntimeMark(); for (unsigned int evi = 0; evi < 2; ++evi) { const ON_SubDVertex* v = e->m_vertex[evi]; if (nullptr != v && false == v->m_status.RuntimeMark()) { v->m_status.SetRuntimeMark(); const_cast(v)->Transform(false, *xform); ++marked_vertex_count; } } } else if (bExtrudeBoundaries && 1 == e->m_face_count && nullptr != e->m_face2[0].Face()) ++potential_isolated_edge_count; } break; case ON_SubDComponentPtr::Type::Face: { const ON_SubDFace* f = cptr_list[i].Face(); if (nullptr != f) { ++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) { 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); ++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->m_status.RuntimeMark()) 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->m_status.SetRuntimeMark(); for (unsigned int evi = 0; evi < 2; ++evi) { const ON_SubDVertex* v = e->m_vertex[evi]; if (nullptr != v && false == v->m_status.RuntimeMark()) { v->m_status.SetRuntimeMark(); ++marked_vertex_count; } } if (0 == --potential_isolated_edge_count) break; } } } } const_cast(subd).ChangeContentSerialNumberForExperts(); 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 (v_count > 0) { this->ClearEvaluationCache(); } 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; const ON_SubD::EdgeTag original_edge_tag = ON_SubD::EdgeTag::Unset; const ON_SubD::EdgeTag moved_edge_tag = ON_SubD::EdgeTag::Unset; return ExtrudeComponents(xform, ci_list, ci_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation, original_edge_tag, moved_edge_tag); } unsigned int ON_SubD::ExtrudeComponents( const ON_Xform& xform, const ON_COMPONENT_INDEX* ci_list, size_t ci_count, bool bExtrudeBoundaries, bool bPermitNonManifoldEdgeCreation, ON_SubD::EdgeTag original_edge_tag, ON_SubD::EdgeTag moved_edge_tag ) { 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, original_edge_tag, moved_edge_tag ); } class ON_Internal_ExtrudedVertexPair { public: ON_Internal_ExtrudedVertexPair() = default; ~ON_Internal_ExtrudedVertexPair() = default; ON_Internal_ExtrudedVertexPair(const ON_Internal_ExtrudedVertexPair&) = default; ON_Internal_ExtrudedVertexPair& operator=(const ON_Internal_ExtrudedVertexPair&) = default; static const ON_Internal_ExtrudedVertexPair Unset; // the marked vertex was in the original subd and will be moved. ON_SubDVertex* m_marked_vertex = nullptr; // the unmarked vertex replaces the marked vertex at the original location. ON_SubDVertex* m_unmarked_vertex = nullptr; // from new vertex to original vertex ON_SubDEdge* m_new_side = nullptr; static int CompareMarkedVertexId( const ON_Internal_ExtrudedVertexPair* lhs, const ON_Internal_ExtrudedVertexPair* rhs ); }; const ON_Internal_ExtrudedVertexPair ON_Internal_ExtrudedVertexPair::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Internal_ExtrudedVertexPair); class ON_Internal_ExtrudedSide { public: ON_Internal_ExtrudedSide() = default; ~ON_Internal_ExtrudedSide() = default; ON_Internal_ExtrudedSide(const ON_Internal_ExtrudedSide&) = default; ON_Internal_ExtrudedSide& operator=(const ON_Internal_ExtrudedSide&) = default; static const ON_Internal_ExtrudedSide Unset; bool m_bExtrudedBoundaryEdge = false; bool m_bHasMovedFace = false; // true if the edge touches a fae that will be moved. bool m_bHasStationaryFace = false; // true if the edge touches a face that will remain stationary. ON_SubD::EdgeTag m_original_marked_edge_tag = ON_SubD::EdgeTag::Unset; ON_SubD::EdgeTag MarkedEdgeTag() const { // this tag is calculated just before the side face is made if ( m_bExtrudedBoundaryEdge ) { const ON_SubDVertex* v0 = nullptr != m_marked_edge ? m_marked_edge->m_vertex[0] : nullptr; const ON_SubDVertex* v1 = nullptr != m_marked_edge ? m_marked_edge->m_vertex[1] : nullptr; if ( m_bHasMovedFace && false == m_bHasStationaryFace && nullptr != v0 && nullptr != v1 ) { return (v0->IsDartOrCreaseOrCorner() && v1->IsDartOrCreaseOrCorner()) ? ON_SubD::EdgeTag::SmoothX : ON_SubD::EdgeTag::Smooth; } } return m_original_marked_edge_tag; } ON_SubD::EdgeTag UnmarkedEdgeTag( const ON_SubDVertex* v0, const ON_SubDVertex* v1 ) const { // This tag is calculate before m_unmarked_edge is created. if ( m_bExtrudedBoundaryEdge && m_bHasStationaryFace && false == m_bHasMovedFace && nullptr != v0 && nullptr != v1 ) { return (v0->IsDartOrCreaseOrCorner() && v1->IsDartOrCreaseOrCorner()) ? ON_SubD::EdgeTag::SmoothX : ON_SubD::EdgeTag::Smooth; } return (ON_SubD::EdgeTag::Crease == m_original_marked_edge_tag) ? ON_SubD::EdgeTag::Crease : ON_SubD::EdgeTag::Unset ; } // the marked edge was in the original object and will be moved. ON_SubDEdge* m_marked_edge = nullptr; // the unmarked edge replaces the marked edge at the original location. ON_SubDEdge* m_unmarked_edge = nullptr; // start at new vertex and end at original vertex; ON_SubDEdge* m_new_side0 = nullptr; ON_SubDEdge* m_new_side1 = nullptr; ON_SubDFace* m_new_face = nullptr; }; const ON_Internal_ExtrudedSide ON_Internal_ExtrudedSide::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Internal_ExtrudedSide); int ON_Internal_ExtrudedVertexPair::CompareMarkedVertexId( const ON_Internal_ExtrudedVertexPair* lhs, const ON_Internal_ExtrudedVertexPair* rhs ) { const unsigned int lhs_id = lhs->m_marked_vertex->m_id; const unsigned int rhs_id = rhs->m_marked_vertex->m_id; if (lhs_id < rhs_id) return -1; if (lhs_id > rhs_id) return 1; return 0; } static ON_SubD::EdgeTag Internal_AdjustedEdgeTag(const ON_SubDEdge* edge) { if (nullptr == edge || nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1]) return ON_SubD::EdgeTag::Unset; // adjust moved edge tag because vertex tags can change when they get moved. const ON_SubD::VertexTag evtag[2] = { edge->m_vertex[0]->m_vertex_tag ,edge->m_vertex[1]->m_vertex_tag }; if (ON_SubD::VertexTag::Unset == evtag[0] || ON_SubD::VertexTag::Unset == evtag[1]) return ON_SubD::EdgeTag::Unset; if (ON_SubD::VertexTag::Smooth == evtag[0] || ON_SubD::VertexTag::Smooth == evtag[1]) return ON_SubD::EdgeTag::Smooth; const ON_SubD::EdgeTag etag = edge->m_edge_tag; if (ON_SubD::EdgeTag::Smooth == etag || ON_SubD::EdgeTag::SmoothX == etag ) return ON_SubD::EdgeTag::SmoothX; return etag; } static void Internal_SetEdgeVertices( ON_SubD& subd, ON_Internal_ExtrudedVertexPair& vertex_pair ) { // marked edges use the marked vertex. ON_SubDVertex* marked_vertex = vertex_pair.m_marked_vertex; ON_SubDVertex* unmarked_vertex = vertex_pair.m_unmarked_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 (vertex_pair.m_new_side == 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 (vertex_pair.m_new_side == 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++; } } } static ON_SubDFace* Internal_AddNewFace( ON_SubD& subd, ON_Internal_ExtrudedSide& side ) { // 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 = side.m_marked_edge; // will be moved ON_SubDEdge* unmarked_edge = side.m_unmarked_edge; // fixed 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 = (0 == edir) ? side.m_new_side0 : side.m_new_side1; ON_SubDEdge* side1 = (0 == edir) ? side.m_new_side1 : side.m_new_side0; ON_SubDEdgePtr new_face_eptr[4]; new_face_eptr[0] = ON_SubDEdgePtr::Create(side.m_marked_edge, 1-edir); new_face_eptr[1] = ON_SubDEdgePtr::Create(side0, 1); new_face_eptr[2] = ON_SubDEdgePtr::Create(side.m_unmarked_edge, edir); new_face_eptr[3] = ON_SubDEdgePtr::Create(side1, 0); const ON_SubD::EdgeTag marked_edge_tag = side.MarkedEdgeTag(); if (marked_edge_tag != marked_edge->m_edge_tag) marked_edge->m_edge_tag = marked_edge_tag; side.m_new_face = subd.AddFace(new_face_eptr, 4); if (nullptr != side.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 = side.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 (side.m_new_face != f[0] && side.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] == side.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) side.m_new_face->ReverseEdgeList(); } return side.m_new_face; } static ON_SubD::EdgeTag Internal_ConnectingEdgeTagAtVertex( bool bExtrudeBoundaries, const ON_SubDVertex* v, ON_SubD::VertexTag& moved_vertex_tag, ON_SubD::VertexTag& stationary_vertex_tag ) { moved_vertex_tag = ON_SubD::VertexTag::Unset; stationary_vertex_tag = ON_SubD::VertexTag::Unset; if (ON_SubD::VertexTag::Crease != v->m_vertex_tag && ON_SubD::VertexTag::Dart != v->m_vertex_tag) { ON_SUBD_ERROR("This function requires a crease or dart vertex as input."); return ON_SubD::EdgeTag::Unset; } const unsigned int vertex_edge_count = v->m_edge_count; // total_count = number of edges currently attached to v that are creases unsigned int total_count = 0; //////////////////////////////////////////////////////////////////////////////////////////////////// // // stationary, moved_to_crease, moved_to_smooth, and split are mutually exclusive sitations. // stationary_count = number of edges currently attached to v that are creases and remain in current locations unsigned int stationary_count = 0; // moved_to_crease_count = number of edges currently attached to v that are creases and move and remain creases unsigned int moved_to_crease_count = 0; // moved_to_crease_count = number of edges currently attached to v that are creases and move and become smooth unsigned int moved_to_smooth_count = 0; // split_count = number of edges currently attached to v that are creases and are between a moved and stationary face unsigned int split_count = 0; //////////////////////////////////////////////////////////////////////////////////////////////////// // // bdry, interior, wire, nonman are mutually exclusive sitations. // bdry_count = number of edges currently attached to v that are creases and have 1 face. unsigned int bdry_count = 0; // interior_count = number of edges currently attached to v that are creases and have 2 faces unsigned int interior_count = 0; // wire_count = number of edges currently attached to v that are creases and have 0 faces unsigned int wire_count = 0; // nonman_count = number of edges currently attached to v that are creases and have 3 or more faces unsigned int nonman_count = 0; //////////////////////////////////////////////////////////////////////////////////////////////////// // // tally up what is happening at each crease edge attached to vertex v // for (unsigned int vei = 0; vei < vertex_edge_count; vei++) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr); if (nullptr == e) continue; if (ON_SubD::EdgeTag::Crease != e->m_edge_tag) continue; const bool bBoundaryEdge = (1 == e->m_face_count) && (nullptr != e->m_face2[0].Face()); const bool bMovedBoundaryEdge = bBoundaryEdge && e->m_status.RuntimeMark(); //const bool bStationaryBoundaryEdge = bBoundaryEdge && false == e->m_status.RuntimeMark(); ++total_count; if (0 == e->m_face_count) ++wire_count; else if (1 == e->m_face_count) ++bdry_count; else if (2 == e->m_face_count) ++interior_count; else if (2 == e->m_face_count) ++nonman_count; unsigned int moved_face_count = 0; unsigned int stationary_face_count = 0; for (unsigned short evi = 0; evi < e->m_face_count; evi++) { const ON_SubDFace* f = e->Face(evi); if (nullptr == f) { ON_SUBD_ERROR("Edge has null face."); return ON_SubD::EdgeTag::Unset; } if (f->m_status.RuntimeMark()) ++moved_face_count; else ++stationary_face_count; } if (moved_face_count > 0 && stationary_face_count > 0) { // This is edge is between a moved face and a stationary face. // It will "split" into two edges ++split_count; } else if (moved_face_count > 0) { // This edge and all faced currently attached to it are moving. if (bExtrudeBoundaries && bMovedBoundaryEdge) ++moved_to_smooth_count; else ++moved_to_crease_count; } else if (stationary_face_count > 0) { // This edge is moving and all faced currently attached to it are stationary. // A new face will be attached to this edge if (bExtrudeBoundaries && bMovedBoundaryEdge) { // This boundary edge will move and a new face will be createed between it // and the face the edge is currently attached to. ++moved_to_crease_count; } else ++stationary_count; // this crease (interior or boundary) and its attached face or faces do not move } } if (total_count != wire_count + split_count + moved_to_smooth_count + moved_to_crease_count + stationary_count) { ON_SUBD_ERROR("Bug in counting code above or invalid topology near this vertex."); return ON_SubD::EdgeTag::Unset; } if (ON_SubD::VertexTag::Dart == v->m_vertex_tag) { if ( 1 == total_count && 1 == interior_count && 0 == bdry_count && 0 == wire_count && 0 == nonman_count ) { if (0 == split_count) { if (1 == moved_to_crease_count && 0 == moved_to_smooth_count && 0 == stationary_count) { // dart crease and both attached faces are moving moved_vertex_tag = ON_SubD::VertexTag::Dart; stationary_vertex_tag = ON_SubD::VertexTag::Smooth; return ON_SubD::EdgeTag::Smooth; } else if (0 == moved_to_crease_count && 0 == moved_to_smooth_count && 1 == stationary_count) { // dart crease and both attached faces are stationary moved_vertex_tag = ON_SubD::VertexTag::Smooth; stationary_vertex_tag = ON_SubD::VertexTag::Dart; return ON_SubD::EdgeTag::Smooth; } } else if (1 == split_count) { // Along the dart crease, one attached face is moving and the other is stationary. // The dart crease will get split into two darts and the new edge will be smooth. moved_vertex_tag = ON_SubD::VertexTag::Dart; stationary_vertex_tag = ON_SubD::VertexTag::Dart; return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision } } ON_SUBD_ERROR("Unexpected dart vertex edge tags - bug in counting code above or current tags or topology are not invalid."); return ON_SubD::EdgeTag::Unset; } ///////////////////////////////////////////////////////////////////////////////////////////// // // The rest of this function is for the case when ON_SubD::VertexTag::Crease == v->m_vertex_tag // if (1 == wire_count) { moved_vertex_tag = ON_SubD::VertexTag::Crease; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::Crease; } if (2 == wire_count) { moved_vertex_tag = ON_SubD::VertexTag::Crease; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision } if (0 != wire_count || 0 != nonman_count) { // If extrusions involving nonmanifold regions need to be supported, lots of changes are required. ON_SUBD_ERROR("Currently, non-manifold cases are not supported."); return ON_SubD::EdgeTag::Unset; } for(;;) { if ( ON_SubD::VertexTag::Crease == v->m_vertex_tag && 2 == (split_count + moved_to_crease_count + moved_to_smooth_count + stationary_count) ) { if (2 == interior_count && 0 == bdry_count) break; if (0 == interior_count && 2 == bdry_count) break; } ON_SUBD_ERROR("Unexpected crease vertex edge tags - bug in counting code above or current tags or topology are not invalid."); return ON_SubD::EdgeTag::Unset; } if (0 == interior_count && 2 == bdry_count) { // The vertex v is currently a boundary crease vertex and split_count always 0 in this case. // // Since 2 = split_count + stationary_count + moved_to_crease_count + moved_to_smooth_count, // stationary_count = 0,1,2 is used as the next "case reduction" filter. if (0 != split_count || 2 != (stationary_count + moved_to_crease_count + moved_to_smooth_count) ) { ON_SUBD_ERROR("Bug in boundary crease case counting code above."); return ON_SubD::EdgeTag::Unset; } if (0 == stationary_count) { // both attached crease edges are moving // 3 possibilities (moved_to_crease_count,moved_to_smooth_count) = (2,0), (0,2), or (1,1) if (2 == moved_to_crease_count ) { moved_vertex_tag = ON_SubD::VertexTag::Crease; stationary_vertex_tag = ON_SubD::VertexTag::Smooth; return ON_SubD::EdgeTag::Smooth; } if (2 == moved_to_smooth_count ) { // attached faces are moving and moved edges will be copied. // The moved edges will convert from crease to smooth and the stationary copies will become creases. // New faces will be created between the moved edge and the stationary copy. moved_vertex_tag = ON_SubD::VertexTag::Smooth; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::Smooth; } if ( 1 == moved_to_crease_count && 1 == moved_to_smooth_count ) { // new edge runs from moved crease to crease that replaces moved to smooth edge moved_vertex_tag = ON_SubD::VertexTag::Crease; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::Crease; } } else if (1 == stationary_count) { // 1 attached crease edge is moving and the other attached crease edge is stationary // 2 possibilities (moved_to_crease_count,moved_to_smooth_count) = (1,0) or (0,1) if (1 == moved_to_crease_count && 0 == moved_to_smooth_count) { // moved edge continues to be a boundary (and crease) edge. // The new edge connecting the stationary crease and the moved edge is a crease. moved_vertex_tag = ON_SubD::VertexTag::Crease; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::Crease; } if (0 == moved_to_crease_count && 1 == moved_to_smooth_count) { // moved crease becomes interior smooth edge // The new edge connecting the stationary crease and the moved edge is a crease. moved_vertex_tag = ON_SubD::VertexTag::Smooth; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::Smooth; } } else if (2 == stationary_count) { // neither attached crease edge is moving // 1 possibility (moved_to_crease_count,moved_to_smooth_count) = (0,0) moved_vertex_tag = ON_SubD::VertexTag::Smooth; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::Smooth; } } else if (2 == interior_count && 0 == bdry_count) { // The vertex v is currently an interior crease vertex and moved_to_smooth_count always 0 in this case. // (move_to_smooth_count>0 requires a boundary edge) // // Since 2 = split_count + stationary_count + moved_to_crease_count + moved_to_smooth_count, // split_count = 0,1,2 is used as the next "case reduction" filter. if (0 != moved_to_smooth_count || 2 != (split_count + stationary_count + moved_to_crease_count) ) { ON_SUBD_ERROR("Bug in interior crease case counting code above."); return ON_SubD::EdgeTag::Unset; } if (0 == split_count) { // stationary_count can be 0, 1 or 2. if (0 == stationary_count) { // both creases and all attached faces are moving moved_vertex_tag = ON_SubD::VertexTag::Crease; stationary_vertex_tag = ON_SubD::VertexTag::Smooth; return ON_SubD::EdgeTag::Smooth; } else if (1 == stationary_count) { // TODO } else if (2 == stationary_count) { // both creases and all attached faces are stationary moved_vertex_tag = ON_SubD::VertexTag::Smooth; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::Smooth; } } else if (1 == split_count) { // stationary_count can be 0 or 1 if (0 == stationary_count) { // 1 = moved_to_crease_count, 0 = moved_to_smooth_count. moved_vertex_tag = ON_SubD::VertexTag::Dart; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision } else if (1 == stationary_count) { // moved_to_crease_count = 0 and moved_to_smooth_count = 0 moved_vertex_tag = ON_SubD::VertexTag::Dart; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision } } else if (2 == split_count) { // stationary_count = 0, moved_to_crease_count = 0, and moved_to_smooth_count = 0 moved_vertex_tag = ON_SubD::VertexTag::Crease; stationary_vertex_tag = ON_SubD::VertexTag::Crease; return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision } } ON_SUBD_ERROR("Unexpected crease vertex edge tags - bug in counting code above or current tags or topology are not invalid."); return ON_SubD::EdgeTag::Unset; } static bool Internal_NonManifoldEdgeWillBeCreated( const ON_SubDVertex* v ) { if (nullptr == v || false == v->m_status.RuntimeMark()) return false; v->m_status.ClearRuntimeMark(); const unsigned int vertex_edge_count = v->m_edge_count; unsigned int boundary_count = 0; for (unsigned int vei = 0; vei < vertex_edge_count; vei++) { const ON_SubDEdge* e = v->Edge(vei); if (nullptr == e || 0 == e->m_face_count) continue; if (e->m_face_count > 2) return true; const ON_SubDFace* f = e->Face(0); const bool b0 = (nullptr != f) ? f->m_status.RuntimeMark() : false; f = (e->m_face_count > 1) ? e->Face(1) : nullptr; const bool b1 = (nullptr != f) ? f->m_status.RuntimeMark() : false; if (b0 == b1) continue; boundary_count++; if (boundary_count > 2) return true; } return false; } 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; const ON_SubD::EdgeTag original_edge_tag = ON_SubD::EdgeTag::Unset; const ON_SubD::EdgeTag moved_edge_tag = ON_SubD::EdgeTag::Unset; return ExtrudeComponents(xform, cptr_list, cptr_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation, original_edge_tag, moved_edge_tag); } unsigned int ON_SubD::ExtrudeComponents( const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, bool bExtrudeBoundaries, bool bPermitNonManifoldEdgeCreation, ON_SubD::EdgeTag original_edge_tag, ON_SubD::EdgeTag moved_edge_tag ) { if (nullptr == cptr_list || cptr_count <= 0) return 0; return Internal_ExtrudeComponents( xform, cptr_list, cptr_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation, original_edge_tag, moved_edge_tag ); } 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, ON_SubD::EdgeTag::Unset, ON_SubD::EdgeTag::Unset ); } unsigned int ON_SubD::Internal_ExtrudeComponents( const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, bool bExtrudeBoundaries, bool bPermitNonManifoldEdgeCreation, ON_SubD::EdgeTag original_edge_tag, ON_SubD::EdgeTag moved_edge_tag ) { 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.IsValidAndNotZeroAndNotIdentity() || (false == bHaveCptrList && false == bExtrudeAll) ) return 0; // The extrusion is initially calculated as if bot original_edge_tag and moved_edge_tag // are Unset. A post process (will be added later to) sets the tags as specified if (ON_SubD::EdgeTag::Crease != original_edge_tag && ON_SubD::EdgeTag::Smooth != original_edge_tag) original_edge_tag = ON_SubD::EdgeTag::Unset; if (ON_SubD::EdgeTag::Crease != moved_edge_tag && ON_SubD::EdgeTag::Smooth != moved_edge_tag) moved_edge_tag = ON_SubD::EdgeTag::Unset; ON_SubDComponentMarksClearAndRestore mark_guard(*this); // Marks very vertex touching a component in the cptr_list. // Skips applying the transform because it is the identity. 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 ); unsigned int moved_face_count = 0; // moved face count unsigned int new_face_count = 0; // number of new faces on moved boundary for (;;) { if (0 == marked_vertex_count) break; // 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); } moved_face_count = moved_faces.UnsignedCount(); // Mark edges on the boundary of the moved subset. // Set moved_edges[] = list of edges that are either // 1) between a moved face and a stationary face // 2) are a boundary edge to be extruded. ON_SimpleArray moved_edges(list_edge_count + list_vertex_count); ON_SimpleArray new_sides(64); ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { const bool bExtrudedBoundaryEdge = bExtrudeBoundaries && e->m_status.RuntimeMark() && 1 == e->m_face_count && ON_SubD::EdgeTag::Crease == e->m_edge_tag ; bool bExtrudeEdge = bExtrudedBoundaryEdge; e->m_status.ClearRuntimeMark(); bool bMarkedFace = false; bool bUnmarkedFace = false; const unsigned int edge_face_count = e->m_face_count; for (unsigned int efi = 0; efi < edge_face_count; efi++) { const ON_SubDFace* f = e->Face(efi); if (nullptr == f) continue; if (f->m_status.RuntimeMark()) bMarkedFace = true; // f is in the subset of moved faces. else bUnmarkedFace = true; // f is in the subset of stationary faces. if (bMarkedFace && bUnmarkedFace) { // e is on the boundary between the subset of moved faces and stationary faces. bExtrudeEdge = true; break; } } if (bExtrudeEdge) { if (false == bMarkedFace) moved_edges.Append(e); // e is not part of a moved face's boundary e->m_status.SetRuntimeMark(); ON_Internal_ExtrudedSide& newside = new_sides.AppendNew(); newside.m_marked_edge = const_cast(e); newside.m_bExtrudedBoundaryEdge = bExtrudedBoundaryEdge; newside.m_bHasMovedFace = bMarkedFace; newside.m_bHasStationaryFace = bUnmarkedFace; newside.m_original_marked_edge_tag = e->m_edge_tag; } } new_face_count = new_sides.UnsignedCount(); if (0 == new_face_count) { // no new faces will be made if (moved_face_count > 0) { // Every face is moving Transform(xform); } break; } if (false == bPermitNonManifoldEdgeCreation) { bool bNonManifoldEdgeWillBeCreated = false; for (unsigned int fi = 0; fi < moved_face_count; fi++) { const ON_SubDFace* f = moved_faces[fi]; const unsigned int face_vertex_count = f->m_edge_count; for (unsigned int fvi = 0; fvi < face_vertex_count; fvi++) { bNonManifoldEdgeWillBeCreated = Internal_NonManifoldEdgeWillBeCreated(f->Vertex(fvi)); if (bNonManifoldEdgeWillBeCreated) break; } if (bNonManifoldEdgeWillBeCreated) break; } if (bNonManifoldEdgeWillBeCreated) break; // not allowd to create non-manifold edges for (unsigned int ei = 0; ei < moved_edges.UnsignedCount(); ei++) { const ON_SubDEdge* e = moved_edges[ei]; for (unsigned int evi = 0; evi < 2; evi++) { bNonManifoldEdgeWillBeCreated = Internal_NonManifoldEdgeWillBeCreated(e->m_vertex[evi]); if (bNonManifoldEdgeWillBeCreated) break; } if (bNonManifoldEdgeWillBeCreated) break; } if (bNonManifoldEdgeWillBeCreated) break; // not allowd to create non-manifold edges } // clear vertex marks. ClearComponentMarks(true, false, false, nullptr); // Duplicate vertices that are on an edge between a marked and unmarked face // or a moved boundary edge ON_SimpleArray vertex_pairs(new_face_count+8); for (unsigned int i = 0; i < new_face_count; i++) { const ON_SubDEdge* e = new_sides[i].m_marked_edge; for (unsigned int evi = 0; evi < 2; evi++) { ON_SubDVertex* v = const_cast(e->m_vertex[evi]); if (nullptr == v) continue; if (v->m_status.RuntimeMark()) continue; // Mark this vertex. It will eventually get moved v->m_status.SetRuntimeMark(); ON_Internal_ExtrudedVertexPair& vpair = vertex_pairs.AppendNew(); vpair.m_marked_vertex = v; ON_SubD::VertexTag moved_vertex_tag = ON_SubD::VertexTag::Unset; // the original vertex gets moved ON_SubD::VertexTag stationary_vertex_tag = ON_SubD::VertexTag::Unset; // (this one get allocated) ON_SubD::EdgeTag connecting_edge_tag = ON_SubD::EdgeTag::Unset; // from stationary to moved vertex switch (v->m_vertex_tag) { case ON_SubD::VertexTag::Dart: connecting_edge_tag = Internal_ConnectingEdgeTagAtVertex(bExtrudeBoundaries, v, moved_vertex_tag, stationary_vertex_tag); break; case ON_SubD::VertexTag::Crease: connecting_edge_tag = Internal_ConnectingEdgeTagAtVertex(bExtrudeBoundaries, v, moved_vertex_tag, stationary_vertex_tag); break; case ON_SubD::VertexTag::Corner: moved_vertex_tag = v->m_vertex_tag; stationary_vertex_tag = v->m_vertex_tag; connecting_edge_tag = ON_SubD::EdgeTag::Crease; break; case ON_SubD::VertexTag::Smooth: moved_vertex_tag = ON_SubD::VertexTag::Smooth; stationary_vertex_tag = ON_SubD::VertexTag::Smooth; connecting_edge_tag = ON_SubD::EdgeTag::Smooth; break; default: moved_vertex_tag = ON_SubD::VertexTag::Unset; stationary_vertex_tag = ON_SubD::VertexTag::Unset; connecting_edge_tag = ON_SubD::EdgeTag::Unset; break; } // original vertex will eventually be moved. v->m_vertex_tag = moved_vertex_tag; // new vertex will become part of the stationary subset. // It is not marked. vpair.m_unmarked_vertex = this->AddVertex(stationary_vertex_tag, v->m_P); // transform the marked boundary vertex v->Transform(false, xform); // edge from stationary subset to moved subset. ON_SubDEdge* connecting_edge = this->AddEdge(connecting_edge_tag, vpair.m_unmarked_vertex, vpair.m_marked_vertex); vpair.m_new_side = connecting_edge; } } // sort vertex pairs so they can be located by the original vertex id. vertex_pairs.QuickSort(ON_Internal_ExtrudedVertexPair::CompareMarkedVertexId); // remove unmarked faces from marked vertices for (unsigned int i = 0; i < vertex_pairs.UnsignedCount(); i++) { ON_SubDVertex* marked_vertex = vertex_pairs[i].m_marked_vertex; ON_SubDVertex* unmarked_vertex = vertex_pairs[i].m_unmarked_vertex; const unsigned int vertex_face_count0 = marked_vertex->m_face_count; GrowVertexFaceArray(unmarked_vertex, vertex_face_count0); marked_vertex->m_face_count = 0; for (unsigned int vfi = 0; vfi < vertex_face_count0; vfi++) { const ON_SubDFace* f = marked_vertex->m_faces[vfi]; if (nullptr == f ) continue; ON_SubDVertex* v = (f->m_status.RuntimeMark()) ? marked_vertex : unmarked_vertex; v->m_faces[v->m_face_count] = f; v->m_face_count++; } } // build new side edges ON_Internal_ExtrudedVertexPair key[2]; for (unsigned int i = 0; i < new_face_count; i++) { const ON_SubDEdge* e = new_sides[i].m_marked_edge; for (unsigned int evi = 0; evi < 2; evi++) { key[evi].m_marked_vertex = const_cast(e->m_vertex[evi]); const int i0 = (nullptr != key[evi].m_marked_vertex) ? vertex_pairs.BinarySearch(&key[evi], ON_Internal_ExtrudedVertexPair::CompareMarkedVertexId) : -1; if (i0 < 0) { key[evi] = ON_Internal_ExtrudedVertexPair::Unset; continue; } key[evi] = vertex_pairs[i0]; } const ON_SubD::EdgeTag unmarked_edge_tag = new_sides[i].UnmarkedEdgeTag(key[0].m_unmarked_vertex, key[1].m_unmarked_vertex); new_sides[i].m_unmarked_edge = this->AddEdge(unmarked_edge_tag, key[0].m_unmarked_vertex, key[1].m_unmarked_vertex); new_sides[i].m_new_side0 = key[0].m_new_side; new_sides[i].m_new_side1 = key[1].m_new_side; } // Mark everything a moved face touches // including interior edges and vertices. // Transform any vertices that are not already marked. for (unsigned int i = 0; i < moved_face_count; i++) { const ON_SubDFace* f = moved_faces[i]; 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; e->m_status.SetRuntimeMark(); for (unsigned int evi = 0; evi < 2; evi++) { ON_SubDVertex* v = const_cast(e->m_vertex[evi]); if (nullptr !=v && false == v->m_status.RuntimeMark()) { v->Transform(false, xform); v->m_status.SetRuntimeMark(); } } } } // Mark everything vertex a moved edge touches // Transform any vertices that are not already marked. for (unsigned int i = 0; i < moved_edges.UnsignedCount(); i++) { const ON_SubDEdge* e = moved_edges[i]; const unsigned int edge_face_count = e->m_face_count; for (unsigned int efi = 0; efi < edge_face_count; ++efi) { e->m_status.SetRuntimeMark(); for (unsigned int evi = 0; evi < 2; evi++) { ON_SubDVertex* v = const_cast(e->m_vertex[evi]); if (nullptr !=v && false == v->m_status.RuntimeMark()) { v->Transform(false, xform); v->m_status.SetRuntimeMark(); } } } } // For the original boundary vertrex, move unmarked edges to use the new vertex. for (unsigned int i = 0; i < vertex_pairs.UnsignedCount(); i++) { Internal_SetEdgeVertices(*this, vertex_pairs[i]); } // build new side faces for (unsigned int i = 0; i < new_face_count; i++) { Internal_AddNewFace(*this, new_sides[i]); } // Any edge touching the vertices in vertex_pairs[] may need its tag adjusted // because those vertices may have had their tags adjusted. // Some edges get checked twice, but adding code to check just once // takes more time than checking twice. for (unsigned int i = 0; i < vertex_pairs.UnsignedCount(); i++) { for (unsigned int j = 0; j < 2; j++) { const ON_SubDVertex* v = (0 == j) ? vertex_pairs[i].m_marked_vertex : vertex_pairs[i].m_unmarked_vertex; if (nullptr == v || nullptr == v->m_edges) continue; for (unsigned int vei = 0; vei < v->m_edge_count; vei++) { ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr); if (nullptr == e) continue; const ON_SubD::EdgeTag adjusted_etag = Internal_AdjustedEdgeTag(e); if (ON_SubD::EdgeTag::Unset != adjusted_etag && e->m_edge_tag != adjusted_etag) e->m_edge_tag = adjusted_etag; } } } // remove cached subdivision calculations ClearEvaluationCache(); // Calculate vertex tags, edge tags, edge sector weights. this->UpdateAllTagsAndSectorCoefficients(true); break; } #if defined(ON_DEBUG) IsValid(); #endif // TODO - run through new_sides[] array and attempt to adjust tags //if (ON_SubD::EdgeTag::Unset != original_edge_tag || ON_SubD::EdgeTag::Unset != moved_edge_tag) //{ // //} // number of moved faces and new boundary faces return moved_face_count + new_face_count; } unsigned int ON_SubD::SetVertexTags( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, ON_SubD::VertexTag vertex_tag ) { if ( ON_SubD::VertexTag::Smooth != vertex_tag && ON_SubD::VertexTag::Crease != vertex_tag && ON_SubD::VertexTag::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_SubD::VertexTag vertex_tag ) { if ( ON_SubD::VertexTag::Smooth != vertex_tag && ON_SubD::VertexTag::Crease != vertex_tag && ON_SubD::VertexTag::Corner != vertex_tag ) return 0; if (cptr_count <= 0 || nullptr == cptr_list) return 0; ON_SubDComponentMarksClearAndRestore mark_guard(*this); const bool bNewVertexTagIsSmooth = (ON_SubD::VertexTag::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; if (ON_SubD::VertexTag::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_SubD::VertexTag::Smooth == vertex_tag) { if (ep.m_interior_edge_count != vertex->m_edge_count) continue; } else if (ON_SubD::VertexTag::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_SubD::VertexTag::Corner == vertex_tag) { const unsigned int crease_count = vertex->EdgeCount(ON_SubD::EdgeTag::Crease); if (2 == crease_count) 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_SubD::VertexTag::Crease != vertex_tag); // This for loop is used when new vertex_tag is ON_SubD::VertexTag::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_SubD::VertexTag 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. ON_SubD::EdgeTag edge_tag; for (;;) { if (2 != edge->m_face_count) { edge_tag = ON_SubD::EdgeTag::Crease; break; } if (2 == v[0]->m_edge_count && (ON_SubD::VertexTag::Crease == vtag[0] || ON_SubD::VertexTag::Corner == vtag[0])) { edge_tag = ON_SubD::EdgeTag::Crease; break; } if (2 == v[1]->m_edge_count && (ON_SubD::VertexTag::Crease == vtag[1] || ON_SubD::VertexTag::Corner == vtag[1])) { edge_tag = ON_SubD::EdgeTag::Crease; break; } if ( (ON_SubD::VertexTag::Crease == vtag[0] || ON_SubD::VertexTag::Corner == vtag[0] || ON_SubD::VertexTag::Dart == vtag[0]) && (ON_SubD::VertexTag::Crease == vtag[1] || ON_SubD::VertexTag::Corner == vtag[1] || ON_SubD::VertexTag::Dart == vtag[1]) ) { edge_tag = ON_SubD::EdgeTag::Crease; break; } edge_tag = ON_SubD::EdgeTag::Smooth; break; } if (ON_SubD::EdgeTag::Crease == edge_tag) edge->m_status.SetRuntimeMark(); } // 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(); 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_SubD::EdgeTag::Crease; edge->EdgeModifiedNofification(); } edge->m_status.SetRuntimeMark(); const ON_SubDVertex* other_vertex = edge->OtherEndVertex(vertex); other_vertex->m_status.SetRuntimeMark(); if ( false == bNewVertexTagIsSmooth && ON_SubD::EdgeTag::Crease == edge->m_edge_tag && other_vertex->IsSmooth() ) { const_cast(other_vertex)->m_vertex_tag = ON_SubD::VertexTag::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_SubD::EdgeTag edge_tag; if (v[0]->IsDartOrCreaseOrCorner() && v[1]->IsDartOrCreaseOrCorner()) edge_tag = ON_SubD::EdgeTag::Crease; else edge_tag = ON_SubD::EdgeTag::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_SubD::EdgeTag::Crease); ON_SubD::VertexTag vtag = vertex->m_vertex_tag; if (2 == crease_count) { if ( false == vertex->IsCreaseOrCorner() ) vtag = ON_SubD::VertexTag::Crease; } else if (1 == crease_count) vtag = ON_SubD::VertexTag::Dart; else if (crease_count > 2) vtag = ON_SubD::VertexTag::Corner; else vtag = ON_SubD::VertexTag::Smooth; if (vertex->m_vertex_tag == vtag) continue; const_cast(vertex)->m_vertex_tag = vtag; vertex->ClearSavedSubdivisionPoints(); } ClearEvaluationCache(); UpdateAllTagsAndSectorCoefficients(false); return changed_vertex_count; } unsigned int ON_SubD::SetEdgeTags( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, ON_SubD::EdgeTag edge_tag ) { if (ON_SubD::EdgeTag::Smooth != edge_tag && ON_SubD::EdgeTag::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_SubD::EdgeTag edge_tag ) { if (ON_SubD::EdgeTag::Smooth != edge_tag && ON_SubD::EdgeTag::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_SubD::EdgeTag::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_SubD::VertexTag::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_SubD::VertexTag::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_SubD::VertexTag vertex_tag; switch (crease_count) { case 0: vertex_tag = ON_SubD::VertexTag::Smooth; break; case 1: vertex_tag = ON_SubD::VertexTag::Dart; break; case 2: vertex_tag = ON_SubD::VertexTag::Crease; break; default: vertex_tag = ON_SubD::VertexTag::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_SubD::EdgeTag e_tag = e->m_edge_tag; if (nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) e_tag = ON_SubD::EdgeTag::Unset; else if (ON_SubD::EdgeTag::Smooth == e_tag && ON_SubD::VertexTag::Smooth != e->m_vertex[0]->m_vertex_tag && ON_SubD::VertexTag::Smooth != e->m_vertex[1]->m_vertex_tag) e_tag = ON_SubD::EdgeTag::Unset; 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_SubD::EdgeTag::Smooth; e->UnsetSectorCoefficientsForExperts(); for (int evi = 0; evi < 2; evi++) { if (nullptr == e->m_vertex[evi]) continue; const_cast(e->m_vertex[evi])->m_vertex_tag = ON_SubD::VertexTag::Unset; } ++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; if (ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type) { if (bIsSmooth) { if (ON_SubD::VertexTag::Smooth != v->m_vertex_tag) break; } else { if (ON_SubD::VertexTag::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()) { if ( ON_SubD::ChainType::EqualEdgeTag == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTag == 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; } 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_SubDRef subd_ref, const ON_SubDEdge* initial_edge ) { return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, ON_SubDEdgePtr::Create(initial_edge, 0)); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_SubDRef subd_ref, const ON_SimpleArray& initial_edge_chain ) { return BeginEdgeChain(subd_ref, initial_edge_chain.Array(), initial_edge_chain.UnsignedCount()); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( 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(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(subd_ref,eptr_chain); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_SubDRef subd_ref, ON_SubDEdgePtr eptr ) { return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, &eptr, 1); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_SubDRef subd_ref, const ON_SimpleArray& initial_edge_chain ) { return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, initial_edge_chain.Array(), initial_edge_chain.UnsignedCount() ); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_SubDRef subd_ref, const ON_SubDEdgePtr* initial_edge_chain, size_t edge_count ) { ClearEdgeChain(); 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(); } void ON_SubDEdgeChain::ClearEdgeChain() { m_edge_chain.SetCount(0); m_unique_tester.ClearList(); } unsigned int ON_SubDEdgeChain::EdgeCount() const { return m_edge_chain.UnsignedCount(); } 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; } 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); } 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_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 = (epl.m_index == epl1.m_index); const bool bReverseFinalChain = false == bClosedChain && unset_nbr1_index == epl.m_nbr_index && (0 == (epl.m_index % 2)); 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()); } if (bReverseFinalChain) chain.Reverse(); } 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; } if (chain.UnsignedCount() >= minimum_chain_length) { ++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". } return chain_count; } #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