Files
opennurbs/opennurbs_subd_fragment.cpp
2019-04-09 10:44:41 -07:00

1798 lines
52 KiB
C++

#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<double*>(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<unsigned int*>(grid.m_F),
grid_S_capacity,
1U,
const_cast<unsigned int*>(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<ON_SubDLimitMeshSealEdgeInfo> 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