#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" #if defined(OPENNURBS_SUBD_WIP) ///////////////////////////////////////////////////////////////////////////////////////// // // ON_SubDLimitMeshFragment // unsigned int ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity( unsigned int display_density ) { if (display_density <= ON_SubDLimitMesh::MaximumDisplayDensity) return (1 << display_density); return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDLimitMeshFragment::DisplayDensityFromSideSegmentCount( unsigned int side_segment_count ) { unsigned int display_density; for (display_density = 0; display_density < ON_SubDLimitMesh::MaximumDisplayDensity; display_density++) { if ( 1U << display_density >= side_segment_count ) break; } if ( 1U << display_density == side_segment_count ) return display_density; return ON_SUBD_RETURN_ERROR(display_density); } unsigned int ON_SubDLimitMeshFragment::PointCountFromDisplayDensity( ON_SubD::FacetType facet_type, unsigned int mesh_density ) { unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); if ( 0 == count) return 0; if ( ON_SubD::FacetType::Quad == facet_type ) return (count + 1)*(count+1); if ( ON_SubD::FacetType::Tri == facet_type ) return ((count+1)*(count+2))/2; return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDLimitMeshFragment::FaceCountFromDisplayDensity( unsigned int mesh_density ) { unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); return count*count; // same for quads or tris } ///////////////////////////////////////////////////////////////////////////////////////// // // ON_SubDManagedLimitMeshFragment // ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT { memset(this, 0, sizeof(*this)); } ON_SubDManagedLimitMeshFragment::~ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT { Destroy(); } ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment(const ON_SubDManagedLimitMeshFragment& src) ON_NOEXCEPT { Clear(); CopyHelper(src); } ON_SubDManagedLimitMeshFragment& ON_SubDManagedLimitMeshFragment::operator=(const ON_SubDManagedLimitMeshFragment& src) ON_NOEXCEPT { if (this != &src) { Clear(); CopyHelper(src); } return *this; } #if defined(ON_HAS_RVALUEREF) // rvalue copy constructor ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment( ON_SubDManagedLimitMeshFragment&& src ) ON_NOEXCEPT { memcpy(this, &src, sizeof(*this)); src.m_storage = nullptr; src.m_storage_capacity = 0; } // rvalue assignment operator ON_SubDManagedLimitMeshFragment& ON_SubDManagedLimitMeshFragment::operator=( ON_SubDManagedLimitMeshFragment&& src ) ON_NOEXCEPT { if (this != &src) { Destroy(); memcpy(this, &src, sizeof(*this)); src.m_storage = nullptr; src.m_storage_capacity = 0; } return *this; } #endif bool ON_SubDManagedLimitMeshFragment::ReserveCapacity( ON_SubD::FacetType facet_type, unsigned int mesh_density ) ON_NOEXCEPT { Clear(); unsigned int fragment_side_count = ON_SubDManagedLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); if ( 0 == fragment_side_count ) return true; // Sanity check if ( fragment_side_count > 0xFFU ) return ON_SUBD_RETURN_ERROR(false); const unsigned int short_capacity_sanity_check = 0xFFFFU; const unsigned int P_capacity = ON_SubDManagedLimitMeshFragment::PointCountFromDisplayDensity(facet_type,mesh_density); if ( P_capacity >= short_capacity_sanity_check ) return ON_SUBD_RETURN_ERROR(false); const unsigned int F_capacity = ON_SubDManagedLimitMeshFragment::FaceCountFromDisplayDensity(mesh_density); if ( F_capacity >= short_capacity_sanity_check) return ON_SUBD_RETURN_ERROR(false); const size_t P_stride = 3; const size_t N_stride = 3; size_t storage_capacity = (P_stride + N_stride)*P_capacity; if (storage_capacity > m_storage_capacity || nullptr == m_storage) { if (m_storage_capacity > 0 && nullptr != m_storage) { delete[] m_storage; m_storage = nullptr; } m_storage = new(std::nothrow) double[storage_capacity]; if (nullptr == m_storage) return ON_SUBD_RETURN_ERROR(false); m_storage_capacity = storage_capacity; } m_P_capacity = (unsigned short)P_capacity; m_P_stride = P_stride; m_N_stride = N_stride; m_P = m_storage; m_N = m_P + (3*P_capacity); m_grid = ON_SubDLimitMeshFragmentGrid::Facets(facet_type,fragment_side_count,0U); if ( nullptr == m_grid.m_F) return ON_SUBD_RETURN_ERROR(false); return true; } void ON_SubDManagedLimitMeshFragment::Clear() ON_NOEXCEPT { memset(this, 0, sizeof(ON_SubDLimitMeshFragment)); } void ON_SubDManagedLimitMeshFragment::Destroy() ON_NOEXCEPT { double* p = const_cast(m_P); memset(this, 0, sizeof(*this)); if ( nullptr != p) delete[] p; } void ON_SubDManagedLimitMeshFragment::CopyHelper(const ON_SubDLimitMeshFragment& src) { unsigned short P_count = (nullptr != src.m_P && src.m_P_stride >= 3) ? src.m_P_count : 0U; unsigned short N_count = (nullptr != src.m_N && src.m_N_stride >= 3) ? src.m_P_count : 0U; const size_t P_stride = 3; const size_t N_stride = 3; size_t storage_capacity = P_stride*P_count + N_stride*N_count; if (storage_capacity > 0) { double* p = new(std::nothrow) double[storage_capacity]; if (nullptr == p) { ON_SubDIncrementErrorCount(); return; } m_storage = p; m_storage_capacity = storage_capacity; if (P_count > 0) { m_P = p; m_P_count = P_count; m_P_stride = P_stride; double* p1 = p + (m_P_stride*P_count); const double* srcP = src.m_P; while (p < p1) { p[0] = srcP[0]; p[1] = srcP[1]; p[2] = srcP[2]; p += P_stride; srcP += src.m_P_stride; } } if (N_count > 0) { m_N = p; m_P_count = N_count; // correct m_P_count counts both m_P and m_N. m_N_stride = N_stride; double* p1 = p + (m_N_stride*N_count); const double* srcN = src.m_N; while (p < p1) { p[0] = srcN[0]; p[1] = srcN[1]; p[2] = srcN[2]; p += N_stride; srcN += src.m_N_stride; } } m_grid = src.m_grid; } } ON_SubDManagedLimitMeshFragment ON_SubDManagedLimitMeshFragment::Create(const ON_SubDLimitMeshFragment& src) ON_NOEXCEPT { ON_SubDManagedLimitMeshFragment mf; mf.CopyHelper(src); return mf; } ///////////////////////////////////////////////////////////////////////////////////////// // // ON_SubDLimitMeshFragmentGrid // unsigned int ON_SubDLimitMeshFragmentGrid::SetQuads( unsigned int side_segment_count, unsigned int level_of_detail, size_t quad_capacity, size_t quad_stride, unsigned int* quads, size_t side_capacity, size_t side_stride, unsigned int* sides ) { if ( false == ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ) return ON_SUBD_RETURN_ERROR(0); if ( side_segment_count <= 1 ) level_of_detail = 0; else if (level_of_detail > 0) { if (level_of_detail >= 32 || 1U << level_of_detail > side_segment_count) { level_of_detail = 1; while ( 2*level_of_detail < side_segment_count ) level_of_detail*= 2; } } const unsigned int side_point_count = (side_segment_count+1); const unsigned int P_di = (1 << level_of_detail); const unsigned int P_dj = P_di*side_point_count; const unsigned int side_quad_count = side_segment_count / P_di; if (quad_capacity > 0 || nullptr != quads) { if ( quad_stride < 4 ) return ON_SUBD_RETURN_ERROR(0); if (side_quad_count*side_quad_count > quad_capacity) return ON_SUBD_RETURN_ERROR(0); unsigned int* fvi = quads; for (unsigned int n = 0; n < side_quad_count; n++) { unsigned int vi0 = n*P_dj; unsigned int vi1 = vi0 + P_dj; for (const unsigned int* fvi1 = fvi + quad_stride*side_quad_count; fvi < fvi1; fvi += quad_stride) { fvi[0] = vi0; fvi[3] = vi1; vi0 += P_di; vi1 += P_di; fvi[1] = vi0; fvi[2] = vi1; } } } if (side_capacity > 0 || nullptr != sides) { if ( side_stride < 1 ) return ON_SUBD_RETURN_ERROR(0); if (side_capacity < 4*side_quad_count +1 ) return ON_SUBD_RETURN_ERROR(0); unsigned int vi = 0; for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) { *sides = vi; vi += P_di; } for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) { *sides = vi; vi += P_dj; } for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) { *sides = vi; vi -= P_di; } for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) { *sides = vi; vi -= P_dj; } *sides = 0; } return side_quad_count*side_quad_count; } bool ON_SubDLimitMeshFragment::SideSegmentCountIsValid( unsigned int side_segment_count ) { if (side_segment_count > 0 && side_segment_count <= ON_SubDLimitMeshFragment::MaximumSideSegmentCount) { for (unsigned int n = 1; n <= side_segment_count; n *= 2) { if (n == side_segment_count) return true; } } return ON_SUBD_RETURN_ERROR(false); } unsigned int ON_SubDLimitMeshFragment::SidePointCountFromSideCount( unsigned int side_segment_count ) { return ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ? (side_segment_count+1) : 0U; } unsigned int ON_SubDLimitMeshFragment::QuadGridPointCountFromSideCount( unsigned int side_segment_count ) { unsigned int side_point_count = ON_SubDLimitMeshFragment::SidePointCountFromSideCount(side_segment_count); return side_point_count*side_point_count; } unsigned int ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount( unsigned int side_segment_count ) { return ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ? (side_segment_count*side_segment_count) : 0U; } const class ON_SubDEdge* ON_SubDLimitMeshFragment::Edge( unsigned int grid_side_index ) const { return EdgePtr(grid_side_index).Edge(); } const class ON_SubDEdgePtr ON_SubDLimitMeshFragment::EdgePtr( unsigned int grid_side_index ) const { const int grid_side_count = 4; // will be 3 for tri grid if (nullptr != m_face && grid_side_index < grid_side_count) { unsigned short fei = m_face_vertex_index[grid_side_index]; if (fei < m_face->m_edge_count) return m_face->EdgePtr(fei); grid_side_index = (grid_side_index+grid_side_count-1) % grid_side_count; fei = m_face_vertex_index[grid_side_index]; if (fei < m_face->m_edge_count) return m_face->EdgePtr(fei); } return ON_SubDEdgePtr::Null; } const class ON_SubDVertex* ON_SubDLimitMeshFragment::Vertex( unsigned int grid_corner_index ) const { ON_SubDEdgePtr eptr = EdgePtr(grid_corner_index); const ON_SubDEdge* edge = eptr.Edge(); if ( nullptr != edge ) return edge->m_vertex[eptr.EdgeDirection()]; return nullptr; } ON_3dPoint ON_SubDLimitMeshFragment::CornerPoint( unsigned int grid_corner_index ) const { if ( grid_corner_index >= 4 || nullptr == m_P || m_P_stride <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::UnsetPoint; //unsigned int i = grid_corner_index*m_grid.m_side_segment_count + 1; unsigned int i = grid_corner_index*m_grid.m_side_segment_count; return ON_3dPoint(m_P + (i*m_P_stride)); } const class ON_SubDVertexPtr ON_SubDLimitMeshFragment::VertexPtr( unsigned int grid_corner_index ) const { return ON_SubDVertexPtr::Create(Vertex(grid_corner_index)); } ON_ComponentStatus ON_SubDLimitMeshFragment::Status() const { return (nullptr == m_face) ? ON_ComponentStatus::NoneSet : m_face->m_status; } bool ON_SubDLimitMeshFragment::IsSubFragment() const { return ( nullptr != m_face && m_face_vertex_index[0] < ON_SubDFace::MaximumEdgeCount ); } bool ON_SubDLimitMeshFragment::IsCompleteFragment() const { return ( nullptr != m_face && m_face_vertex_index[0] < ON_SubDFace::MaximumEdgeCount && m_face_vertex_index[1] < ON_SubDFace::MaximumEdgeCount && m_face_vertex_index[2] < ON_SubDFace::MaximumEdgeCount ); } unsigned int ON_SubDLimitMeshFragmentGrid::SideSegmentCount() const { unsigned int side_segment_count = 1; while( side_segment_count*side_segment_count < m_F_count) side_segment_count *= 2; return (side_segment_count*side_segment_count == m_F_count) ? side_segment_count : 0; } unsigned int ON_SubDLimitMeshFragmentGrid::GridId() const { for (;;) { if (nullptr != m_F) break; // m_F_count = 2^(2n) for (unsigned int id = 0; id <= 16; id += 2) { if ((1U << id) == m_F_count) { id /= 2; // id = "n" unsigned int lod = (m_F_level_of_detail > id) ? id : (unsigned int)m_F_level_of_detail; id = 32*id + 2*lod; if ( ON_SubD::FacetType::Tri == m_F_type) id += 1; else if ( ON_SubD::FacetType::Quad != m_F_type) break; return id; } } } return 0; } unsigned int ON_SubDLimitMeshFragmentGrid::GridFacetSideCount() const { switch (m_F_type) { case ON_SubD::FacetType::Tri: return 3; case ON_SubD::FacetType::Quad: return 4; } return 0; } bool ON_SubDLimitMeshFragmentGrid::GetGridParameters( unsigned int grid_point_index, double grid_parameters[2] ) const { for (;;) { const unsigned int side_segment_count = SideSegmentCount(); if ( 0 == side_segment_count ) break; const unsigned int grid_side_point_count = side_segment_count + 1; if (grid_point_index >= grid_side_point_count*grid_side_point_count) break; unsigned int i = grid_point_index % grid_side_point_count; unsigned int j = grid_point_index / grid_side_point_count; if ( 0 == i ) grid_parameters[0] = 0.0; else if ( i == grid_side_point_count) grid_parameters[0] = 1.0; else grid_parameters[0] = i*1.0 / ((double)grid_side_point_count); if ( 0 == j ) grid_parameters[1] = 0.0; else if ( j == grid_side_point_count) grid_parameters[1] = 1.0; else grid_parameters[1] = j*1.0 / ((double)grid_side_point_count); return true; } grid_parameters[0] = ON_UNSET_VALUE; grid_parameters[1] = ON_UNSET_VALUE; return false; } ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Facets( ON_SubD::FacetType facet_type, unsigned int side_segment_count, unsigned int level_of_detail ) { if (ON_SubD::FacetType::Quad == facet_type) return ON_SubDLimitMeshFragmentGrid::Quads(side_segment_count, level_of_detail); if (ON_SubD::FacetType::Tri == facet_type) return ON_SubDLimitMeshFragmentGrid::Quads(side_segment_count, level_of_detail); return ON_SubDLimitMeshFragmentGrid::Empty; } ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Tris( unsigned int side_segment_count, unsigned int level_of_detail ) { return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty); } ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( unsigned int side_segment_count, unsigned int level_of_detail ) { static const ON_SubDLimitMeshFragmentGrid* grid_cache[9] = { 0 }; // side_segment_count = 1,2,4,8,16,32,64,128,256 const unsigned int grid_cache_index = ON_SubDLimitMeshFragment::DisplayDensityFromSideSegmentCount(side_segment_count); if ( side_segment_count != ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(grid_cache_index) ) return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty); const ON_SubDLimitMeshFragmentGrid* fragment_grid = grid_cache[grid_cache_index]; if (nullptr != fragment_grid) { while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail) fragment_grid = fragment_grid->m_next_level_of_detail; return *fragment_grid; } #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) // The code below is thread safe and constructs the ON_SubDLimitMeshFragmentGrids // that are resources shared by all ON_SubDLimitMeshFragments. static ON_SleepLock lock; const bool bReturnLock = lock.GetLock(50,ON_SleepLock::OneMinute,true); #endif // The ON_SubDLimitMeshFragmentGrid classes created below are created one time as needed // and used millions of times after that. These are app workspace allocations // and not memory leaks. Once a grid exists, it is saved in grid_cache[] and returned // above the next zillion times it is required. ON_MemoryAllocationTracking disable_tracking(false); // make the requested grid unsigned int quad_capacity = 0; unsigned int side_segment_capacity = 0; unsigned int grid_count = 0; unsigned int grid_cache_index0 = grid_cache_index; unsigned int grid_cache_index1 = grid_cache_index; if (grid_cache_index <= 4) { // make all the common small grids grid_cache_index0 = 0; grid_cache_index1 = 4; } for (unsigned int i = grid_cache_index0; i <= grid_cache_index1; i++) { // allocate all levels of detail for each segment side count unsigned int s1 = (1U << i); for (unsigned int s2 = s1; s2 > 0; s2 /= 2) { quad_capacity += ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount(s2); side_segment_capacity += 4*s2 + 1; grid_count++; } } const unsigned int vdex_capacity = (quad_capacity*4 + side_segment_capacity); size_t sz1 = grid_count*sizeof(ON_SubDLimitMeshFragmentGrid); size_t sz2 = vdex_capacity*sizeof(unsigned int); if (0 != sz2 % sizeof(ON_SubDLimitMeshFragmentGrid)) sz2 = (1 + sz2/sizeof(ON_SubDLimitMeshFragmentGrid))*sizeof(ON_SubDLimitMeshFragmentGrid); ON_SubDLimitMeshFragmentGrid* grids = new (std::nothrow) ON_SubDLimitMeshFragmentGrid[(sz1 + sz2)/sizeof(ON_SubDLimitMeshFragmentGrid)]; ON_SubDLimitMeshFragmentGrid grid = ON_SubDLimitMeshFragmentGrid::Empty; grid.m_F_type = ON_SubD::FacetType::Quad; grid.m_F_stride = 4; unsigned int* vdex0 = (unsigned int*)(grids + grid_count); unsigned int* vdex1 = vdex0 + vdex_capacity; unsigned int* vdex = vdex0; for (unsigned int i = grid_cache_index0; i <= grid_cache_index1; i++) { const unsigned int s1 = (1U << i); ON_SubDLimitMeshFragmentGrid* first_lod = grids; ON_SubDLimitMeshFragmentGrid* prev_lod = nullptr; grid.m_F_level_of_detail = 0; for (unsigned int s2 = s1; s2 > 0; s2 /= 2, grids++) { const unsigned int grid_F_capacity = ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount(s2); const unsigned int grid_S_capacity = 4*s2 + 1; grid.m_side_segment_count = (unsigned char)s2; grid.m_F_count = (unsigned short)grid_F_capacity; grid.m_F = vdex; vdex += 4*grid_F_capacity; grid.m_S = vdex; vdex += grid_S_capacity; if (vdex > vdex1) { ON_SubDIncrementErrorCount(); break; } ON_SubDLimitMeshFragmentGrid::SetQuads( s1, // top level side_segment_count grid.m_F_level_of_detail, grid_F_capacity, grid.m_F_stride, const_cast(grid.m_F), grid_S_capacity, 1U, const_cast(grid.m_S) ); *grids = grid; if ( nullptr != prev_lod ) { grids->m_prev_level_of_detail = prev_lod; prev_lod->m_next_level_of_detail = grids; } prev_lod = grids; grid.m_F += (grid.m_F_count*grid.m_F_stride); grid.m_F_level_of_detail++; } // Do not initialize grid_cache[i] until entire linked list is ready to be used. // This way if the lock is stolen for some unforseen reason, we risk leaking memory // but we will not crash. grid_cache[i] = first_lod; } #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) if ( bReturnLock ) lock.ReturnLock(); #endif if (vdex != vdex1) { ON_SubDIncrementErrorCount(); } fragment_grid = grid_cache[grid_cache_index]; if (nullptr != fragment_grid) { while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail) fragment_grid = fragment_grid->m_next_level_of_detail; return *fragment_grid; } return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty); } ////////////////////////////////////////////////////////////////////////// // // ON_SubDLimitMesh // void ON_SubDLimitMeshImpl::ClearFragmentFacePointers() { // When the ON_SubDimple that manages the faces referenced by // fragment->m_face is deleted, fragment->m_face must be set to zero. if (nullptr != m_first_fragment && nullptr != m_first_fragment->m_face) { for (auto fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) fragment->m_face = nullptr; } } // //bool ON_SubDLimitMeshImpl::ReserveCapacityx( // const class ON_SubD& subd, // ON_SubDDisplayParameters limit_mesh_parameters // ) //{ // const unsigned int level_index = limit_mesh_parameters.m_level_index; // // unsigned int subd_fragment_count = subd.LimitSurfaceMeshFragmentCount(level_index); // if ( subd_fragment_count < 1 ) // return ON_SUBD_RETURN_ERROR(false); // // ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd.LevelSubDType(level_index)); // // return ReserveCapacity( // subd_fragment_count, // facet_type, // limit_mesh_parameters.m_display_density, // level_index); //} bool ON_SubDLimitMeshImpl::ReserveCapacity( unsigned int subd_fragment_count, ON_SubD::FacetType facet_type, unsigned int display_density ) { ClearTree(); m_display_density = 0; m_facet_type = ON_SubD::FacetType::Unset; m_fragment_count = 0; m_fragment_point_count = 0; m_first_fragment = nullptr; if ( display_density > ON_SubDLimitMesh::MaximumDisplayDensity) return ON_SUBD_RETURN_ERROR(false); unsigned int fragment_point_count = ON_SubDLimitMeshFragment::PointCountFromDisplayDensity(facet_type, display_density); if ( subd_fragment_count < 1 ) return ON_SUBD_RETURN_ERROR(false); size_t sizeof_point_and_normals = 6*fragment_point_count*sizeof(double); size_t sizeof_fragment = sizeof(ON_SubDLimitMeshFragment); if (0 != sizeof_fragment % sizeof(double)) { sizeof_fragment = (1 + sizeof_fragment / sizeof(double))*sizeof(double); } if( false == m_fsp.Create(sizeof_fragment + sizeof_point_and_normals,subd_fragment_count,0)) return ON_SUBD_RETURN_ERROR(false); m_display_density = display_density; m_facet_type = facet_type; m_fragment_point_count = fragment_point_count; return true; } ON_SubDLimitMeshFragment* ON_SubDLimitMeshImpl::CopyCallbackFragment( const ON_SubDLimitMeshFragment* callback_fragment ) { if ( nullptr == callback_fragment) return ON_SUBD_RETURN_ERROR(nullptr); if ( 0 == callback_fragment->m_P_count ) return ON_SUBD_RETURN_ERROR(nullptr); if ( callback_fragment->m_P_count > m_fragment_point_count ) return ON_SUBD_RETURN_ERROR(nullptr); if ( nullptr == callback_fragment->m_P || nullptr == callback_fragment->m_N ) return ON_SUBD_RETURN_ERROR(nullptr); double* P = (double*)m_fsp.AllocateDirtyElement(); if ( nullptr == P) return ON_SUBD_RETURN_ERROR(nullptr); double* N = P + 3*m_fragment_point_count; ON_SubDLimitMeshFragment* fragment = (ON_SubDLimitMeshFragment*)(N + 3*m_fragment_point_count); *fragment = *callback_fragment; const size_t fragment_stride = 3; fragment->m_P_capacity = (unsigned short)m_fragment_point_count; fragment->m_P_stride = fragment_stride; fragment->m_P = P; fragment->m_N_stride = fragment_stride; fragment->m_N = N; size_t srcP_stride = callback_fragment->m_P_stride; const double* srcP = callback_fragment->m_P; const double* srcP1 = srcP + srcP_stride*callback_fragment->m_P_count; while ( srcP < srcP1 ) { P[0] = srcP[0]; P[1] = srcP[1]; P[2] = srcP[2]; P += fragment_stride; srcP += srcP_stride; } srcP_stride = callback_fragment->m_N_stride; srcP = callback_fragment->m_N; srcP1 = srcP + srcP_stride*callback_fragment->m_P_count; while ( srcP < srcP1 ) { N[0] = srcP[0]; N[1] = srcP[1]; N[2] = srcP[2]; N += fragment_stride; srcP += srcP_stride; } m_limit_mesh_content_serial_number = ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber(); return fragment; } bool ON_SubDLimitMeshImpl::AddFinishedFragment( ON_SubDLimitMeshFragment* finished_fragment ) { if ( nullptr == finished_fragment) return ON_SUBD_RETURN_ERROR(false); if ( 0 == finished_fragment->m_P_count ) return ON_SUBD_RETURN_ERROR(false); if ( finished_fragment->m_P_count > m_fragment_point_count ) return ON_SUBD_RETURN_ERROR(false); if ( nullptr == finished_fragment->m_P || nullptr == finished_fragment->m_N ) return ON_SUBD_RETURN_ERROR(false); m_fragment_count++; if (nullptr == m_first_fragment) { m_first_fragment = finished_fragment; m_last_fragment = finished_fragment; m_bbox = finished_fragment->m_bbox; } else { m_last_fragment->m_next_fragment = finished_fragment; finished_fragment->m_prev_fragment = m_last_fragment; m_last_fragment = finished_fragment; m_bbox.Union(finished_fragment->m_bbox); } m_limit_mesh_content_serial_number = ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber(); return true; } unsigned int ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber() { static unsigned int serial_number = 0; serial_number++; return serial_number; } ON_SubDLimitMeshImpl::ON_SubDLimitMeshImpl() : m_limit_mesh_content_serial_number(ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber()) {} ON_SubDLimitMeshImpl::ON_SubDLimitMeshImpl( const class ON_SubDLimitMeshImpl& src ) : m_limit_mesh_content_serial_number(ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber()) { if ( nullptr != src.m_first_fragment && ReserveCapacity((unsigned int)src.m_fsp.ActiveElementCount(), src.m_facet_type, src.m_display_density ) ) { for (const ON_SubDLimitMeshFragment* src_fragment = src.m_first_fragment; nullptr != src_fragment; src_fragment = src_fragment->m_next_fragment) { ON_SubDLimitMeshFragment* fragment = CopyCallbackFragment(src_fragment); AddFinishedFragment(fragment); } } } void ON_SubDLimitMeshImpl::ClearTree() { if (nullptr != m_fragment_tree) { delete m_fragment_tree; m_fragment_tree = nullptr; } } const ON_RTree& ON_SubDLimitMeshImpl::FragmentTree() const { if (nullptr != m_fragment_tree && nullptr != m_first_fragment) { double Pbox[2][3]; ON_RTree* fragment_tree = new ON_RTree(); for (const ON_SubDLimitMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) { const double* P = fragment->m_P; const size_t P_stride = fragment->m_P_stride; if (nullptr == P || P_stride < 3 || fragment->m_P_count <= 0) continue; const double* P1 = P + P_stride*fragment->m_P_count; Pbox[0][0] = Pbox[1][0] = P[0]; Pbox[0][1] = Pbox[1][1] = P[1]; Pbox[0][2] = Pbox[1][2] = P[2]; for (P += P_stride; P < P1; P += P_stride) { if (P[0] < Pbox[0][0]) Pbox[0][0] = P[0]; else if (P[0] > Pbox[1][0]) Pbox[1][0] = P[0]; if (P[1] < Pbox[0][1]) Pbox[0][1] = P[1]; else if (P[1] > Pbox[1][1]) Pbox[1][1] = P[1]; if (P[2] < Pbox[0][2]) Pbox[0][2] = P[2]; else if (P[2] > Pbox[1][2]) Pbox[1][2] = P[2]; fragment_tree->Insert(Pbox[0], Pbox[1], (void*)fragment); } } const_cast< ON_SubDLimitMeshImpl* >(this)->m_fragment_tree = fragment_tree; } return (nullptr == m_fragment_tree ) ? ON_RTree::Empty : *m_fragment_tree; } bool ON_SubDLimitMeshImpl::GetTightBoundingBox( ON_BoundingBox& bbox, bool bGrowBox, const ON_Xform* xform ) const { ON_BoundingBox local_bbox = ON_BoundingBox::EmptyBoundingBox; if (nullptr != xform && xform->IsIdentity()) xform = nullptr; if (nullptr == xform) { local_bbox = m_bbox; } else { for (const ON_SubDLimitMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) { ON_GetPointListBoundingBox(3, 0, fragment->m_P_count, (int)fragment->m_P_stride, fragment->m_P, local_bbox, fragment != m_first_fragment, xform); } } if (bGrowBox && bbox.IsValid()) bbox.Union(local_bbox); else bbox = local_bbox; return true; } class /*DO NOT EXPORT*/ON_SubDLimitMeshImpl_CallbackContext { public: #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) bool m_bUseMultipleThreads = false; // If true, callback uses the lock ON_SleepLock m_lock; #endif ON_SubDLimitMeshImpl* m_impl = nullptr; ON_SimpleArray< ON_SubDLimitMeshFragment* > m_face_mesh_fragments; static bool FragmentCallbackFunction( ON__UINT_PTR impl_context_as_void, const class ON_SubDLimitMeshFragment* fragment ); /* Description: compares the face id and then the fragment index. Parameters: a - [in] b - [in] The caller insures that a, b, a->m_face and b->m_face are not nullptr. */ static int CompareFragmentIndex( ON_SubDLimitMeshFragment*const* a, ON_SubDLimitMeshFragment*const* b ); /* Description: Once all the mesh fragments for a face are delivered and sorted, this function is called to make coincident vertices have identical locations and normals. Parameters: count - [in] Number of elements in mesh_fragments[]. This should be the same number as sub_fragments[i].m_face_fragment_count. mesh_fragments - [in] Every element in the array should have the same ON_SubDLimitMeshFragment.m_face->m_id */ static bool ProcessSortedFragments( unsigned int count, ON_SubDLimitMeshFragment** mesh_fragments ); static bool CoincidentSideCopy( ON_SubDLimitMeshFragment* prev_fragment, ON_SubDLimitMeshFragment* fragment ); }; bool ON_SubDLimitMeshImpl_CallbackContext::FragmentCallbackFunction( ON__UINT_PTR callback_context_as_void, const class ON_SubDLimitMeshFragment* callback_fragment ) { bool bContinueMeshCalculation = true; ON_SubDLimitMeshImpl_CallbackContext* context = (ON_SubDLimitMeshImpl_CallbackContext*)callback_context_as_void; #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) bool bReleaseLock = false; if (context->m_bUseMultipleThreads) { if (false == context->m_lock.GetLock(0, ON_SleepLock::OneSecond)) { // return true to keep going, but something is really hogging the lock return ON_SUBD_RETURN_ERROR(bContinueMeshCalculation); } } #endif for (;;) { ON_SubDLimitMeshFragment* impl_managed_fragment = context->m_impl->CopyCallbackFragment(callback_fragment); if (nullptr == impl_managed_fragment) { ON_SubDIncrementErrorCount(); bContinueMeshCalculation = false; // terminate meshing break; } const unsigned int face_fragment_count = impl_managed_fragment->m_face_fragment_count; const unsigned int face_id = (nullptr == impl_managed_fragment->m_face) ? 0 : impl_managed_fragment->m_face->m_id; if ( 0 == face_id || ON_UNSET_UINT_INDEX == face_id || face_fragment_count <= 1 || impl_managed_fragment->m_face_fragment_index >= face_fragment_count ) { // simple case where no additional processing is reqired context->m_impl->AddFinishedFragment(impl_managed_fragment); break; } unsigned int count = context->m_face_mesh_fragments.UnsignedCount(); if (count + 1 < face_fragment_count) { // waiting on more fragments context->m_face_mesh_fragments.Append(impl_managed_fragment); break; } ON_SubDLimitMeshFragment** mesh_fragments = context->m_face_mesh_fragments.Array(); unsigned int delivered_mesh_fragment_count = 0; unsigned int i0 = ON_UNSET_UINT_INDEX; for (unsigned int i = 0; i < count; i++) { if ( face_id != mesh_fragments[i]->m_face->m_id) continue; if ( 0 == delivered_mesh_fragment_count++) i0 = i; if ( delivered_mesh_fragment_count + 1 == face_fragment_count ) break; } if (delivered_mesh_fragment_count + 1 < face_fragment_count) { // waiting on more fragments context->m_face_mesh_fragments.Append(impl_managed_fragment); break; } // copy the fragments we need to process to local storage I can release the lock. ON_SimpleArray< ON_SubDLimitMeshFragment* > local_mesh_fragments(face_fragment_count); if (delivered_mesh_fragment_count == count) { local_mesh_fragments.Append(count,mesh_fragments); context->m_face_mesh_fragments.SetCount(0); } else { local_mesh_fragments.Append(mesh_fragments[i0]); unsigned int count1 = 0; for (unsigned int i = i0+1; i < count; i++) { if (face_id != mesh_fragments[i]->m_face->m_id) { mesh_fragments[count1++] = mesh_fragments[i]; continue; } local_mesh_fragments.Append(mesh_fragments[i]); } context->m_face_mesh_fragments.SetCount(count1); } #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) if (bReleaseLock) { // don't keep lock during processing context->m_lock.ReturnLock(); bReleaseLock = false; } #endif local_mesh_fragments.Append(impl_managed_fragment); local_mesh_fragments.QuickSort(ON_SubDLimitMeshImpl_CallbackContext::CompareFragmentIndex); count = local_mesh_fragments.UnsignedCount(); mesh_fragments = local_mesh_fragments.Array(); if ( count == face_fragment_count) ON_SubDLimitMeshImpl_CallbackContext::ProcessSortedFragments(count,mesh_fragments); else { // there is a bug in the code above ON_SubDIncrementErrorCount(); } #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) // If needed, get a lock before adding the processed fragments to // the linked list. if (context->m_bUseMultipleThreads) { if (false == context->m_lock.GetLock(0, ON_SleepLock::OneSecond)) { // return true to keep going, but something is really hogging the lock return ON_SUBD_RETURN_ERROR(bContinueMeshCalculation); } } #endif for ( unsigned int i = 0; i < count; i++ ) context->m_impl->AddFinishedFragment(local_mesh_fragments[i]); break; } #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) if (bReleaseLock) context->m_lock.ReturnLock(); #endif return bContinueMeshCalculation; } int ON_SubDLimitMeshImpl_CallbackContext::CompareFragmentIndex( ON_SubDLimitMeshFragment*const* a, ON_SubDLimitMeshFragment*const* b ) { if ( a == b ) return 0; // caller insures no nulls //if ( nullptr == a ) // return 1; //if ( nullptr == b ) // return -1; //unsigned int a_id = ( nullptr == (*a)->m_face ) ? ON_UNSET_UINT_INDEX : (*a)->m_face->m_id; //unsigned int b_id = ( nullptr == (*b)->m_face ) ? ON_UNSET_UINT_INDEX : (*b)->m_face->m_id; //if ( a_id < b_id ) // return -1; //if ( a_id > b_id ) // return 1; unsigned int a_index = (*a)->m_face_fragment_index; unsigned int b_index = (*b)->m_face_fragment_index; if ( a_index < b_index ) return -1; if ( a_index > b_index ) return 1; // identical values should never appear in separate entries. return ON_SUBD_RETURN_ERROR(0); } bool ON_SubDLimitMeshImpl_CallbackContext::CoincidentSideCopy( ON_SubDLimitMeshFragment* prev_fragment, ON_SubDLimitMeshFragment* fragment ) { if ( prev_fragment->m_grid.m_side_segment_count != fragment->m_grid.m_side_segment_count) return ON_SUBD_RETURN_ERROR(false); //const unsigned int side_segment_count = fragment->m_grid.m_side_segment_count; //const unsigned int* S0 = prev_fragment->m_grid.m_S + 4*side_segment_count; //const unsigned int* S1 = fragment->m_grid.m_S; //const double* P0 = prev_fragment->m_P; //double* P1 = fragment->m_P; //const double* N0 = prev_fragment->m_N; //double* N1 = fragment->m_N; // const double* src; // double* dst; //for (const unsigned int* S1max = S1 + side_segment_count; S1 < S1max; S1++, S0--) //{ // src = P0 + P0_stride * *S0; // dst = P1 + P1_stride * *S1; // // d = fabs(src[0] - dst[0]) + fabs(src[1] - dst[1]) + fabs(src[2] - dst[2]); // if (!(d <= 1e-8)) // return ON_SUBD_RETURN_ERROR(false); // *dst++ = *src++; // *dst++ = *src++; // *dst = *src; // src = N0 + N0_stride * *S0; // dst = N1 + N1_stride * *S1; // d = fabs(src[0] - dst[0]) + fabs(src[1] - dst[1]) + fabs(src[2] - dst[2]); // if (!(d <= 0.01)) // return ON_SUBD_RETURN_ERROR(false); // *dst++ = *src++; // *dst++ = *src++; // *dst = *src; //} const size_t side_point_count = 1U + (unsigned int)(fragment->m_grid.m_side_segment_count); const double* P0 = prev_fragment->m_P; const double* N0 = prev_fragment->m_N; const size_t P0_stride = side_point_count*prev_fragment->m_P_stride; const size_t N0_stride = side_point_count*prev_fragment->m_N_stride; double* P1 = fragment->m_P; double* N1 = fragment->m_N; const size_t P1_stride = fragment->m_P_stride; const size_t N1_stride = fragment->m_N_stride; double* P1max = P1 + side_point_count*P1_stride; while ( P1 < P1max ) { double d = fabs(P0[0] - P1[0]) + fabs(P0[1] - P1[1]) + fabs(P0[2] - P1[2]); if (!(d <= 1e-8)) return ON_SUBD_RETURN_ERROR(false); P1[0] = P0[0]; P1[1] = P0[1]; P1[2] = P0[2]; P0 += P0_stride; P1 += P1_stride; d = fabs(N0[0] - N1[0]) + fabs(N0[1] - N1[1]) + fabs(N0[2] - N1[2]); if (!(d <= 0.01)) return ON_SUBD_RETURN_ERROR(false); N1[0] = N0[0]; N1[1] = N0[1]; N1[2] = N0[2]; N0 += N0_stride; N1 += N1_stride; } return true; } bool ON_SubDLimitMeshImpl_CallbackContext::ProcessSortedFragments( unsigned int count, ON_SubDLimitMeshFragment** mesh_fragments ) { if ( count < 2 || nullptr == mesh_fragments) return ON_SUBD_RETURN_ERROR(false); if ( nullptr == mesh_fragments[0] || nullptr == mesh_fragments[0]->m_face || nullptr == mesh_fragments[0]->m_P || nullptr == mesh_fragments[0]->m_N || 0 != mesh_fragments[0]->m_face_fragment_index || count != mesh_fragments[0]->m_face_fragment_count ) return ON_SUBD_RETURN_ERROR(false); const unsigned int face_id = mesh_fragments[0]->m_face->m_id; const unsigned int grid_F_count = mesh_fragments[0]->m_grid.m_F_count; const unsigned int grid_side_count = mesh_fragments[0]->m_grid.m_side_segment_count; if ( 0 == face_id || ON_UNSET_UINT_INDEX == face_id ) return ON_SUBD_RETURN_ERROR(false); if ( 0 == grid_F_count || grid_side_count*grid_side_count != grid_F_count ) return ON_SUBD_RETURN_ERROR(false); if ( nullptr == mesh_fragments[count-1] || nullptr == mesh_fragments[count-1]->m_face || nullptr == mesh_fragments[count-1]->m_P || nullptr == mesh_fragments[count-1]->m_N || (count-1) != mesh_fragments[count-1]->m_face_fragment_index || count != mesh_fragments[count-1]->m_face_fragment_count || face_id != mesh_fragments[count-1]->m_face->m_id || grid_F_count != mesh_fragments[count-1]->m_grid.m_F_count || grid_side_count != mesh_fragments[count-1]->m_grid.m_side_segment_count ) return ON_SUBD_RETURN_ERROR(false); ON_SubDLimitMeshFragment* fragment = mesh_fragments[count-1]; for (unsigned int i = 0; i < count; i++) { ON_SubDLimitMeshFragment* prev_fragment = fragment; fragment = mesh_fragments[i]; if (i > 0) { if (nullptr == fragment || nullptr == fragment->m_face || face_id != fragment->m_face->m_id || grid_F_count != fragment->m_grid.m_F_count || grid_side_count != fragment->m_grid.m_side_segment_count || i != fragment->m_face_fragment_index || count != fragment->m_face_fragment_count || nullptr == fragment->m_P || nullptr == fragment->m_N ) return ON_SUBD_RETURN_ERROR(false); } if ( false == ON_SubDLimitMeshImpl_CallbackContext::CoincidentSideCopy(prev_fragment,fragment) ) return ON_SUBD_RETURN_ERROR(false); } return true; } #if defined(ON_HAS_RVALUEREF) ON_SubDLimitMesh::ON_SubDLimitMesh( ON_SubDLimitMesh&& src) ON_NOEXCEPT : m_impl_sp(std::move(src.m_impl_sp)) {} ON_SubDLimitMesh& ON_SubDLimitMesh::operator=(ON_SubDLimitMesh&& src) { if (this != &src) { m_impl_sp.reset(); m_impl_sp = std::move(src.m_impl_sp); } return *this; } #endif ON_SubDDisplayParameters ON_SubDLimitMesh::DisplayParameters() const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) { ON_SubDDisplayParameters dp; dp.m_display_density = impl->m_display_density; return dp; } return ON_SubDDisplayParameters::Empty; } unsigned int ON_SubDLimitMesh::DisplayDensity() const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); return (nullptr != impl) ? impl->m_display_density : 0; } unsigned int ON_SubDLimitMesh::FragmentCount() const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); return (nullptr != impl) ? impl->m_fragment_count : 0; } const ON_SubDLimitMeshFragment* ON_SubDLimitMesh::FirstFragment() const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); return (nullptr != impl) ? impl->m_first_fragment : nullptr; } bool ON_SubDLimitMesh::Update( bool bShareUpdate ) { return false; } bool ON_SubDLimitMesh::IsEmpty() const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); return (nullptr == impl || 0 == impl->m_fragment_count ); } const ON_RTree& ON_SubDLimitMesh::FragmentTree() const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) return impl->FragmentTree(); return ON_RTree::Empty; } ON_BoundingBox ON_SubDLimitMesh::BoundingBox() const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) return impl->m_bbox; return ON_BoundingBox::EmptyBoundingBox; } ON_SubD ON_SubDLimitMesh::SubD() const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); if ( nullptr == impl ) return ON_SubD::Empty; ON_SubD subd; subd.ShareDimple(*impl); return subd; } ON_SubDRef ON_SubDLimitMesh::SubDRef() const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); if ( nullptr == impl ) return ON_SubDRef::Empty; ON_SubDRef subd_ref; ON_SubD& subd = subd_ref.NewSubD(); subd.ShareDimple(*impl); return subd_ref; } bool ON_SubDLimitMesh::GetTightBoundingBox( ON_BoundingBox& bbox, bool bGrowBox, const ON_Xform* xform ) const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) return impl->GetTightBoundingBox(bbox,bGrowBox,xform); return (bGrowBox && bbox.IsValid()); } ON_SubD::FacetType ON_SubDLimitMesh::GridType() const { const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) return impl->m_facet_type; return ON_SubD::FacetType::Unset; } void ON_SubDLimitMesh::Clear() { m_impl_sp.reset(); } void ON_SubDLimitMesh::ClearTree() { ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) impl->ClearTree(); } unsigned int ON_SubDLimitMesh::ContentSerialNumber() const { ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); return (nullptr != impl) ? impl->m_limit_mesh_content_serial_number : 0; } ON_SubDLimitMesh* ON_SubDLimitMesh::Create( const ON_SubD& subd, const class ON_SubDDisplayParameters& limit_mesh_parameters, ON_SubDLimitMesh* destination_mesh ) { ON_SubDFaceIterator fit = subd.FaceIterator(); return ON_SubDLimitMesh::Create(fit,limit_mesh_parameters,destination_mesh); } ON_SubDLimitMesh& ON_SubDLimitMesh::CopyFrom( const ON_SubDLimitMesh& src ) { if (this != &src) { m_impl_sp.reset(); const ON_SubDLimitMeshImpl* src_impl = src.m_impl_sp.get(); if (nullptr != src_impl) { ON_SubDLimitMeshImpl* impl = new ON_SubDLimitMeshImpl(*src_impl); std::shared_ptr< ON_SubDLimitMeshImpl > new_impl_sp(impl); m_impl_sp.swap(new_impl_sp); } } return *this; } void ON_SubDLimitMesh::Swap( ON_SubDLimitMesh& a, ON_SubDLimitMesh& b ) { if (&a == &ON_SubDLimitMesh::Empty || &b == &ON_SubDLimitMesh::Empty) { ON_SubDIncrementErrorCount(); } else { std::swap(a.m_impl_sp, b.m_impl_sp); } } ON_SubDLimitMesh* ON_SubDLimitMesh::Create( ON_SubDFaceIterator fit, const class ON_SubDDisplayParameters& limit_mesh_parameters, ON_SubDLimitMesh* destination_mesh ) { ON_SubDLimitMeshImpl* impl = 0; std::shared_ptr< ON_SubDLimitMeshImpl > impl_sp; if (nullptr != destination_mesh) { destination_mesh->Clear(); std::shared_ptr< ON_SubDLimitMeshImpl > dest_sp = destination_mesh->m_impl_sp; if (1 == dest_sp.use_count()) { impl_sp.swap(dest_sp); impl = impl_sp.get(); } } if ( limit_mesh_parameters.m_display_density > ON_SubDLimitMesh::MaximumDisplayDensity ) return ON_SUBD_RETURN_ERROR(nullptr); const ON_SubD& subd = fit.SubD(); ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd.ActiveLevelSubDType()); const unsigned int subd_fragment_count = fit.LimitSurfaceMeshFragmentCount(facet_type); if ( 0 == subd_fragment_count ) return ON_SUBD_RETURN_ERROR(nullptr); if (nullptr == impl) { impl = new(std::nothrow) ON_SubDLimitMeshImpl(); if ( nullptr == impl) return ON_SUBD_RETURN_ERROR(nullptr); std::shared_ptr< ON_SubDLimitMeshImpl > new_impl_sp(impl); impl_sp.swap(new_impl_sp); } if (false == impl->ReserveCapacity(subd_fragment_count,facet_type,limit_mesh_parameters.m_display_density)) return ON_SUBD_RETURN_ERROR(nullptr); ON_SubDLimitMeshImpl_CallbackContext callback_context; #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) callback_context.m_bUseMultipleThreads = limit_mesh_parameters.m_bUseMultipleThreads; #endif callback_context.m_impl = impl_sp.get(); subd.GetLimitSurfaceMeshInFragments( limit_mesh_parameters, (ON__UINT_PTR)&callback_context, ON_SubDLimitMeshImpl_CallbackContext::FragmentCallbackFunction ); if (ON_Terminator::TerminationRequested(limit_mesh_parameters.m_terminator)) { return nullptr; // not an error - outside intervention canceled meshing } if (impl->m_fragment_count < 1) return ON_SUBD_RETURN_ERROR(nullptr); ON_SubDLimitMesh* limit_mesh = (nullptr != destination_mesh) ? destination_mesh : new ON_SubDLimitMesh(); // The next three lines are conceptually the same as // impl_sp->m_subdimple_sp = fit.SubD().m_subdimple_sp; // The three line approach is required because ON_SubD::m_subdimple_sp is private. { ON_SubD tmp_sub; tmp_sub.ShareDimple(fit.SubD()); // tmp_sub.m_subdimple_sp = fit.SubD().m_subdimple_sp (increments fit.SubD().m_subdimple_sp ref count) // NOTE: // There are at least std::shared_ptr references to the ON_SubDimple (fit and tmp_subd), // This insures the std::weak_ptr on impl_sp will be valid. tmp_sub.SwapDimple(*impl_sp); // swap impl_sp->m_subdimple_wp and tmp_sub.m_subdimple_sp } // Let limit_mesh manage the contents limit_mesh->m_impl_sp.swap(impl_sp); // return the new mesh return limit_mesh; } ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensity( unsigned int display_density ) { if ( display_density > ON_SubDLimitMesh::MaximumDisplayDensity ) return ON_SUBD_RETURN_ERROR(ON_SubDDisplayParameters::Empty); ON_SubDDisplayParameters limit_mesh_parameters; limit_mesh_parameters.m_display_density = display_density; return limit_mesh_parameters; } #endif