mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-06 23:15:54 +08:00
1798 lines
52 KiB
C++
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
|