// // Copyright (c) 1993-2022 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 . // //////////////////////////////////////////////////////////////// #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" bool ON_SubDFaceRegionBreakpoint( unsigned int level0_face_id, const class ON_SubDComponentRegionIndex& region_index ) { #if defined(ON_DEBUG) if ( 8 != level0_face_id ) { return false; } const unsigned short region_pattern[] = { 2, 2 }; // { 2, 2, 1 }; const unsigned short region_pattern_count = (unsigned short)(sizeof(region_pattern) / sizeof(region_pattern[0])); if (region_index.m_subdivision_count < region_pattern_count) return false; for (unsigned short i = 0; i < region_pattern_count; i++) { if (region_index.m_index[i] != region_pattern[i]) return false; } return true;// <- breakpoint here (or above) #else return false; #endif } bool ON_SubDComponentRegionBreakpoint(const ON_SubDComponentRegion* component_region) { #if defined(ON_DEBUG) if (nullptr != component_region) { switch (component_region->m_level0_component.ComponentType()) { case ON_SubDComponentPtr::Type::Face: return ON_SubDFaceRegionBreakpoint(component_region->m_level0_component_id, component_region->m_region_index); break; case ON_SubDComponentPtr::Type::Edge: break; case ON_SubDComponentPtr::Type::Vertex: break; default: break; } } #endif return false; } const ON_SubDComponentRegion ON_SubDComponentRegion::Create( const class ON_SubDFace* level0_face ) { ON_SubDComponentRegion r; r.m_level0_component = ON_SubDComponentPtr::Create(level0_face); r.m_level0_component_id = (nullptr != level0_face ? level0_face->m_id : 0); return r; } const ON_SubDComponentRegion ON_SubDComponentRegion::Create( unsigned int component_id, ON_SubDComponentPtr::Type component_type, bool bComponentMark ) { ON_SubDComponentRegion r; r.m_level0_component = ON_SubDComponentPtr::CreateNull(component_type, bComponentMark); r.m_level0_component_id = component_id; return r; } const ON_SubDComponentRegion ON_SubDComponentRegion::CreateSubdivisionRegion( ON_SubDComponentPtr::Type component_type, bool bComponentDirection, unsigned short subdivision_count, bool bAssignTransientId ) { ON_SubDComponentRegion r; r.m_region_index = ON_SubDComponentRegionIndex::Unset; r.m_region_index.m_subdivision_count = subdivision_count; r.m_level0_component = ON_SubDComponentPtr::CreateNull(component_type, bComponentDirection); if (bAssignTransientId) { r.m_level0_component_id = ON_SubDComponentRegion::NewTransientId(); } return r; } ////ON_SubDComponentRegion ON_SubDComponentRegion::Reverse() const ////{ //// ON_SubDComponentRegion r(*this); //// r.m_level0_component = r.m_level0_component.ToggleMark(); //// if (r.m_subdivision_count > 0) //// { //// const int c = (int)(sizeof(m_region_index) / sizeof(m_region_index[0])); //// int i = (int)(r.m_subdivision_count - 1); //// int j = 0; //// while (j < c && 0xFFFF == r.m_region_index[j]) //// j++; //// for ( /*empty init*/; j < i && j < c; ++j,--i) //// { //// if (i < c) //// { //// unsigned short x = r.m_region_index[i]; //// r.m_region_index[i] = r.m_region_index[j]; //// r.m_region_index[j] = x; //// } //// else //// { //// r.m_region_index[j] = 0; //// } //// } //// } //// return r; ////} //// ////ON_SubDComponentRegion ON_SubDComponentRegion::ReverseIfMarked() const ////{ //// return //// 0 != m_level0_component.ComponentMark() //// ? Reverse() //// : *this; ////} int ON_SubDComponentRegion::CompareTypeIdDirection( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) { if (lhs == rhs) return 0; if (nullptr == rhs) return 1; if (nullptr == lhs) return -1; int rc = ON_SubDComponentPtr::CompareType(&lhs->m_level0_component, &rhs->m_level0_component); if (0 != rc) return rc; if (lhs->m_level0_component_id < rhs->m_level0_component_id) return -1; if (lhs->m_level0_component_id > rhs->m_level0_component_id) return 1; rc = (0 != lhs->m_level0_component.ComponentDirection() ? (int)1 : (int)0) - (0 != lhs->m_level0_component.ComponentDirection() ? (int)1 : (int)0); if (0 != rc) return rc; return 0; } int ON_SubDComponentRegionIndex::Compare( const ON_SubDComponentRegionIndex* lhs, const ON_SubDComponentRegionIndex* rhs ) { if (lhs == rhs) return 0; if (nullptr == rhs) return 1; if (nullptr == lhs) return -1; if (lhs->m_subdivision_count < rhs->m_subdivision_count) return -1; if (lhs->m_subdivision_count > rhs->m_subdivision_count) return 1; return ON_SubDComponentRegionIndex::CompareMinimumSubregion(lhs, rhs); } int ON_SubDComponentRegionIndex::CompareMinimumSubregion( const ON_SubDComponentRegionIndex* lhs, const ON_SubDComponentRegionIndex* rhs ) { if (lhs == rhs) return 0; if (nullptr == lhs) return 1; if (nullptr == rhs) return -1; unsigned short subdivision_count0 = (lhs->m_subdivision_count < rhs->m_subdivision_count) ? lhs->m_subdivision_count : rhs->m_subdivision_count; if (subdivision_count0 > ON_SubDComponentRegionIndex::IndexCapacity) subdivision_count0 = ON_SubDComponentRegionIndex::IndexCapacity; for (unsigned short i = 0; i < subdivision_count0; i++) { if (lhs->m_index[i] < rhs->m_index[i]) return -1; if (lhs->m_index[i] > rhs->m_index[i]) return 1; } return 0; } int ON_SubDComponentRegion::CompareTypeIdDirectionMinimumSubregion( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) { if (lhs == rhs) return 0; const int rc = ON_SubDComponentRegion::CompareTypeIdDirection(lhs, rhs); if (0 != rc) return rc; return ON_SubDComponentRegionIndex::CompareMinimumSubregion( &lhs->m_region_index, &rhs->m_region_index); } int ON_SubDComponentRegion::CompareTypeIdDirectionSubregion( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) { if (lhs == rhs) return 0; int rc = ON_SubDComponentRegion::CompareTypeIdDirection(lhs, rhs); if (0 == rc) { rc = ON_SubDComponentRegionIndex::CompareMinimumSubregion(&lhs->m_region_index, &rhs->m_region_index); if (0 == rc) { if (lhs->m_region_index.m_subdivision_count < rhs->m_region_index.m_subdivision_count) rc = -1; else if (lhs->m_region_index.m_subdivision_count > rhs->m_region_index.m_subdivision_count) rc = 1; } } return rc; } int ON_SubDComponentRegion::Compare( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) { if (lhs == rhs) return 0; const int rc = ON_SubDComponentRegion::CompareTypeIdDirectionSubregion(lhs, rhs); if (0 != rc) return rc; if (lhs->m_level0_component.m_ptr < rhs->m_level0_component.m_ptr) return -1; if (lhs->m_level0_component.m_ptr > rhs->m_level0_component.m_ptr) return 1; return 0; } void ON_SubDComponentRegion::SetLevel0Component( ON_SubDComponentPtr component_ptr ) { const class ON_SubDComponentBase* component_base = component_ptr.ComponentBase(); if (nullptr != component_base) { m_level0_component = component_ptr; m_level0_component_id = component_base->m_id; } else { m_level0_component = ON_SubDComponentPtr::Null; m_level0_component_id = 0; } m_region_index = ON_SubDComponentRegionIndex::Zero; } void ON_SubDComponentRegion::SetLevel0Face( const ON_SubDFace* face ) { SetLevel0Component(ON_SubDComponentPtr::Create(face)); } void ON_SubDComponentRegion::SetLevel0EdgePtr( const ON_SubDEdgePtr edge_ptr ) { SetLevel0Component(ON_SubDComponentPtr::Create(edge_ptr)); } void ON_SubDComponentRegion::SetLevel0Vertex( const ON_SubDVertex* vertex ) { SetLevel0Component(ON_SubDComponentPtr::Create(vertex)); } void ON_SubDComponentRegionIndex::Push( unsigned int region_index ) { if ( region_index > 0xFFFFU ) region_index = 0xFFFFU; if ( m_subdivision_count < ON_SubDComponentRegionIndex::IndexCapacity ) m_index[m_subdivision_count] = (unsigned short)region_index; ++m_subdivision_count; } void ON_SubDComponentRegionIndex::Pop() { if (m_subdivision_count > 0) { m_subdivision_count--; if ( m_subdivision_count < ON_SubDComponentRegionIndex::IndexCapacity) m_index[m_subdivision_count] = 0; } } void ON_SubDComponentRegion::PushAdjusted( unsigned int region_index ) { if ( ON_SubDComponentPtr::Type::Edge == m_level0_component.ComponentType() && 0 != m_level0_component.ComponentDirection() && region_index <= 1 ) { region_index = 1 - region_index; } PushAbsolute(region_index); } void ON_SubDComponentRegion::PushAbsolute( unsigned int region_index ) { m_region_index.Push(region_index); ON_SubDComponentRegionBreakpoint(this); } void ON_SubDComponentRegion::Pop() { m_region_index.Pop(); } unsigned short ON_SubDComponentRegion::SubdivisionCount() const { return m_region_index.m_subdivision_count; } unsigned short ON_SubDComponentRegionIndex::Index( unsigned short i ) const { return (i < m_subdivision_count && i < ON_SubDComponentRegionIndex::IndexCapacity) ? m_index[i] : 0xFFFF; } const ON_SubDFace* ON_SubDFaceRegion::Level0Face() const { return this->m_face_region.m_level0_component.Face(); } unsigned int ON_SubDFaceRegion::CornerIndexFromVertexId( unsigned int vertex_id ) const { unsigned corner_index = ON_UNSET_UINT_INDEX; if (vertex_id > 0 && vertex_id < ON_UNSET_UINT_INDEX) { for (unsigned i = 0; i < 4; ++i) { if (vertex_id == this->m_vertex_id[i]) { if (ON_UNSET_UINT_INDEX == corner_index) corner_index = i; else return ON_UNSET_UINT_INDEX; } } } return corner_index; } void ON_SubDFaceRegion::Push(unsigned int quadrant_index) { m_face_region.PushAbsolute(quadrant_index); if (quadrant_index >= 0 && quadrant_index < 4) { m_edge_region[quadrant_index].PushAdjusted(0); // 1st half of this edge relative to face's orientation (adjusted to edge's orientation) m_edge_region[(quadrant_index + 1) % 4] = ON_SubDComponentRegion::CreateSubdivisionRegion(ON_SubDComponentPtr::Type::Edge, true, m_edge_region[quadrant_index].SubdivisionCount(), false); m_edge_region[(quadrant_index + 2) % 4] = ON_SubDComponentRegion::CreateSubdivisionRegion(ON_SubDComponentPtr::Type::Edge, false, m_edge_region[quadrant_index].SubdivisionCount(), false); m_edge_region[(quadrant_index + 3) % 4].PushAdjusted(1); // 2nd half of this edge relative to face's orientation (adjusted to edge's orientation) } const int surviving_vi = ((4 != m_level0_edge_count) && (1 == m_face_region.SubdivisionCount())) ? 2 : quadrant_index; m_vertex_id[(surviving_vi+1)%4] = 0; m_vertex_id[(surviving_vi+2)%4] = 0; m_vertex_id[(surviving_vi+3)%4] = 0; m_sector_id[(surviving_vi + 1) % 4] = ON_SubDSectorId::Zero; m_sector_id[(surviving_vi + 2) % 4] = ON_SubDSectorId::Zero; m_sector_id[(surviving_vi + 3) % 4] = ON_SubDSectorId::Zero; } bool ON_SubDComponentRegion::IsEmptyRegion() const { return ON_SubDComponentPtr::Type::Unset == m_level0_component.ComponentType() && m_level0_component.IsNull() && 0 == m_level0_component_id && 0 == SubdivisionCount(); } bool ON_SubDFaceRegion::IsValid( bool bSilentError ) const { if (m_face_region.IsEmptyRegion()) { for (int ei = 0; ei < 4; ei++) { if (false == m_edge_region[ei].IsEmptyRegion()) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region is empty and m_edge_region[] is not empty."); return false; } } for (int vi = 0; vi < 4; vi++) { if (0 != m_vertex_id[vi]) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region is empty and m_vertex_id[] is not zero."); return false; } if (false == m_sector_id[vi].IsZero()) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region is empty and m_sector_id[] is not zero."); return false; } } return true; } const ON_SubDComponentPtr::Type face_type = m_face_region.m_level0_component.ComponentType(); if (ON_SubDComponentPtr::Type::Face != face_type) { if (false == bSilentError) ON_SUBD_ERROR("Invalid m_face_region."); return false; } if (false == m_face_region.IsPersistentId()) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region.IsPersistentId() is false"); return false; } const ON_SubDFace* face = m_face_region.m_level0_component.Face(); if (nullptr == face ) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region.m_level0_component.Face() is nullptr."); return false; } if (face->m_id != m_face_region.m_level0_component_id) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected value for m_face_region.m_level0_component_id"); return false; } const unsigned int edge_count = (nullptr != face) ? face->EdgeCount() : 0; const bool bIsQuad = (4 == edge_count); if (false == bIsQuad) { if (0 == m_face_region.SubdivisionCount()) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region is not a quad and 0 = m_subdivision_count."); return false; } if (((unsigned int)m_face_region.m_region_index.m_index[0]) >= edge_count) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected value in face_region.m_region_index[0]."); return false; } } const unsigned short face_region_subdivision_count = m_face_region.SubdivisionCount(); bool bPersistentVertex[4] = { bIsQuad, bIsQuad, true, bIsQuad }; bool bPersistentEdge[4] = { bIsQuad, true, true, bIsQuad }; for (unsigned short i = bIsQuad?0:1; i < face_region_subdivision_count && i < ON_SubDComponentRegionIndex::IndexCapacity; i++) { const unsigned short r = m_face_region.m_region_index.m_index[i]; if (r >= 4) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected value in face_region.m_region_index[]."); return false; } bPersistentVertex[(r+1)%4] = false; bPersistentVertex[(r+2)%4] = false; bPersistentVertex[(r+3)%4] = false; bPersistentEdge[(r+1)%4] = false; bPersistentEdge[(r+2)%4] = false; if (false == bPersistentVertex[r] && false == bPersistentEdge[r] && false == bPersistentEdge[(r + 3) % 4] ) break; } unsigned int fei[4] = { ON_UNSET_UINT_INDEX,ON_UNSET_UINT_INDEX,ON_UNSET_UINT_INDEX,ON_UNSET_UINT_INDEX }; const ON_SubDVertex* fv[4] = {}; for (int ei = 0; ei < 4; ei++) { const bool bEmptyEdge = m_edge_region[ei].IsEmptyRegion(); if (bEmptyEdge) { if ( bPersistentEdge[ei]) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected empty edge in m_edge_region[]."); return false; } continue; } const ON_SubDComponentPtr::Type edge_type = m_edge_region[ei].m_level0_component.ComponentType(); if (ON_SubDComponentPtr::Type::Edge != edge_type) { if (false == bSilentError) ON_SUBD_ERROR("Invalid m_face_region."); return false; } if ( m_edge_region[ei].SubdivisionCount() != m_face_region.SubdivisionCount() ) { if (false == bSilentError) ON_SUBD_ERROR("m_edge_region[].m_subdivision_count != m_face_region.m_subdivision_count."); return false; } if (bPersistentEdge[ei]) { if (false == m_edge_region[ei].IsPersistentId()) { if (false == bSilentError) ON_SUBD_ERROR("m_edge_region[] missing a persistent edge id."); return false; } const ON_SubDEdge* edge = m_edge_region[ei].m_level0_component.Edge(); if (nullptr == edge) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected value for m_edge_region[].m_level0_component.Edge()"); return false; } if (edge->m_id != m_edge_region[ei].m_level0_component_id) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected value for m_edge_region[].m_level0_component_id"); return false; } fei[ei] = face->EdgeArrayIndex(edge); if (ON_UNSET_UINT_INDEX == fei[ei]) { if (false == bSilentError) ON_SUBD_ERROR("m_edge_region[].m_level0_component.Edge() not in face."); return false; } fv[ei] = face->Vertex(ei); if ( nullptr == fv[ei] ) { if (false == bSilentError) ON_SUBD_ERROR("m_edge_region[].m_level0_component.Edge()->Vertex() is missing."); return false; } } else { if ( false == m_edge_region[ei].IsTransientId() ) { if (false == bSilentError) ON_SUBD_ERROR("m_edge_region[] missing a transient edge id."); return false; } } } for (unsigned int vi = 0; vi < 4; vi++) { if (bPersistentVertex[vi]) { if (false == ON_SubDComponentRegion::IsPersistentId(m_vertex_id[vi])) { if (false == bSilentError) ON_SUBD_ERROR("m_vertex_id[] missing a persistent vertex id."); return false; } if (face_region_subdivision_count <= 1) { unsigned int fvi = ON_UNSET_UINT_INDEX; if (0 == face_region_subdivision_count) fvi = vi; else if (1 == face_region_subdivision_count) fvi = m_face_region.m_region_index.m_index[0]; const ON_SubDVertex* v = face->Vertex(fvi); if (nullptr == v) { if (false == bSilentError) ON_SUBD_ERROR("face->Vertex() is nullptr."); return false; } if (v->m_id != m_vertex_id[vi]) { if (false == bSilentError) ON_SUBD_ERROR("m_vertex_id[] and face->Vertex()->m_id are different."); return false; } } const unsigned sector_vertex_id = m_sector_id[vi].VertexId(); if (0 != sector_vertex_id) { if (sector_vertex_id != m_vertex_id[vi]) { if (false == bSilentError) ON_SUBD_ERROR("m_sector_id[].VertexId() is incorrect."); return false; } if (false == m_sector_id[vi].IsSet()) { if (false == bSilentError) ON_SUBD_ERROR("m_sector_id[] is missing face information."); return false; } } else { if (false == m_sector_id[vi].IsZero()) { if (false == bSilentError) ON_SUBD_ERROR("m_sector_id[] is missing vertex information."); return false; } } } else if ( 0 != m_vertex_id[vi] ) { if (false == ON_SubDComponentRegion::IsTransientId(m_vertex_id[vi])) { if (false == bSilentError) ON_SUBD_ERROR("m_vertex_id[] missing a transient vertex id."); return false; } if (false == m_sector_id[vi].IsZero()) { if (false == bSilentError) ON_SUBD_ERROR("Transient vertex has an nonzero m_sector_id[]."); return false; } } } return true; } bool ON_SubDComponentRegion::IsTransientId() const { return ON_SubDComponentRegion::IsTransientId(m_level0_component_id); } bool ON_SubDComponentRegion::IsPersistentId() const { return ON_SubDComponentRegion::IsPersistentId(m_level0_component_id); } static unsigned int Internal_TransientIdHelper( bool bReset ) { static std::atomic src(0); if (bReset) { src = 0; return 0; } unsigned int transient_id = ++src; if (0 != (ON_SubDComponentRegion::TransientIdBit & transient_id)) { // This should be extremely rare. // Calculations that use transient_id are time consuming, src should be small // after the first thread sets it back to zero. static ON_SleepLock global_resource_lock; ON_SleepLockGuard guard(global_resource_lock); if (0 != (ON_SubDComponentRegion::TransientIdBit & src)) src = 0; transient_id = ++src; } transient_id |= ON_SubDComponentRegion::TransientIdBit; return transient_id; } void ON_SubDComponentRegion::ResetTransientId() { Internal_TransientIdHelper(true); } const unsigned int ON_SubDComponentRegion::NewTransientId() { return Internal_TransientIdHelper(false); } bool ON_SubDComponentRegion::IsPersistentId(unsigned int id) { return (0 != id) && (0 == (ON_SubDComponentRegion::TransientIdBit & id)); } unsigned int ON_SubDComponentRegion::TransientId(unsigned int id) { return (0 != (ON_SubDComponentRegion::TransientIdBit & id)) ? ((~ON_SubDComponentRegion::TransientIdBit) & id) : 0; } bool ON_SubDComponentRegion::IsTransientId(unsigned int id) { return 0 != (ON_SubDComponentRegion::TransientIdBit & id) && 0 != ((~ON_SubDComponentRegion::TransientIdBit) & id); } static wchar_t* Internal_AppendUnsigned( //wchar_t prefix1, //wchar_t prefix2, unsigned int i, wchar_t* s, wchar_t* s1 ) { //if (0 != prefix1 && s < s1) // *s++ = prefix1; //if (0 != prefix2 && s < s1) // *s++ = prefix2; const bool bTransientId = (0 != (ON_SubDComponentRegion::TransientIdBit & i) ); if (bTransientId) { if (s < s1) *s++ = '<'; i &= ~ON_SubDComponentRegion::TransientIdBit; } wchar_t buffer[64]; wchar_t* sdigit = buffer; wchar_t* sdigit1 = sdigit + (sizeof(buffer)/sizeof(buffer[0])); for ( *sdigit++ = 0; sdigit < sdigit1; sdigit++ ) { *sdigit = (wchar_t)('0' + (i%10)); i /= 10; if (0 == i) { while ( s < s1 && 0 != (*s = *sdigit--) ) s++; break; } } if (bTransientId) { if (s < s1) *s++ = '>'; } if (s <= s1) *s = 0; return s; } wchar_t* ON_SubDComponentPtr::ToString( wchar_t* s, size_t s_capacity ) const { if (s_capacity <= 0 || nullptr == s) return nullptr; *s = 0; wchar_t* s1 = s + (s_capacity - 1); *s1 = 0; if (s < s1) { if (0 == m_ptr) { if (s + 7 < s1) { *s++ = 'N'; *s++ = 'u'; *s++ = 'l'; *s++ = 'l'; *s++ = 'P'; *s++ = 't'; *s++ = 'r'; } } else { wchar_t c; switch (ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: c = 'v'; break; case ON_SubDComponentPtr::Type::Edge: if ( s+2 < s1 ) *s++ = (ComponentDirection()) ? '-' : '+'; c = 'e'; break; case ON_SubDComponentPtr::Type::Face: c = 'f'; break; case ON_SubDComponentPtr::Type::Unset: c = 0; break; default: c = 0; break; } if (0 == c) { *s++ = '?'; } else { *s++ = c; if (IsNull() && s + 6 < s1) { *s++ = '['; *s++ = 'n'; *s++ = 'u'; *s++ = 'l'; *s++ = 'l'; *s++ = ']'; } } } } if (nullptr != s && s <= s1) *s = 0; return s; }; const ON_wString ON_SubDComponentRegionIndex::ToString() const { wchar_t buffer[32]; if (nullptr != ToString(buffer, sizeof(buffer) / sizeof(buffer[0]))) return ON_wString(buffer); return ON_wString::EmptyString; } wchar_t* ON_SubDComponentRegionIndex::ToString( wchar_t* s, size_t s_capacity ) const { if (s_capacity <= 0 || nullptr == s) return nullptr; *s = 0; wchar_t* s1 = s + (s_capacity - 1); *s1 = 0; if (s < s1) { for (unsigned short i = 0; i < m_subdivision_count && nullptr != s && s < s1; i++) { if (s < s1) *s++ = '.'; if (i >= ON_SubDComponentRegionIndex::IndexCapacity) { // more subdivision levels that m_region_index[] can record. if (s < s1) *s++ = '_'; break; } if (0xFFFF == m_index[i]) { // This is component was added during a subdivision // and did not exist at level i. if (s < s1) *s++ = 'x'; } else { // portion of component subdivided at level i s = Internal_AppendUnsigned(m_index[i], s, s1); } } } if (nullptr != s && s <= s1) *s = 0; return s; } const ON_wString ON_SubDComponentPtr::ToString() const { wchar_t buffer[32]; if (nullptr != ToString(buffer, sizeof(buffer) / sizeof(buffer[0]))) return ON_wString(buffer); return ON_wString::EmptyString; } wchar_t* ON_SubDComponentRegion::ToString( wchar_t* s, size_t s_capacity ) const { if (s_capacity <= 0 || nullptr == s) return nullptr; *s = 0; wchar_t* s1 = s + (s_capacity - 1); *s1 = 0; if (s < s1) { s = m_level0_component.ToString(s, s_capacity); if (nullptr != s && s < s1) s = Internal_AppendUnsigned(m_level0_component_id, s, s1); } if (nullptr != s && s < s1) s = m_region_index.ToString(s, 1 + (s1 - s)); if (nullptr != s && s <= s1) *s = 0; return s; } const ON_wString ON_SubDComponentRegion::ToString() const { wchar_t buffer[128]; if (nullptr != ToString(buffer, sizeof(buffer) / sizeof(buffer[0]))) return ON_wString(buffer); return ON_wString::EmptyString; } const ON_wString ON_SubDSectorId::ToString(bool bVerbose) const { if (IsZero()) return ON_wString(bVerbose ? L"ON_SubDSectorId::Zero" : L"Zero"); if (m_sector_face_count > 0xFFFFU) return ON_wString(bVerbose ? L"ON_SubDSectorId::Invalid" : L"Invalid"); wchar_t s_buffer[64]; if (nullptr != ToString(s_buffer, sizeof(s_buffer) / sizeof(s_buffer[0]))) { return bVerbose ? ON_wString::FormatToString(L"ON_SubDSectorId %ls", s_buffer) : ON_wString(s_buffer); } return ON_wString::EmptyString; } wchar_t* ON_SubDSectorId::ToString( wchar_t* s, size_t s_capacity ) const { if (s_capacity <= 0 || nullptr == s) return nullptr; *s = 0; wchar_t* s1 = s + (s_capacity - 1); *s1 = 0; if (s < s1) { if (IsZero()) *s++ = '0'; else if (m_sector_face_count > 0xFFFFU) *s++ = 'X'; else if ( s+6 < s1 ) { *s++ = 'v'; if (nullptr != s && s < s1) { s = Internal_AppendUnsigned(this->m_vertex_id, s, s1); if (nullptr != s && s + 5 < s1) { *s++ = '.'; *s++ = 'f'; s = Internal_AppendUnsigned(this->m_minimum_face_id, s, s1); if (nullptr != s && s + 2 < s1) { *s++ = 'x'; s = Internal_AppendUnsigned(this->m_sector_face_count, s, s1); } } } } } if (nullptr != s && s <= s1) *s = 0; return s; } ON__UINT32 ON_SubDComponentRegionIndex::ToCompressedRegionIndex() const { return ON_SubDComponentRegionIndex::ToCompressedRegionIndex( m_subdivision_count, m_index); } const ON_SubDComponentRegionIndex ON_SubDComponentRegionIndex::FromCompressedRegionIndex( ON__UINT32 compressed_region_index ) { ON_SubDComponentRegionIndex ri; ON_SubDComponentRegionIndex::FromCompressedRegionIndex(compressed_region_index, &ri.m_subdivision_count, ri.m_index); return ri; } ON__UINT32 ON_SubDComponentRegionIndex::ToCompressedRegionIndex( unsigned short subdivision_count, const unsigned short* region_index ) { ON__UINT32 rc = (subdivision_count >= 255) ? 255 : (ON__UINT32)subdivision_count; rc <<= 24; if (nullptr != region_index && subdivision_count > 0) { ON__UINT32 idx = (region_index[0] >= 255) ? 255 : (ON__UINT32)region_index[0]; idx <<= 16; ON__UINT32 shift = 14; for (unsigned short i = 1; i < subdivision_count && i < ON_SubDComponentRegionIndex::IndexCapacity && shift <= 14; i++) { ON__UINT32 bits = (ON__UINT32)region_index[i]; if (bits > 3) bits = 3; idx |= (bits << shift); shift -= 2; } rc |= idx; } return rc; //ON__UINT32 shift = 27; //ON__UINT32 rc = (ON__UINT32)subdivision_count; //rc <<= shift; // //const unsigned short count // = (subdivision_count <= ON_SubDComponentRegion::region_index_capacity) // ? subdivision_count // : ON_SubDComponentRegion::region_index_capacity; //for (unsigned short i = 0; i < count; i++) //{ // shift -= 3; // ON__UINT32 three_bits = (ON__UINT32)(region_index[i] % 0x07); // if (0 != three_bits) // rc |= (three_bits << shift); //} //return rc; } void ON_SubDComponentRegionIndex::FromCompressedRegionIndex( ON__UINT32 compressed_region_index, unsigned short* subdivision_count, unsigned short* region_index ) { const ON__UINT32 count = (compressed_region_index >> 24); if (nullptr != subdivision_count) *subdivision_count = (unsigned short)count; if (nullptr != region_index) { region_index[0] = (unsigned short)((compressed_region_index & 0x00FF0000) >> 16); for (unsigned short i = 1; i < ON_SubDComponentRegionIndex::IndexCapacity; i++) { region_index[i] = (unsigned short)((compressed_region_index & 0x0000C000) >> 14); compressed_region_index <<= 2; } } //ON__UINT32 shift = 27; //ON__UINT32 count = (region32 >> shift); //if (nullptr != subdivision_count) // *subdivision_count = (unsigned short)count; //if (nullptr != region_index) //{ // for (unsigned short i = 0; i < ON_SubDComponentRegion::region_index_capacity; i++) // region_index[i] = 0; // unsigned int idx = region32 << (32-shift); // for (unsigned short i = 0; 0 != idx && i < ON_SubDComponentRegion::region_index_capacity; i++) // { // ON__UINT32 three_bits = (idx & 0xE0000000U); // three_bits >>= 29; // region_index[i] = (unsigned short)three_bits; // idx <<= 3; // } //} } wchar_t* ON_SubDFaceRegion::ToString( wchar_t* s, size_t s_capacity ) const { if (s_capacity <= 0 || nullptr == s) return nullptr; wchar_t* s1 = s + s_capacity-1; *s1 = 0; s = m_face_region.ToString(s, s_capacity); if (nullptr != s && s+4 < s1) { for (unsigned int i = 0; i < 4 && nullptr != s && s + 4 < s1; i++) { *s++ = ON_wString::Space; *s++ = (0 == i) ? '(' : ','; if ( ON_SubDComponentPtr::Type::Edge == m_edge_region[i].m_level0_component.ComponentType() ) { s = m_edge_region[i].ToString(s, s1 - s); } else { *s++ = 'e'; *s++ = '?'; } } if (nullptr != s && s < s1) *s++ = ')'; } if (nullptr != s && s + 4 < s1) { for (unsigned int i = 0; i < 4 && nullptr != s && s + 4 < s1; i++) { *s++ = ON_wString::Space; *s++ = (0 == i) ? '(' : ','; if (0 != m_vertex_id[i]) { *s++ = 'v'; s = Internal_AppendUnsigned(m_vertex_id[i], s, s1); } else { *s++ = '0'; } } if (nullptr != s && s < s1) *s++ = ')'; } if ( false == m_sector_id[0].IsZero() || false == m_sector_id[1].IsZero() || false == m_sector_id[2].IsZero() || false == m_sector_id[3].IsZero() ) { if (nullptr != s && s + 4 < s1) { for (unsigned int i = 0; i < 4 && nullptr != s && s + 4 < s1; i++) { *s++ = ON_wString::Space; *s++ = (0 == i) ? '(' : ','; s = m_sector_id[i].ToString(s, s1 - s); } if (nullptr != s && s < s1) *s++ = ')'; } } if (nullptr != s && s <= s1) *s = 0; return s; } const ON_wString ON_SubDFaceRegion::ToString() const { wchar_t buffer[256]; if (nullptr != ToString(buffer, sizeof(buffer) / sizeof(buffer[0]))) return ON_wString(buffer); return ON_wString::EmptyString; } static bool Internal_Seal3d(const double* src, double* dst, double tol ) { #if 1 // coded this way for debugging. // Release build optimization will inline this static and doubles will be in registers. const double d = (fabs(src[0] - dst[0]) + fabs(src[1] - dst[1]) + fabs(src[2] - dst[2])); if (d <= tol) { *dst++ = *src++; *dst++ = *src++; *dst = *src; return true; } return false; #else // to see what happens if no "micro gap" sealing occurs. return true; #endif } bool ON_SubDMeshFragment::SealPoints( bool bTestNearEqual, const double* src, double* dst ) { if (bTestNearEqual) return Internal_Seal3d( src, dst, 1.0e-8 ); *dst++ = *src++; *dst++ = *src++; *dst = *src; return true; } bool ON_SubDMeshFragment::SealNormals( bool bTestNearEqual, const double* src, double* dst ) { if (bTestNearEqual) return Internal_Seal3d( src, dst, 1.0e-2 ); *dst++ = *src++; *dst++ = *src++; *dst = *src; return true; } bool ON_SubDMeshFragment::SealAdjacentSides( bool bTestNearEqual, bool bCopyNormals, const ON_SubDMeshFragment& src_fragment, unsigned int i0, unsigned int i1, ON_SubDMeshFragment& dst_fragment, unsigned int j0, unsigned int j1 ) { for (;;) { unsigned int m = 4 * src_fragment.m_grid.m_side_segment_count; if (i0 > m || i1 > m) break; m = 4 * dst_fragment.m_grid.m_side_segment_count; if (j0 > m || j1 > m) break; m = (i0 > i1) ? i0 - i1 : i1 - i0; if ( m != ((j0 > j1) ? j0 - j1 : j1 - j0)) break; if (i0 > i1) { m = i0; i0 = i1; i1 = m; m = j0; j0 = j1; j1 = m; } const int delta_j = (j0 < j1) ? 1 : -1; const double* src; double* dst; unsigned int src_stride = (unsigned int)src_fragment.m_P_stride; unsigned int dst_stride = (unsigned int)dst_fragment.m_P_stride; int j = (int)j0; for (unsigned int i = i0; i <= i1; i++, j += delta_j) { src = &src_fragment.m_P[src_fragment.m_grid.m_S[i]*src_stride]; dst = &dst_fragment.m_P[dst_fragment.m_grid.m_S[j]*dst_stride]; if (false == ON_SubDMeshFragment::SealPoints(bTestNearEqual,src,dst)) { ON_SUBD_ERROR("Point locations failed near equal test."); return false; } } if (bCopyNormals) { src_stride = (unsigned int)src_fragment.m_N_stride; dst_stride = (unsigned int)dst_fragment.m_N_stride; j = (int)j0; for (unsigned int i = i0; i <= i1; i++, j += delta_j) { src = &src_fragment.m_N[src_fragment.m_grid.m_S[i] * src_stride]; dst = &dst_fragment.m_N[dst_fragment.m_grid.m_S[j] * dst_stride]; if (false == ON_SubDMeshFragment::SealNormals(bTestNearEqual,src,dst)) { ON_SUBD_ERROR("Normal locations failed near equal test."); return false; } } } return true; } ON_SUBD_ERROR("Invalid input."); return false; } class VertexToDuplicate { public: // ON_SubD information const ON_SubDVertex* m_vertex = nullptr; const ON_SubDFace* m_face = nullptr; unsigned int m_sector_id = 0; // all the dups for a specific vertex that are in the same sector get a unique nonzero sector id // ON_Mesh information unsigned int m_mesh_V_index = 0; unsigned int m_mesh_F_index = 0; static int CompareVertexId(const class VertexToDuplicate* a, const class VertexToDuplicate*); static int CompareVertexIdAndFaceId(const class VertexToDuplicate* a, const class VertexToDuplicate*); static int CompareSectorIdAndFaceId(const class VertexToDuplicate* a, const class VertexToDuplicate*); static bool NeedsDuplicated(const ON_SubDVertex* vertex); }; int VertexToDuplicate::CompareVertexId(const class VertexToDuplicate* a, const class VertexToDuplicate* b) { if ( a == b ) return 0; if ( nullptr == a ) return -1; if ( nullptr == b ) return 1; unsigned int a_id = a->m_vertex ? a->m_vertex->m_id : 0; unsigned int b_id = b->m_vertex ? b->m_vertex->m_id : 0; if ( a_id < b_id ) return -1; if ( a_id > b_id ) return 1; return 0; } int VertexToDuplicate::CompareVertexIdAndFaceId(const class VertexToDuplicate* a, const class VertexToDuplicate* b) { if ( a == b ) return 0; if (nullptr == a) return -1; if (nullptr == b) return 1; unsigned int a_id = a->m_vertex ? a->m_vertex->m_id : 0; unsigned int b_id = b->m_vertex ? b->m_vertex->m_id : 0; if (a_id < b_id) return -1; if (a_id > b_id) return 1; a_id = a->m_face ? a->m_face->m_id : 0; b_id = b->m_face ? b->m_face->m_id : 0; if (a_id < b_id) return -1; if (a_id > b_id) return 1; return 0; } int VertexToDuplicate::CompareSectorIdAndFaceId(const class VertexToDuplicate* a, const class VertexToDuplicate* b) { if (a == b) return 0; if (nullptr == a) return -1; if (nullptr == b) return 1; unsigned int a_id = a->m_sector_id; unsigned int b_id = b->m_sector_id; if (a_id < b_id) return -1; if (a_id > b_id) return 1; a_id = a->m_face ? a->m_face->m_id : 0; b_id = b->m_face ? b->m_face->m_id : 0; if (a_id < b_id) return -1; if (a_id > b_id) return 1; return 0; } bool VertexToDuplicate::NeedsDuplicated( const ON_SubDVertex* vertex ) { if ( nullptr == vertex || vertex->m_face_count < 2 || vertex->m_edge_count < 2 || nullptr == vertex->m_edges || nullptr == vertex->m_faces) return false; if (vertex->IsSmooth()) return false; const unsigned int edge_count = vertex->m_edge_count; for (unsigned int vei = 0; vei < edge_count; vei++) { const ON_SubDEdge* edge = vertex->Edge(vei); if (nullptr != edge && false == edge->IsSmooth() && edge->m_face_count > 1) return true; } return false; } static bool Internal_UpdateMeshFaceVertexIndex( ON_Mesh& mesh, unsigned mesh_F_index, unsigned int mesh_F_count, unsigned mesh_V_index0, unsigned mesh_V_index1 ) { if (mesh_F_index < mesh_F_count && mesh_V_index0 < mesh_V_index1 ) { unsigned int* fvi = (unsigned int*)(mesh.m_F[mesh_F_index].vi); if (fvi[0] == mesh_V_index0) fvi[0] = mesh_V_index1; if (fvi[1] == mesh_V_index0) fvi[1] = mesh_V_index1; if (fvi[2] == mesh_V_index0) fvi[2] = mesh_V_index1; if (fvi[3] == mesh_V_index0) fvi[3] = mesh_V_index1; return true; } ON_SubDIncrementErrorCount(); return false; } static VertexToDuplicate* Internal_FindMatchingVertexIdAndFaceId(const VertexToDuplicate* key, VertexToDuplicate* vertex_dups, unsigned int vertex_dups_count ) { return (VertexToDuplicate*)bsearch(key, vertex_dups, vertex_dups_count, sizeof(vertex_dups[0]), (int(*)(const void*, const void*))VertexToDuplicate::CompareVertexIdAndFaceId); } static bool Internal_DuplicateVertices( ON_Mesh& mesh, ON_3dPointArray& D, ON_SimpleArray& dups_array ) { const unsigned int mesh_F_count = mesh.m_F.UnsignedCount(); const unsigned int mesh_D_count0 = D.UnsignedCount(); const unsigned int dups_count = dups_array.UnsignedCount(); if (dups_count <= 1) return true; unsigned int sector_id = 0; dups_array.QuickSortAndRemoveDuplicates(VertexToDuplicate::CompareVertexIdAndFaceId); ON_SubDSectorIterator sit; VertexToDuplicate* dups = dups_array; VertexToDuplicate key; unsigned int i1 = 0; for (unsigned int i0 = i1; i0 < dups_count; i0 = i1) { key = dups[i0]; if (nullptr == key.m_vertex) { ON_SubDIncrementErrorCount(); return false; } for (i1 = i0 + 1; i1 < dups_count; i1++) { int rc = VertexToDuplicate::CompareVertexId(&key,dups+i1); if (rc < 0) break; if ( 0 != rc || key.m_vertex != dups[i1].m_vertex || key.m_mesh_V_index != dups[i1].m_mesh_V_index || key.m_mesh_V_index >= mesh_D_count0 ) { ON_SubDIncrementErrorCount(); return false; } } if ( i1 == i0+1) continue; const unsigned int mesh_V_index0 = key.m_mesh_V_index; const ON_3dPoint P = D[mesh_V_index0]; VertexToDuplicate* vertex_dups = dups+i0; const unsigned vertex_dups_count = i1 - i0; unsigned int sector_count = 0; // Set the sector id for (unsigned int k = 0; k < vertex_dups_count; ++k) { if (vertex_dups[k].m_sector_id > 0 ) continue; // this was in a previously found sector. ++sector_id; ++sector_count; // When the priority is encoding creases, we need to divide the faces around this vertex // into sets that are in the same sector. Since a dart vertex has only 1 sector // but the crease edge at a dart needs to have duplicate vertices at both ends, // darts get special case handling. if (nullptr == sit.Initialize(vertex_dups[k].m_face, 0, key.m_vertex)) { // not fatal, but don't bother with the vertices in vertex_dups[]. ON_SubDIncrementErrorCount(); sector_count = 0; break; } if (nullptr == sit.IncrementToCrease(-1)) { // not fatal, but don't bother with the vertices in vertex_dups[]. ON_SubDIncrementErrorCount(); sector_count = 0; break; } if (0 == k && key.m_vertex->IsDart()) { // darts have a single sector, but the dart vertex needs to be duplicated across the creased edge. const ON_SubDEdge* edge = sit.CurrentEdge(0); if (nullptr == edge || false == edge->IsCrease() || 2 != edge->m_face_count) { // not fatal, but don't bother with the vertices in vertex_dups[]. ON_SubDIncrementErrorCount(); sector_count = 0; break; } // we found the creased edge - duplicate the ON_Mesh vertices at this edge. for (unsigned int efi = 0; efi < 2; efi++) { key.m_face = edge->Face(efi); VertexToDuplicate* vertex_dup = Internal_FindMatchingVertexIdAndFaceId(&key, vertex_dups, vertex_dups_count); if (nullptr == vertex_dup) { // not fatal, but don't bother with the vertices in vertex_dups[]. ON_SubDIncrementErrorCount(); sector_count = 0; break; } // add a new vertex on either side of the dart's edge vertex_dup->m_mesh_V_index = D.UnsignedCount(); D.Append(P); if ( false == Internal_UpdateMeshFaceVertexIndex(mesh, vertex_dup->m_mesh_F_index, mesh_F_count, mesh_V_index0, vertex_dup->m_mesh_V_index) ) { // not fatal, but don't bother with the vertices in vertex_dups[]. ON_SubDIncrementErrorCount(); sector_count = 0; break; } } for (k = 0; k < vertex_dups_count; ++k) { vertex_dups[k].m_sector_id = sector_id; } break; } // assign the sector id for (key.m_face = sit.CurrentFace(); nullptr != key.m_face; key.m_face = sit.NextFace(ON_SubDSectorIterator::StopAt::AnyCrease)) { VertexToDuplicate* vertex_dup = Internal_FindMatchingVertexIdAndFaceId(&key, vertex_dups, vertex_dups_count); if (nullptr == vertex_dup) { // not fatal, but don't bother with the vertices in vertex_dups[]. ON_SubDIncrementErrorCount(); sector_count = 0; break; } vertex_dup->m_sector_id = sector_id; } if (0 == sector_count) break; } if (0 == sector_count) continue; if (sector_count > 1) { // sort vertex_dups[] by sector id ON_qsort(vertex_dups, vertex_dups_count, sizeof(vertex_dups[0]), (int(*)(const void*, const void*))VertexToDuplicate::CompareSectorIdAndFaceId); } unsigned int k1 = 0; for (unsigned int k0 = 0; k0 < vertex_dups_count; k0 = k1) { key = vertex_dups[k0]; for (k1 = k0 + 1; k1 < vertex_dups_count; ++k1) { if (key.m_sector_id != vertex_dups[k1].m_sector_id) break; } if (k0 > 0) { // make a new vertex for this sector; key.m_mesh_V_index = D.UnsignedCount(); D.Append(P); // update ON_Mesh faces in this sector to use new vertex for (unsigned k = k0; k < k1; ++k) Internal_UpdateMeshFaceVertexIndex(mesh, vertex_dups[k].m_mesh_F_index, mesh_F_count, mesh_V_index0, key.m_mesh_V_index); } } } return true; } //static const ON_2dPoint Internal_NgonFakeSurfaceParameter( // const ON_SubDFace* face, // const ON_2dPoint face_pack_rect_corners[4], // const ON_2dPoint& center, // unsigned fvi //) //{ // for (;;) // { // if (nullptr == face || face->m_edge_count < 5) // break; // const ON_2dVector diag = face_pack_rect_corners[0] - center; // const double r = 0.5 * ((fabs(diag.y) < fabs(diag.x)) ? fabs(diag.y) : fabs(diag.x)); // const double a = (((double)fvi) / ((double)face->m_edge_count))*ON_2PI; // return ON_2dPoint( center.x + r * cos(a), center.y + r*sin(a) ); // } // return ON_2dPoint::NanPoint; //} ON_Mesh* ON_SubD::GetControlNetMesh( ON_Mesh* destination_mesh, ON_SubDGetControlNetMeshPriority priority ) const { if (destination_mesh) destination_mesh->Destroy(); const ON_SubDimple* subdimple = SubDimple(); if (nullptr == subdimple) return nullptr; // SubD is empty - not an error const ON_SubDLevel& level = ActiveLevel(); if (level.IsEmpty()) return ON_SUBD_RETURN_ERROR(nullptr); if (level.m_vertex_count < 3) return ON_SUBD_RETURN_ERROR(nullptr); if (level.m_edge_count < 3) return ON_SUBD_RETURN_ERROR(nullptr); if ( level.m_face_count < 1) return ON_SUBD_RETURN_ERROR(nullptr); std::unique_ptr< ON_Mesh > up; ON_Mesh* mesh = nullptr; if (nullptr != destination_mesh) { mesh = destination_mesh; *mesh = ON_Mesh::Empty; } else { mesh = new ON_Mesh(); } bool bSuccess = false; switch (priority) { case ON_SubDGetControlNetMeshPriority::Geometry: { unsigned int archive_id_partition[4] = {}; bool bLevelLinkedListIncreasingId[3] = {}; level.SetArchiveId(*subdimple, archive_id_partition, bLevelLinkedListIncreasingId); if (archive_id_partition[1] - archive_id_partition[0] == level.m_vertex_count) { // Have to use idit because subd editing (deleting and then adding) can leave the level's linked lists // with components in an order that is not increasing in id and it is critical that the next three for // loops iterate the level's components in order of increasing id. // must iterate vertices in order of increasing id ON_SubDLevelComponentIdIterator vit_by_id; vit_by_id.Initialize(bLevelLinkedListIncreasingId[0], ON_SubDComponentPtr::Type::Vertex, *subdimple, level); // must iterate vertices in order of increasing id ON_SubDLevelComponentIdIterator fit_by_id; fit_by_id.Initialize(bLevelLinkedListIncreasingId[2], ON_SubDComponentPtr::Type::Face, *subdimple, level); bSuccess = Internal_GetGeometryControlNetMesh(level, vit_by_id, fit_by_id, *mesh); } } break; case ON_SubDGetControlNetMeshPriority::TextureCoordinates: bSuccess = Internal_GetTextureCoordinatesGeometryControlNetMesh(level, *mesh); break; } if (false == bSuccess) { if (mesh != destination_mesh) delete mesh; mesh = nullptr; } else { mesh->UpdateSinglePrecisionVertices(); if (ON_SubDGetControlNetMeshPriority::TextureCoordinates != priority) { mesh->ComputeFaceNormals(); mesh->ComputeVertexNormals(); } mesh->BoundingBox(); } return mesh; } bool ON_SubD::Internal_GetGeometryControlNetMesh( const ON_SubDLevel& level, ON_SubDLevelComponentIdIterator& vit_by_id, ON_SubDLevelComponentIdIterator& fit_by_id, ON_Mesh& mesh ) const { VertexToDuplicate dup; ON_SimpleArray dups_array; const unsigned int subd_vertex_count = level.m_vertex_count; unsigned int mesh_ngon_count = 0; unsigned int mesh_face_count = 0; unsigned int max_ngon_Vcount = 0; for (const ON_SubDFace* face = level.m_face[0]; nullptr != face; face = face->m_next_face) { if ( face->m_edge_count < 2 ) continue; if (face->m_edge_count <= 4) { mesh_face_count++; continue; } mesh_ngon_count++; mesh_face_count += face->m_edge_count; if ( max_ngon_Vcount < face->m_edge_count ) max_ngon_Vcount = face->m_edge_count; } if (subd_vertex_count < 3 || mesh_face_count < 1 ) return ON_SUBD_RETURN_ERROR(false); const size_t D_initial_capacity = subd_vertex_count + mesh_ngon_count; ON_3dPointArray& D = mesh.DoublePrecisionVertices(); D.Reserve(D_initial_capacity); D.SetCount(0); ON_SimpleArray mesh_VertexNeedsDuplicated(D_initial_capacity); mesh.m_F.Reserve(mesh_face_count); mesh.m_F.SetCount(0); ON_SimpleArray< ON_2udex > ngon_spans(mesh_ngon_count); bool rc = false; for (;;) { // must iterate vertices in order of increasing id for (const ON_SubDVertex* vertex = vit_by_id.FirstVertex(); nullptr != vertex; vertex = vit_by_id.NextVertex()) { unsigned int vi = vertex->ArchiveId(); if (vi < 1 || vi > subd_vertex_count) break; if (D.UnsignedCount()+1 != vi) break; D.AppendNew() = vertex->m_P; mesh_VertexNeedsDuplicated.AppendNew() = VertexToDuplicate::NeedsDuplicated(vertex); } if (D.UnsignedCount() != subd_vertex_count) break; ngon_spans.Reserve(mesh_ngon_count); unsigned int max_ngon_face_count = 0; mesh_face_count = 0; // must iterate faces in order of increasing id for (const ON_SubDFace* face = fit_by_id.FirstFace(); nullptr != face; face = fit_by_id.NextFace()) { ON_MeshFace meshf = {}; if (face->m_edge_count <= 4) { // SubD quad face or 3-gon face gets a single ON_Mesh face if (face->m_edge_count < 3) continue; //const bool bQuad = 4 == face->m_edge_count; for (unsigned short fvi = 0; fvi < face->m_edge_count; fvi++) { const ON_SubDVertex* vertex = face->Vertex(fvi); meshf.vi[fvi] = (int)((nullptr != vertex) ? vertex->ArchiveId() : 0U); if (meshf.vi[fvi] < 1 || meshf.vi[fvi] > (int)subd_vertex_count) { meshf.vi[0] = -1; break; } meshf.vi[fvi]--; if (mesh_VertexNeedsDuplicated[meshf.vi[fvi]]) { dup.m_vertex = vertex; dup.m_face = face; dup.m_mesh_F_index = mesh.m_F.UnsignedCount(); dup.m_mesh_V_index = meshf.vi[fvi]; dups_array.Append(dup); } } if (-1 == meshf.vi[0] ) continue; if ( 3 == face->m_edge_count) meshf.vi[3] = meshf.vi[2]; mesh.m_F.Append(meshf); continue; } else // face->m_edge_count >= 5 { // SubD n-gon face with n >= 5 gets n ON_Mesh triangles grouped into ON_3dPoint center_point; if (false == face->GetSubdivisionPoint( center_point)) continue; ON_2udex ngon_span = { mesh.m_F.UnsignedCount(), 0 }; const unsigned int dup_count0 = dups_array.UnsignedCount(); const unsigned int Dcount0 = D.UnsignedCount(); const unsigned int Fcount0 = mesh.m_F.UnsignedCount(); meshf.vi[2] = (int)Dcount0; meshf.vi[3] = meshf.vi[2]; const ON_SubDVertex* vertex = face->Vertex(0); meshf.vi[1] = (nullptr != vertex) ? vertex->ArchiveId() : 0; if (meshf.vi[1] < 1 || meshf.vi[1] > (int)subd_vertex_count) continue; meshf.vi[1]--; if (mesh_VertexNeedsDuplicated[meshf.vi[1]]) { dup.m_vertex = vertex; dup.m_face = face; dup.m_mesh_F_index = mesh.m_F.UnsignedCount(); dup.m_mesh_V_index = meshf.vi[1]; dups_array.Append(dup); } mesh_VertexNeedsDuplicated.Append(false); D.Append(center_point); for (unsigned short fvi = 1; fvi <= face->m_edge_count; fvi++) { meshf.vi[0] = meshf.vi[1]; vertex = face->Vertex(fvi % face->m_edge_count); meshf.vi[1] = (int)((nullptr != vertex) ? vertex->ArchiveId() : 0U); if (meshf.vi[1] < 1 || meshf.vi[1] > (int)subd_vertex_count) { meshf.vi[0] = -1; break; } meshf.vi[1]--; if (fvi < face->m_edge_count) { if (mesh_VertexNeedsDuplicated[meshf.vi[1]]) { dup.m_vertex = vertex; dup.m_face = face; dup.m_mesh_F_index = mesh.m_F.UnsignedCount(); dup.m_mesh_V_index = meshf.vi[1]; dups_array.Append(dup); } } mesh.m_F.Append(meshf); } ngon_span.j = mesh.m_F.UnsignedCount(); unsigned int ngon_face_count = ngon_span.j - ngon_span.i; if ( -1 == meshf.vi[0] || ngon_face_count != face->EdgeCount() ) { D.SetCount(Dcount0); mesh.m_F.SetCount(Fcount0); dups_array.SetCount(dup_count0); continue; } ngon_span.j = mesh.m_F.UnsignedCount(); if (ngon_face_count >= 3) { ngon_spans.Append(ngon_span); if ( ngon_face_count > max_ngon_face_count) max_ngon_face_count = ngon_face_count; } } } if (mesh.m_F.UnsignedCount() <= 0) break; rc = true; break; } level.ClearArchiveId(); if (false == rc ) return ON_SUBD_RETURN_ERROR(false); if (D.UnsignedCount() < 3 || mesh.m_F.UnsignedCount() < 1) return ON_SUBD_RETURN_ERROR(false); Internal_DuplicateVertices( mesh, D, dups_array); // group all mesh faces that came from the same level zero subd face into an ngon. if (ngon_spans.UnsignedCount() > 0 && max_ngon_Vcount >= 3) { ON_SimpleArray< unsigned int> ngon_buffer; unsigned int* ngon_fi = ngon_buffer.Reserve(2*max_ngon_Vcount); unsigned int* ngon_vi = ngon_fi + max_ngon_Vcount; for (unsigned int ngon_dex = 0; ngon_dex < ngon_spans.UnsignedCount(); ngon_dex++ ) { ON_2udex ngon_span = ngon_spans[ngon_dex]; unsigned int Fcount = ngon_span.j-ngon_span.i; if ( Fcount < 3) continue; ngon_fi[0] = ngon_span.i; ngon_fi[0] = (unsigned int)mesh.m_F[ngon_fi[0]].vi[0]; unsigned int ngon_Vcount = 0; for (unsigned int i = ngon_span.i; i < ngon_span.j; i++) { ngon_fi[ngon_Vcount] = i; ngon_vi[ngon_Vcount] = (unsigned int)(mesh.m_F[i].vi[0]); ngon_Vcount++; } mesh.AddNgon(ngon_Vcount, ngon_vi, ngon_Vcount, ngon_fi ); } } return true; } bool ON_SubD::Internal_GetTextureCoordinatesGeometryControlNetMesh( const ON_SubDLevel& level, ON_Mesh& mesh ) const { const bool bSetMeshT = 2 * this->TexturePointsAreSet() > this->FaceCount(); // more than half the faces have texture points. bool bSubdivide = false; // required if any SubD faces are not quads. unsigned mesh_4gon_count = 0; unsigned mesh_quad_count = 0; for (const ON_SubDFace* f = level.m_face[0]; nullptr != f; f = f->m_next_face) { const unsigned n = f->EdgeCount(); if (n < 3) continue; if (false == bSubdivide) { if (4 == n) { // subdivision is not required and we have another quad face ++mesh_quad_count; continue; } // first non-quad SubD face - switch to subdivided case bSubdivide = true; // each of the previously counted quad faces will generate 9 mesh vertices, 4 mesh faces, and a single ON_MeshNgon. mesh_4gon_count = mesh_quad_count; mesh_quad_count *= 4; } // In the subdivided case, each SubD face is represented by n quads in the ON_Mesh. if ( 4 == n) ++mesh_4gon_count; mesh_quad_count += n; } if (mesh_quad_count < 1) return ON_SUBD_RETURN_ERROR(false); mesh.m_F.Reserve(mesh_quad_count); mesh.m_F.SetCount(0); mesh.m_FN.Reserve(mesh_quad_count); mesh.m_FN.SetCount(0); const unsigned mesh_vertex_count = 4 * (mesh_quad_count - 4* mesh_4gon_count) + 9 * mesh_4gon_count; ON_3dPointArray& D = mesh.DoublePrecisionVertices(); D.Reserve(mesh_vertex_count); D.SetCount(0); mesh.m_N.Reserve(mesh_vertex_count); mesh.m_N.SetCount(0); mesh.m_S.Reserve(mesh_vertex_count); mesh.m_S.SetCount(0); if (bSetMeshT) mesh.m_T.Reserve(mesh_vertex_count); mesh.m_T.SetCount(0); mesh.m_F.Reserve(mesh_quad_count); mesh.m_F.SetCount(0); mesh.m_FN.Reserve(mesh_quad_count); mesh.m_FN.SetCount(0); ON_MeshFace mesh_f; ON_2dPoint face_pack_rect_corners[4]; ON_3dPoint quadP[4]; if (bSubdivide) { ON_2dVector ngon_sub_pack_rect_size = ON_2dVector::NanVector; ON_2dVector ngon_sub_pack_rect_delta = ON_2dVector::NanVector; ON_2dPoint quadS[4] = { ON_2dPoint::NanPoint, ON_2dPoint::NanPoint, ON_2dPoint::NanPoint, ON_2dPoint::NanPoint }; ON_3dPoint quadT[4] = { ON_3dPoint::NanPoint, ON_3dPoint::NanPoint, ON_3dPoint::NanPoint, ON_3dPoint::NanPoint }; ON_3dPoint faceT[2] = { ON_3dPoint ::NanPoint, ON_3dPoint::NanPoint }; ON_SimpleArray P(64); for (const ON_SubDFace* f = level.m_face[0]; nullptr != f; f = f->m_next_face) { const unsigned n = f->m_edge_count; if (n < 3) continue; P.SetCount(0); for (unsigned i = 0; i < n; ++i) { const ON_SubDVertex* v = f->Vertex(i); if (nullptr == v) break; const ON_3dPoint C = v->ControlNetPoint(); if (false == C.IsValid()) break; P.Append(v->ControlNetPoint()); } if (n != P.UnsignedCount()) continue; quadP[0] = f->ControlNetCenterPoint(); if (false == quadP[0].IsValid()) continue; const ON_3fVector N(f->ControlNetCenterNormal()); f->GetFacePackRectCorners(false, face_pack_rect_corners); const ON_2dVector face_pack_rect_size = f->PackRectSize(); if (bSetMeshT) { faceT[0] = f->TexturePoint(n - 1); faceT[1] = f->TexturePoint(0); quadT[0] = f->TextureCenterPoint(); } if (4 == n) { // An ON_SubDFace quad becomes an ON_Mesh ngon made from 4 ON_Mesh quads. // center point mesh_f.vi[0] = D.UnsignedCount(); D.Append(quadP[0]); mesh.m_N.Append(N); mesh.m_S.Append(f->PackRectOrigin() + 0.5 * f->PackRectSize()); if (bSetMeshT) mesh.m_T.Append(ON_2fPoint(quadT[0])); ON_MeshNgon* four_gon = mesh.AllocateNgon(8,4); four_gon->m_vi[0] = D.UnsignedCount(); four_gon->m_vi[1] = four_gon->m_vi[0] + 1; four_gon->m_vi[2] = four_gon->m_vi[0] + 2; four_gon->m_vi[3] = four_gon->m_vi[0] + 3; four_gon->m_vi[4] = four_gon->m_vi[0] + 4; four_gon->m_vi[5] = four_gon->m_vi[0] + 5; four_gon->m_vi[6] = four_gon->m_vi[0] + 6; four_gon->m_vi[7] = four_gon->m_vi[0] + 7; four_gon->m_fi[0] = mesh.m_F.UnsignedCount(); four_gon->m_fi[1] = four_gon->m_fi[0] + 1; four_gon->m_fi[2] = four_gon->m_fi[0] + 2; four_gon->m_fi[3] = four_gon->m_fi[0] + 3; // add 8 boundary vertices for (unsigned i = 0; i < 4; ++i) { D.Append(P[i]); D.Append(ON_3dPoint::Midpoint(P[i], P[(i + 1) % 4])); mesh.m_N.Append(N); mesh.m_N.Append(N); mesh.m_S.Append(face_pack_rect_corners[i]); mesh.m_S.Append(ON_2dPoint::Midpoint(face_pack_rect_corners[i], face_pack_rect_corners[(i+1)%4])); if (bSetMeshT) { faceT[0] = faceT[1]; faceT[1] = f->TexturePoint((i + 1) % 4); mesh.m_T.Append(ON_2fPoint(faceT[0])); mesh.m_T.Append(ON_2fPoint(ON_3dPoint::Midpoint(faceT[0], faceT[1]))); } } // add 4 ON_Mesh quads that make up the ON_SubDFace quad mesh_f.vi[3] = four_gon->m_vi[7]; for (unsigned i = 0; i < 4; ++i) { mesh_f.vi[1] = mesh_f.vi[3]; mesh_f.vi[2] = four_gon->m_vi[2 * i]; mesh_f.vi[3] = mesh_f.vi[2] + 1; mesh.m_F.Append(mesh_f); mesh.m_FN.Append(N); } // add an ON_MeshNgon that represents the ON_SubDFace quad mesh.AddNgon(four_gon); } else { quadP[3] = ON_3dPoint::Midpoint(P[n - 1], P[0]); if (bSetMeshT) quadT[3] = ON_3dPoint::Midpoint(faceT[0], faceT[1]); const ON_2udex ngon_grid_size = (n >= 5) ? ON_SubDFace::GetNgonSubPackRectSizeAndDelta(n, face_pack_rect_size, ngon_sub_pack_rect_size, ngon_sub_pack_rect_delta) : ON_2udex::Zero; // an ON_Mesh n-gon is not possible because the fake packed surface parameters are not continuous across the ON_SubD face. for (unsigned i = 0; i < n; ++i) { if (3 == n) { ON_SubDMeshFragment::Get3gonFaceFragmentPackRectCorners(false, face_pack_rect_corners, i, false, quadS); } else { ON_SubDMeshFragment::GetNgonFaceFragmentPackRectCorners( n, i, false, face_pack_rect_corners, face_pack_rect_size, ngon_grid_size, ngon_sub_pack_rect_size, ngon_sub_pack_rect_delta, quadS ); } quadP[1] = quadP[3]; quadP[2] = P[i]; quadP[3] = ON_3dPoint::Midpoint(P[i], P[(i + 1) % n]); if (bSetMeshT) { faceT[0] = faceT[1]; faceT[1] = f->TexturePoint((i + 1) % n); quadT[1] = quadT[3]; quadT[2] = faceT[0]; quadT[3] = ON_3dPoint::Midpoint(faceT[0], faceT[1]); } mesh_f.vi[0] = D.UnsignedCount(); mesh_f.vi[1] = mesh_f.vi[0] + 1; mesh_f.vi[2] = mesh_f.vi[1] + 1; mesh_f.vi[3] = mesh_f.vi[2] + 1; for (unsigned j = 0; j < 4U; ++j) { D.Append(quadP[j]); mesh.m_N.Append(N); mesh.m_S.Append(quadS[j]); if (bSetMeshT) mesh.m_T.Append(ON_2fPoint(quadT[j])); } mesh.m_F.Append(mesh_f); mesh.m_FN.Append(N); } } } } else { // All SubD faces are quads for (const ON_SubDFace* f = level.m_face[0]; nullptr != f; f = f->m_next_face) { if (4 != f->m_edge_count) continue; quadP[3].x = ON_DBL_QNAN; for (unsigned i = 0; i < 4; ++i) { const ON_SubDVertex* v = f->Vertex(i); if (nullptr == v) break; quadP[i] = v->ControlNetPoint(); if (false == quadP[i].IsValid()) break; } if (false == quadP[3].IsValid()) continue; const ON_3fVector N(f->ControlNetCenterNormal()); mesh_f.vi[0] = D.UnsignedCount(); mesh_f.vi[1] = mesh_f.vi[0] + 1; mesh_f.vi[2] = mesh_f.vi[1] + 1; mesh_f.vi[3] = mesh_f.vi[2] + 1; mesh.m_F.Append(mesh_f); mesh.m_FN.Append(N); for (unsigned i = 0; i < 4U; ++i) { D.Append(quadP[i]); mesh.m_N.Append(N); mesh.m_S.Append(f->PackRectCorner(false, i)); if (bSetMeshT) mesh.m_T.Append(ON_2fPoint(f->TexturePoint(i))); } } } ON_MappingTag mapping_tag = this->TextureMappingTag(false); if (mesh.m_S.UnsignedCount() != D.UnsignedCount()) { mesh.m_S.Destroy(); if (ON_TextureMapping::TYPE::srfp_mapping == mapping_tag.m_mapping_type) mapping_tag = ON_MappingTag::Unset; } else { // set fake surface mapping information mesh.m_srf_domain[0] = ON_Interval::ZeroToOne; mesh.m_srf_domain[1] = ON_Interval::ZeroToOne; mesh.m_srf_scale[0] = 0.0; mesh.m_srf_scale[1] = 0.0; mesh.m_packed_tex_domain[0] = ON_Interval::ZeroToOne; mesh.m_packed_tex_domain[1] = ON_Interval::ZeroToOne; mesh.m_packed_tex_rotate = false; if ( false == bSetMeshT && (false == mapping_tag.IsSet() || mapping_tag.IsDefaultSurfaceParameterMapping()) ) { const int count = mesh.m_S.Count(); mesh.m_T.Reserve(count); mesh.m_T.SetCount(0); for (int i = 0; i < count; ++i) mesh.m_T.Append(ON_2fPoint(mesh.m_S[i])); } } if (bSetMeshT) { if (mesh.m_T.UnsignedCount() != D.UnsignedCount()) mesh.m_T.Destroy(); } mesh.m_Ttag = mapping_tag; return true; } void ON_SubD::ClearEvaluationCache() const { const ON_SubDLevel* level = ActiveLevelConstPointer(); if (nullptr != level) { const_cast(this)->ChangeGeometryContentSerialNumberForExperts(false); level->ClearEvaluationCache(); } }