#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 unsigned int grid_side_count = 4; // will be 3 for a tri sub-D if (nullptr != m_face && m_face->m_edge_count >= 3 && 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); fei = m_face_vertex_index[(grid_side_index+1) % grid_side_count]; if (fei < m_face->m_edge_count) return m_face->EdgePtr((fei+m_face->m_edge_count-1) % m_face->m_edge_count); } return ON_SubDEdgePtr::Null; } const class ON_SubDVertex* ON_SubDLimitMeshFragment::Vertex( unsigned int grid_corner_index ) const { if (nullptr != m_face && m_face->m_edge_count >= 3 && grid_corner_index < 4) { const unsigned short fei = m_face_vertex_index[grid_corner_index]; if (fei < m_face->m_edge_count) return m_face->Vertex(fei); } return nullptr; } const 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::NanPoint; const unsigned int i = m_grid.m_S[grid_corner_index * m_grid.m_side_segment_count]; return ON_3dPoint(m_P + (i*m_P_stride)); } const ON_3dVector ON_SubDLimitMeshFragment::CornerNormal(unsigned int grid_corner_index) const { if ( grid_corner_index >= 4 || nullptr == m_N || m_N_stride <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::NanPoint; const unsigned int i = m_grid.m_S[grid_corner_index * m_grid.m_side_segment_count]; return ON_3dVector(m_N + (i*m_N_stride)); } const ON_3dPoint ON_SubDLimitMeshFragment::SidePoint(unsigned int grid_side_index) const { if ( grid_side_index >= 4 || nullptr == m_P || m_P_stride <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::NanPoint; const unsigned int i = grid_side_index*m_grid.m_side_segment_count + m_grid.m_side_segment_count/2; return ON_3dPoint(m_P + (i*m_P_stride)); } const ON_3dVector ON_SubDLimitMeshFragment::SideNormal(unsigned int grid_side_index) const { if ( grid_side_index >= 4 || nullptr == m_N || m_N_stride <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::NanPoint; const unsigned int i = grid_side_index*m_grid.m_side_segment_count + m_grid.m_side_segment_count/2; return ON_3dVector(m_N + (i*m_N_stride)); } const ON_3dPoint ON_SubDLimitMeshFragment::CenterPoint() const { if ( nullptr == m_P || m_P_stride <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::NanPoint; const unsigned int i = (m_grid.m_side_segment_count*(m_grid.m_side_segment_count + 2)) / 2; return ON_3dPoint(m_P + (i*m_P_stride)); } const ON_3dVector ON_SubDLimitMeshFragment::CenterNormal() const { if ( nullptr == m_N || m_N_stride <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::NanPoint; const unsigned int i = (m_grid.m_side_segment_count*(m_grid.m_side_segment_count + 2)) / 2; return ON_3dVector(m_N + (i*m_N_stride)); } bool ON_SubDLimitMeshFragment::Internal_GetFrameHelper( unsigned int P_dex, unsigned int Q_dex, ON_Plane& frame ) const { const unsigned int P_count = m_P_count; if ( P_dex < P_count && Q_dex < P_count && nullptr != m_P && m_P_stride > 0 && nullptr != m_N && m_N_stride > 0 ) { const ON_3dVector Z(m_N + (P_dex*m_N_stride)); const ON_3dPoint P(m_P + (P_dex*m_P_stride)); const ON_3dPoint Q (m_P + (Q_dex*m_P_stride)); ON_3dVector V = (Q - P).UnitVector(); ON_3dVector X = (V - (frame.zaxis*V)*V).UnitVector(); if (X.IsUnitVector()) { frame.origin = P; frame.xaxis = X; frame.yaxis = ON_CrossProduct(Z, X); frame.zaxis = Z; frame.UpdateEquation(); } else { frame = ON_Plane(P, Z); } return true; } return false; } bool ON_SubDLimitMeshFragment::GetCornerFrame( unsigned int grid_corner_index, ON_Plane& frame ) const { if (grid_corner_index < 4 && m_grid.m_side_segment_count > 0 && nullptr != m_grid.m_S) { unsigned int S_dex = grid_corner_index * m_grid.m_side_segment_count; if (Internal_GetFrameHelper(m_grid.m_S[S_dex], m_grid.m_S[S_dex + 1], frame)) return true; } frame = ON_Plane::NanPlane; return false; } bool ON_SubDLimitMeshFragment::GetSideFrame(unsigned int grid_side_index, ON_Plane & frame) const { const unsigned int count = m_grid.m_side_segment_count; if (grid_side_index < 4U && count > 0 && nullptr != m_grid.m_S) { const unsigned int S_dex = grid_side_index * count + count / 2U; const unsigned int S_dex1 = (S_dex < (4U * count)) ? (S_dex + 1U) : (S_dex - 1U); if (Internal_GetFrameHelper(m_grid.m_S[S_dex], m_grid.m_S[S_dex1], frame)) return true; } frame = ON_Plane::NanPlane; return false; } bool ON_SubDLimitMeshFragment::GetCenterFrame(ON_Plane & frame) const { if (m_grid.m_side_segment_count > 0 && nullptr != m_grid.m_S) { const unsigned int n = m_grid.m_side_segment_count; const unsigned int P_dex = (n * (n + 2))/2; // central point index if (Internal_GetFrameHelper(P_dex, P_dex+1, frame)) return true; } frame = ON_Plane::NanPlane; return false; } 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::IsPartialFragment() const { return ( nullptr != m_face && m_face->m_edge_count < ON_SubDFace::MaximumEdgeCount && m_face_vertex_index[0] >= ON_SubDFace::MaximumEdgeCount && m_face_vertex_index[1] >= ON_SubDFace::MaximumEdgeCount && m_face_vertex_index[2] < m_face->m_edge_count && m_face_vertex_index[3] >= ON_SubDFace::MaximumEdgeCount && m_grid.m_side_segment_count > 0 && nullptr != m_grid.m_S ); } bool ON_SubDLimitMeshFragment::IsCompleteFragment() const { // For a complete fragment, // m_face_vertex_index[i] = {0,1,2,3} for quads // m_face_vertex_index[i] = {0,1,2,?} for tris return ( nullptr != m_face && m_face->m_edge_count < ON_SubDFace::MaximumEdgeCount && m_face_vertex_index[0] < m_face->m_edge_count && m_face_vertex_index[1] < m_face->m_edge_count && m_face_vertex_index[2] < m_face->m_edge_count && m_grid.m_side_segment_count > 0 && nullptr != m_grid.m_S ); } 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; } // 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); // 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 ( bReturnLock ) lock.ReturnLock(); 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( bool bResetSubDWeakPtr ) { // When the ON_SubDimple that manages the faces referenced by // fragment->m_face is deleted, fragment->m_face must be set to zero. if (bResetSubDWeakPtr) m_subdimple_wp.reset(); 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; } ////class ON_SubDLimitMeshSealVertexInfo ////{ ////public: //// ON_SubDLimitMeshSealVertexInfo() = default; //// ~ON_SubDLimitMeshSealVertexInfo() = default; //// ON_SubDLimitMeshSealVertexInfo(const ON_SubDLimitMeshSealVertexInfo&) = default; //// ON_SubDLimitMeshSealVertexInfo& operator=(const ON_SubDLimitMeshSealVertexInfo&) = default; //// //// static const ON_SubDLimitMeshSealVertexInfo Unset; //// //// enum Bits : unsigned char //// { //// // Set if the vertex is smooth and normals should be sealed along with location. //// SmoothVertex = 0x01, //// //// // Set if the edge is smooth and normals should be sealed along with location. //// SmoothEdge = 0x02, //// }; //// ////public: //// unsigned int m_vertex_id = 0; //// unsigned int m_edge_id = 0; //// unsigned char m_bits = 0; //// double* m_P = nullptr; //// double* m_N = nullptr; ////}; ////const ON_SubDLimitMeshSealVertexInfo ON_SubDLimitMeshSealVertexInfo::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDLimitMeshSealVertexInfo); class ON_SubDLimitMeshSealEdgeInfo { public: ON_SubDLimitMeshSealEdgeInfo() = default; ~ON_SubDLimitMeshSealEdgeInfo() = default; ON_SubDLimitMeshSealEdgeInfo(const ON_SubDLimitMeshSealEdgeInfo&) = default; ON_SubDLimitMeshSealEdgeInfo& operator=(const ON_SubDLimitMeshSealEdgeInfo&) = default; static const ON_SubDLimitMeshSealEdgeInfo Unset; enum Bits : unsigned char { // Set if edge orientation is opposite face boundary ccw orientation. EdgeDir = 0x01, // Set if the edge is smooth and normals should be sealed along with location. Smooth = 0x02, // The high bits are used for half edges so they will sort after full edges FirstHalf = 0x40, SecondHalf = 0x80, HalfMask = 0xC0, // Used to ignore edge dir IgnoreEdgeDirMask = 0xFE }; bool SetEdge( unsigned int grid_side_dex ) { for (;;) { if (nullptr == m_fragment || nullptr == m_fragment->m_face) break; m_face_edge_count = m_fragment->m_face->m_edge_count; if (m_face_edge_count < 3 || m_face_edge_count > ON_SubDFace::MaximumEdgeCount) break; // bogus face const ON_SubDEdgePtr eptr = m_fragment->EdgePtr(grid_side_dex); const ON_SubDEdge* edge = eptr.Edge(); m_edge_id = (nullptr != edge && edge->m_face_count >= 2) ? edge->m_id : 0; if (0 == m_edge_id) break; // nothing to seal const bool bCompleteFragment = m_fragment->IsCompleteFragment(); const bool bPartialFragment = (false == bCompleteFragment) && m_fragment->m_face_vertex_index[0] > ON_SubDFace::MaximumEdgeCount && m_fragment->m_face_vertex_index[1] > ON_SubDFace::MaximumEdgeCount && m_fragment->m_face_vertex_index[2] < ON_SubDFace::MaximumEdgeCount && m_fragment->m_face_vertex_index[3] > ON_SubDFace::MaximumEdgeCount ; if (false == bCompleteFragment && false == bPartialFragment) { ON_SUBD_ERROR("Unexpected m_face_vertex[] falues in partial fragment."); break; } m_bits = 0; const ON__UINT_PTR edge_dir = eptr.EdgeDirection(); if (bPartialFragment) { // The high bit is used for partial fragments so they will sort after full fragments. if ( 1 == grid_side_dex ) m_bits |= (0==edge_dir) ? Bits::SecondHalf : Bits::FirstHalf; else if ( 2 == grid_side_dex ) m_bits |= (0==edge_dir) ? Bits::FirstHalf : Bits::SecondHalf; else { // this is an inteior edge of a partial fragment and it // is alwasy sealed with its neighbor when it is created. break; } } if (0 != edge_dir) m_bits |= Bits::EdgeDir; if (edge->IsSmooth()) m_bits |= Bits::Smooth; m_grid_side_dex = (unsigned char)grid_side_dex; // 0,1,2, or 3 return true; } m_edge_id = 0; m_bits = 0; m_grid_side_dex = 0; m_face_edge_count = 0; return false; } static bool Seal( const ON_SubDLimitMeshSealEdgeInfo& src, const ON_SubDLimitMeshSealEdgeInfo& dst ); static int CompareEdgeIdBitsFaceId( const ON_SubDLimitMeshSealEdgeInfo* lhs, const ON_SubDLimitMeshSealEdgeInfo* rhs ) { // sort by edge id if (lhs->m_edge_id < rhs->m_edge_id) return -1; if (lhs->m_edge_id > rhs->m_edge_id) return 1; // then sort by bits with full edges before half edges // Critical to ignore the EdgeDir bit because we need // the identical sections of edges to be sorted together. // When an edge has 2 N-gons (N != 4) attached, we need // the pairs for the first half and 2nd half sorted toether const unsigned char lhs_bits = (lhs->m_bits & ON_SubDLimitMeshSealEdgeInfo::Bits::IgnoreEdgeDirMask); const unsigned char rhs_bits = (rhs->m_bits & ON_SubDLimitMeshSealEdgeInfo::Bits::IgnoreEdgeDirMask); if (lhs_bits < rhs_bits) return -1; if (lhs_bits > rhs_bits) return 1; // then sort by face id unsigned int lhs_face_id = (nullptr != lhs->m_fragment->m_face) ? lhs->m_fragment->m_face->m_id : 0xFFFFFFFF; unsigned int rhs_face_id = (nullptr != rhs->m_fragment->m_face) ? rhs->m_fragment->m_face->m_id : 0xFFFFFFFF; if (0 == lhs_face_id) lhs_face_id = 0xFFFFFFFE; if (0 == rhs_face_id) rhs_face_id = 0xFFFFFFFE; if (lhs_face_id < rhs_face_id) return -1; if (lhs_face_id > rhs_face_id) return 1; return 0; } public: unsigned int m_edge_id = 0; // m_bits is bitwise or of ON_SubDLimitMeshSealEdgeInfo::Bits enum values unsigned char m_bits = 0; unsigned char m_grid_side_dex = 0; // 0,1,2,or 3 unsigned short m_face_edge_count = 0; ON_SubDLimitMeshFragment* m_fragment = nullptr; }; const ON_SubDLimitMeshSealEdgeInfo ON_SubDLimitMeshSealEdgeInfo::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDLimitMeshSealEdgeInfo); bool ON_SubDLimitMeshSealEdgeInfo::Seal( const ON_SubDLimitMeshSealEdgeInfo& src, const ON_SubDLimitMeshSealEdgeInfo& dst ) { if (src.m_edge_id != dst.m_edge_id || 0 == src.m_edge_id) return false; if (nullptr == src.m_fragment || nullptr == dst.m_fragment) return false; const bool bSealNormals = (0 != (src.m_bits & ON_SubDLimitMeshSealEdgeInfo::Bits::Smooth)); const unsigned char src_half = (src.m_bits & ON_SubDLimitMeshSealEdgeInfo::Bits::HalfMask); const unsigned char dst_half = (dst.m_bits & ON_SubDLimitMeshSealEdgeInfo::Bits::HalfMask); unsigned int src_side_segment_count = src.m_fragment->m_grid.m_side_segment_count; unsigned int dst_side_segment_count = dst.m_fragment->m_grid.m_side_segment_count; unsigned int i0 = src.m_grid_side_dex*src_side_segment_count; unsigned int i1 = i0 + src_side_segment_count; ////unsigned int vid[2] = {}; ////const ON_SubDEdge* e = src.m_fragment->Edge(src.m_grid_side_dex); ////if (nullptr != e ) ////{ //// const ON_SubDVertex* v = src.m_fragment->Vertex(src.m_grid_side_dex); //// if ( nullptr != v ) //// vid[0] = v->m_id; //// v = src.m_fragment->Vertex((src.m_grid_side_dex+1)%4); //// if ( nullptr !=v ) //// vid[1] = v->m_id; ////} // src_dir = 0 if SubD edge and fragment side have compatible natural orientations const unsigned char src_dir = (src.m_bits&ON_SubDLimitMeshSealEdgeInfo::Bits::EdgeDir); if (src_half != dst_half || src_side_segment_count != dst_side_segment_count) { if ( 0 == src_half && 4 == src.m_face_edge_count && 4 != dst.m_face_edge_count && 2 * dst_side_segment_count == src_side_segment_count) { // The face for src_half is a quad and the face for dest_half is an N-gon with N != 3, // and src_fragment is a full sized fragment and dst_fragment is a half sized fragment. if (ON_SubDLimitMeshSealEdgeInfo::Bits::FirstHalf == dst_half) { // only copy half of src_fragment side if (0 == src_dir) { i1 -= dst_side_segment_count; // copy first half of src_framgent side ////vid[1] = 0; } else { i0 += dst_side_segment_count; // copy 2nd half of src_framgent side ////vid[0] = 0; } } else if (ON_SubDLimitMeshSealEdgeInfo::Bits::SecondHalf == dst_half) { // only copy half of src_fragment side if (0 == src_dir) { i0 += dst_side_segment_count; // copy 2nd half of src_framgent side ////vid[0] = 0; } else { i1 -= dst_side_segment_count; // copy first half of src_framgent side ////vid[1] = 0; } } else { // bug in this code or the code that sets the m_bits information. ON_SUBD_ERROR("unexpected dst_half"); return false; } } else { // Either the parent subd is invalid or information // set in the fragments, or the sorting in ON_SubDLimitMeshImpl::SealEdges() // is not valid (or some other bug). ON_SUBD_ERROR("unexpected sealing fragment portions"); return false; } } // seal this edge const bool bSameDirection = (src_dir == (dst.m_bits&ON_SubDLimitMeshSealEdgeInfo::Bits::EdgeDir)); const unsigned int j0 = dst.m_grid_side_dex*dst_side_segment_count + (bSameDirection?0:dst_side_segment_count); const unsigned int j1 = bSameDirection ? (j0+dst_side_segment_count) : (j0-dst_side_segment_count); ON_SubDLimitMeshFragment::SealAdjacentSides( true, // bTestNearEqual, bSealNormals, *src.m_fragment, i0, i1, *dst.m_fragment, j0, j1 ); return true; } void ON_SubDLimitMeshImpl::SealEdges() { ON_SimpleArray fe_list(m_fragment_count); ON_SubDLimitMeshSealEdgeInfo fe; for (fe.m_fragment = m_first_fragment; nullptr != fe.m_fragment; fe.m_fragment = fe.m_fragment->m_next_fragment) { if (nullptr == fe.m_fragment->m_face) continue; for (unsigned int grid_side_dex = 0; grid_side_dex < 4; grid_side_dex++) { if ( fe.SetEdge(grid_side_dex) ) fe_list.Append(fe); } } fe_list.QuickSort(ON_SubDLimitMeshSealEdgeInfo::CompareEdgeIdBitsFaceId); const unsigned int fe_list_count = fe_list.UnsignedCount(); unsigned int i0 = 0; while ( i0 < fe_list_count ) { fe = fe_list[i0]; const unsigned char src_half_mask = (fe.m_bits & ON_SubDLimitMeshSealEdgeInfo::Bits::HalfMask); for ( i0++; i0 < fe_list_count && fe.m_edge_id == fe_list[i0].m_edge_id; i0++ ) { if (0 != src_half_mask && 0 == (src_half_mask & fe_list[i0].m_bits)) break; // necessary when all faces attached to an edge are not quads. ON_SubDLimitMeshSealEdgeInfo::Seal(fe, fe_list[i0]); } } } 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; } #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; } const ON_SubDLimitMeshFragment* ON_SubDLimitMesh::FaceFragment( const class ON_SubDFace* face ) const { if (nullptr == face) return nullptr; for (const ON_SubDLimitMeshFragment* fragment = FirstFragment(); nullptr != fragment; fragment = fragment->m_next_fragment) { if (face == fragment->m_face) return fragment; } return nullptr; } bool ON_SubDLimitMesh::GetFaceCenterPointAndNormal( const class ON_SubDFace* face, double* P, double* N ) const { if (nullptr != P) { P[0] = ON_DBL_QNAN; P[1] = ON_DBL_QNAN; P[2] = ON_DBL_QNAN; } if (nullptr != N) { N[0] = ON_DBL_QNAN; N[1] = ON_DBL_QNAN; N[2] = ON_DBL_QNAN; } const ON_SubDLimitMeshFragment* fragment = FaceFragment(face); if (nullptr == fragment) return false; if (nullptr == fragment->m_P || nullptr == fragment->m_N) return false; const unsigned int n = fragment->m_grid.m_side_segment_count; const unsigned int P_dex = fragment->IsCompleteFragment() ? (n*(n + 2) / 2) : 0; if (P_dex >= (unsigned int)fragment->m_P_count) return nullptr; const double* fragment_P = fragment->m_P + (P_dex * fragment->m_P_stride); const double* fragment_N = fragment->m_N + (P_dex * fragment->m_N_stride); if (nullptr != P) { P[0] = fragment_P[0]; P[1] = fragment_P[1]; P[2] = fragment_P[2]; } if (nullptr != N) { N[0] = fragment_N[0]; N[1] = fragment_N[1]; N[2] = fragment_N[2]; } return true; } bool ON_SubDLimitMesh::GetEdgeCenterPointAndNormal( const class ON_SubDEdge* edge, unsigned int edge_face_index, double* P, double* N ) const { if (nullptr != P) { P[0] = ON_DBL_QNAN; P[1] = ON_DBL_QNAN; P[2] = ON_DBL_QNAN; } if (nullptr != N) { N[0] = ON_DBL_QNAN; N[1] = ON_DBL_QNAN; N[2] = ON_DBL_QNAN; } if (nullptr == edge) return false; const ON_SubDFace* face = edge->Face(edge_face_index); if (nullptr == face) return false; const unsigned int fei = face->EdgeArrayIndex(edge); if (fei >= face->EdgeCount()) return false; unsigned int P_dex = ON_UNSET_UINT_INDEX; bool bIsPartialFragment = false; const ON_SubDLimitMeshFragment* fragment = nullptr; for ( fragment = FaceFragment(face); nullptr != fragment && fragment->m_face == face; fragment = bIsPartialFragment ? fragment->m_next_fragment : nullptr ) { bIsPartialFragment = fragment->IsPartialFragment(); for (unsigned int grid_side_index = 0; grid_side_index < 4; grid_side_index++) { const ON_SubDEdge* grid_side_edge = fragment->Edge(grid_side_index); if (edge != grid_side_edge) continue; const unsigned int n = fragment->m_grid.m_side_segment_count; if (n <= 0 || nullptr == fragment->m_grid.m_S) break; if (bIsPartialFragment) { const ON_SubDVertex* v = fragment->Vertex(grid_side_index); if (nullptr != v) { const unsigned int evi = (0 == face->EdgeDirection(fei)) ? 0U : 1U; if (v == edge->Vertex(evi)) grid_side_index++; P_dex = fragment->m_grid.m_S[grid_side_index*n]; } } else if (fragment->IsCompleteFragment()) { P_dex = fragment->m_grid.m_S[grid_side_index*n + n / 2]; } break; } if (false == bIsPartialFragment || ON_UNSET_UINT_INDEX != P_dex) break; } if (nullptr == fragment) return false; if (P_dex >= (unsigned int)fragment->m_P_count) return false; const double* fragment_P = fragment->m_P + (P_dex * fragment->m_P_stride); const double* fragment_N = fragment->m_N + (P_dex * fragment->m_N_stride); if (nullptr != P) { P[0] = fragment_P[0]; P[1] = fragment_P[1]; P[2] = fragment_P[2]; } if (nullptr != N) { N[0] = fragment_N[0]; N[1] = fragment_N[1]; N[2] = fragment_N[2]; } return true; } 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::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_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