Files
opennurbs/opennurbs_subd_limit.cpp
Will Pearson 5f462fed0d Update source to v6.8.18240.20051
Previous source was actually for 6.1...
2018-09-15 11:26:15 -07:00

3760 lines
121 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)
bool ON_SubDQuadNeighborhood::IsValid() const
{
unsigned int count = 0;
for (unsigned int i = 0; i < 4; i++)
{
if (m_bExtraordinaryCornerVertex[i])
count++;
}
if (count != m_extraordinary_corner_vertex_count)
return ON_SUBD_RETURN_ERROR(false);
count = 0;
for (unsigned int i = 0; i < 4; i++)
{
if (m_bExactQuadrantPatch[i])
{
if (m_bExtraordinaryCornerVertex[i] )
return ON_SUBD_RETURN_ERROR(false);
count++;
}
}
if (count != m_exact_quadrant_patch_count)
return ON_SUBD_RETURN_ERROR(false);
count = 0;
for (unsigned int i = 0; i < 4; i++)
{
if (m_bBoundaryCrease[i])
{
//if ( m_bDartCrease[i] )
// return ON_SUBD_RETURN_ERROR(false);
count++;
}
}
if (count != m_boundary_crease_count)
return ON_SUBD_RETURN_ERROR(false);
//count = 0;
//for (unsigned int i = 0; i < 4; i++)
//{
// if (m_bDartCrease[i])
// count++;
//}
//if (count != m_dart_crease_count)
// return ON_SUBD_RETURN_ERROR(false);
const ON_SubDFace* quad_face = m_face_grid[1][1];
if (nullptr == quad_face || 4 != quad_face->m_edge_count)
return ON_SUBD_RETURN_ERROR(false);
int quad_fvi0 = 0;
int quad_dir = 0;
{
for (quad_fvi0 = 0; quad_fvi0 < 4; quad_fvi0++)
{
const ON_SubDVertex* v = quad_face->Vertex(quad_fvi0);
if ( nullptr == v )
return ON_SUBD_RETURN_ERROR(false);
if (m_vertex_grid[1][1] == v)
{
v = quad_face->Vertex((quad_fvi0+1)%4);
if ( nullptr == v )
return ON_SUBD_RETURN_ERROR(false);
if (m_vertex_grid[2][1] == v)
quad_dir = 1;
else if (m_vertex_grid[1][2] == v)
quad_dir = -1;
else
return ON_SUBD_RETURN_ERROR(false);
break;
}
}
if (0 == quad_dir || quad_fvi0 >= 4)
return ON_SUBD_RETURN_ERROR(false);
}
const ON_2dex face_corner_dex[4] = { { 0, 0 }, { 2, 0 }, { 2, 2 }, { 0, 2 } };
const ON_2dex face_side_dex[4] = { { 1, 0 }, { 2, 1 }, { 1, 2 }, { 0, 1 } };
const ON_2dex grid_quad_vertex_dex[4] = { { 1,1 }, { 2,1 }, { 2,2 }, { 1,2 } };
const ON_2dex grid_corner_dex[4] = { { 0, 0 }, { 3, 0 }, { 3, 3 }, { 0, 3 } };
const ON_2dex grid_side_dex[4][2] = { {{ 1, 0 }, { 2, 0 }}, {{ 3, 1 }, { 3, 2 }}, {{ 2, 3 }, { 1, 3 }}, {{ 0, 2 }, { 0, 1 }} };
ON_2dex dex;
const int fvi_map[4] = {
quad_fvi0,
(quad_fvi0+4+quad_dir)%4,
(quad_fvi0+4+2*quad_dir)%4,
(quad_fvi0+4+3*quad_dir)%4};
const ON_SubDVertex* quad_vertex[4] = {
quad_face->Vertex(fvi_map[0]),
quad_face->Vertex(fvi_map[1]),
quad_face->Vertex(fvi_map[2]),
quad_face->Vertex(fvi_map[3]) };
const ON_SubDEdgePtr quad_eptr[4] = {
quad_face->m_edge4[fvi_map[0]],
quad_face->m_edge4[fvi_map[1]],
quad_face->m_edge4[fvi_map[2]],
quad_face->m_edge4[fvi_map[3]] };
const ON_SubDEdge* quad_edge[4] = {
quad_eptr[0].Edge(),
quad_eptr[1].Edge(),
quad_eptr[2].Edge(),
quad_eptr[3].Edge() };
for (unsigned int fi = 0; fi < 4; fi++)
{
if (nullptr == quad_edge[fi])
return ON_SUBD_RETURN_ERROR(false);
if (nullptr == quad_edge[fi]->m_vertex[0])
return ON_SUBD_RETURN_ERROR(false);
if (nullptr == quad_edge[fi]->m_vertex[1])
return ON_SUBD_RETURN_ERROR(false);
if (quad_edge[fi]->m_face_count < 1)
return ON_SUBD_RETURN_ERROR(false);
if (nullptr == quad_vertex[fi])
return ON_SUBD_RETURN_ERROR(false);
if (quad_vertex[fi]->m_edge_count < 2 || nullptr == quad_vertex[fi]->m_edges )
return ON_SUBD_RETURN_ERROR(false);
if (quad_vertex[fi]->m_face_count < 1 || nullptr == quad_vertex[fi]->m_faces )
return ON_SUBD_RETURN_ERROR(false);
dex = grid_quad_vertex_dex[fi];
if ( quad_vertex[fi] != m_vertex_grid[dex.i][dex.j])
return ON_SUBD_RETURN_ERROR(false);
if ( quad_edge[fi] != m_center_edges[fi] )
return ON_SUBD_RETURN_ERROR(false);
if ( quad_vertex[(fi+1)%4] != quad_edge[fi]->m_vertex[1-quad_eptr[fi].EdgeDirection()] )
return ON_SUBD_RETURN_ERROR(false);
}
const ON_SubDFace* side_face[4] = {};
const ON_SubDFace* corner_face[4] = {};
for (unsigned int fi = 0; fi < 4; fi++)
{
dex = grid_quad_vertex_dex[fi];
if (quad_vertex[fi] != m_vertex_grid[dex.i][dex.j])
return ON_SUBD_RETURN_ERROR(false);
dex = face_side_dex[fi];
side_face[fi] = m_face_grid[dex.i][dex.j];
if ( quad_face == side_face[fi])
return ON_SUBD_RETURN_ERROR(false);
if (m_bBoundaryCrease[fi])
{
// A tag of ON_SubD::EdgeTag::X is an error here
if ( false == quad_edge[fi]->IsCrease(false) )
return ON_SUBD_RETURN_ERROR(false);
if ( ON_UNSET_UINT_INDEX == quad_edge[fi]->FaceArrayIndex(quad_face) )
return ON_SUBD_RETURN_ERROR(false);
if (2 == quad_edge[fi]->m_face_count)
{
if ( false == quad_edge[fi]->m_vertex[0]->IsCreaseOrCorner())
return ON_SUBD_RETURN_ERROR(false);
if ( false == quad_edge[fi]->m_vertex[1]->IsCreaseOrCorner())
return ON_SUBD_RETURN_ERROR(false);
}
if (nullptr != side_face[fi])
return ON_SUBD_RETURN_ERROR(false);
}
else
{
if (nullptr == side_face[fi])
return ON_SUBD_RETURN_ERROR(false);
// A tag of ON_SubD::EdgeTag::X is permitted here
if ( 2 != quad_edge[fi]->m_face_count)
return ON_SUBD_RETURN_ERROR(false);
if (quad_edge[fi]->IsCrease(false))
{
unsigned int dart_count = 0;
unsigned int crease_count = 0;
if ( ON_SubD::VertexTag::Dart == quad_edge[fi]->m_vertex[0]->m_vertex_tag)
dart_count++;
else if ( quad_edge[fi]->m_vertex[0]->IsCreaseOrCorner())
crease_count++;
else
return ON_SUBD_RETURN_ERROR(false);
if ( ON_SubD::VertexTag::Dart == quad_edge[fi]->m_vertex[1]->m_vertex_tag)
dart_count++;
else if ( quad_edge[fi]->m_vertex[1]->IsCreaseOrCorner())
crease_count++;
else
return ON_SUBD_RETURN_ERROR(false);
if ( 0 == dart_count )
return ON_SUBD_RETURN_ERROR(false);
if ( 2 != dart_count + crease_count )
return ON_SUBD_RETURN_ERROR(false);
}
else
{
if (false == quad_edge[fi]->IsSmooth(true))
return ON_SUBD_RETURN_ERROR(false);
}
const ON_SubDFace* edge_faces[2] = { ON_SUBD_FACE_POINTER(quad_edge[fi]->m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(quad_edge[fi]->m_face2[1].m_ptr) };
if (quad_face == edge_faces[0])
{
if (side_face[fi] != edge_faces[1])
return ON_SUBD_RETURN_ERROR(false);
}
else if (quad_face == edge_faces[1])
{
if (side_face[fi] != edge_faces[0])
return ON_SUBD_RETURN_ERROR(false);
}
else
return ON_SUBD_RETURN_ERROR(false);
if (nullptr == m_edge_grid[fi][0] || nullptr == m_edge_grid[fi][1])
return ON_SUBD_RETURN_ERROR(false);
unsigned int side_fei[3] = {};
side_fei[1] = side_face[fi]->EdgeArrayIndex(quad_edge[fi]);
if ( ON_UNSET_UINT_INDEX == side_fei[1] )
return ON_SUBD_RETURN_ERROR(false);
const ON_SubDEdgePtr side_face_eptr = side_face[fi]->EdgePtr(side_fei[1]);
if ( quad_edge[fi] != side_face_eptr.Edge())
return ON_SUBD_RETURN_ERROR(false);
unsigned int k = (side_face_eptr.EdgeDirection() == quad_eptr[fi].EdgeDirection()) ? 2 : 0;
unsigned int side_face_edge_count = side_face[fi]->m_edge_count;
side_fei[2-k] = (side_fei[1] + side_face_edge_count - 1) % side_face_edge_count;
side_fei[k] = (side_fei[1] + 1) % side_face_edge_count;
if ( m_edge_grid[fi][0] != side_face[fi]->Edge(side_fei[0]))
return ON_SUBD_RETURN_ERROR(false);
if ( m_edge_grid[fi][1] != side_face[fi]->Edge(side_fei[2]))
return ON_SUBD_RETURN_ERROR(false);
}
dex = face_corner_dex[fi];
corner_face[fi] = m_face_grid[dex.i][dex.j];
if ( quad_face == corner_face[fi])
return ON_SUBD_RETURN_ERROR(false);
bool bCornerShouldExist
= 4 == quad_vertex[fi]->m_edge_count
&& 4 == quad_vertex[fi]->m_face_count
&& quad_vertex[fi]->IsSmoothOrDart()
&& false == m_bBoundaryCrease[fi]
&& false == m_bBoundaryCrease[(fi + 3) % 4];
if (bCornerShouldExist)
{
for (unsigned int vei = 0; vei < 4; vei++)
{
const ON_SubDEdge* e = quad_vertex[fi]->Edge(vei);
if ( nullptr == e)
return ON_SUBD_RETURN_ERROR(false);
if (2 == e->m_face_count)
{
if (e->IsSmooth(true))
continue;
if (e->IsCrease(false) && ON_SubD::VertexTag::Dart == quad_vertex[fi]->m_vertex_tag)
continue;
}
if (false == e->IsCrease(false))
return ON_SUBD_RETURN_ERROR(false);
bCornerShouldExist = false;
break;
}
}
dex = grid_corner_dex[fi];
if (bCornerShouldExist)
{
if (nullptr == corner_face[fi])
return ON_SUBD_RETURN_ERROR(false);
if (side_face[fi] == corner_face[fi])
return ON_SUBD_RETURN_ERROR(false);
if (ON_UNSET_UINT_INDEX == corner_face[fi]->VertexIndex(quad_vertex[fi]))
return ON_SUBD_RETURN_ERROR(false);
const unsigned int corner_face_vi = corner_face[fi]->VertexIndex(quad_vertex[fi]);
if ( ON_UNSET_UINT_INDEX == corner_face_vi)
return ON_SUBD_RETURN_ERROR(false);
if (4 == corner_face[fi]->m_edge_count)
{
if (nullptr == m_vertex_grid[dex.i][dex.j])
return ON_SUBD_RETURN_ERROR(false);
if ( corner_face[fi]->Vertex((corner_face_vi+2)%4) != m_vertex_grid[dex.i][dex.j] )
return ON_SUBD_RETURN_ERROR(false);
}
else
{
if (nullptr != m_vertex_grid[dex.i][dex.j])
return ON_SUBD_RETURN_ERROR(false);
}
}
//else
//{
// if (nullptr != corner_face[fi])
// return ON_SUBD_RETURN_ERROR(false);
// if (nullptr != m_vertex_grid[dex.i][dex.j])
// return ON_SUBD_RETURN_ERROR(false);
//}
if (false == m_bBoundaryCrease[fi])
{
for (unsigned int j = 0; j < 2; j++)
{
const ON_SubDVertex* v = quad_vertex[(fi + j) % 4];
unsigned int k = (v != m_edge_grid[fi][j]->m_vertex[0]) ? 0 : 1;
if (v != m_edge_grid[fi][j]->m_vertex[1 - k])
return ON_SUBD_RETURN_ERROR(false);
dex = grid_side_dex[fi][j];
if (m_vertex_grid[dex.i][dex.j] != m_edge_grid[fi][j]->m_vertex[k])
return ON_SUBD_RETURN_ERROR(false);
}
}
}
return true;
}
void ON_SubDQuadNeighborhood::Clear(
ON_SubDQuadNeighborhood* subd_quad_nbd,
bool bRetainFixedSizeHeap
)
{
if (nullptr != subd_quad_nbd)
{
ON_SubD_FixedSizeHeap* fsh = (bRetainFixedSizeHeap) ? subd_quad_nbd->m_fsh : nullptr;
subd_quad_nbd->Internal_Destroy(true);
subd_quad_nbd->m_fsh = fsh;
}
}
ON_SubDQuadNeighborhood::~ON_SubDQuadNeighborhood()
{
Internal_Destroy(false);
}
void ON_SubDQuadNeighborhood::Internal_Destroy(bool bReinitialize)
{
if (nullptr != m_fsh)
{
if (bReinitialize)
m_fsh->Reset();
m_fsh = nullptr;
}
if (bReinitialize)
{
m_bIsCubicPatch = false;
m_initial_subdivision_level = 0;
m_current_subdivision_level = 0;
m_extraordinary_corner_vertex_count = 0;
m_bExtraordinaryCornerVertex[0] = false;
m_bExtraordinaryCornerVertex[1] = false;
m_bExtraordinaryCornerVertex[2] = false;
m_bExtraordinaryCornerVertex[3] = false;
m_exact_quadrant_patch_count = 0;
m_bExactQuadrantPatch[0] = false;
m_bExactQuadrantPatch[1] = false;
m_bExactQuadrantPatch[2] = false;
m_bExactQuadrantPatch[3] = false;
m_boundary_crease_count = 0;
m_bBoundaryCrease[0] = false;
m_bBoundaryCrease[1] = false;
m_bBoundaryCrease[2] = false;
m_bBoundaryCrease[3] = false;
m_bCenterEdgeLimitPoint[0] = false;
m_bCenterEdgeLimitPoint[1] = false;
m_bCenterEdgeLimitPoint[2] = false;
m_bCenterEdgeLimitPoint[3] = false;
m_center_edge_limit_point[0] = ON_SubDSectorLimitPoint::Nan;
m_center_edge_limit_point[1] = ON_SubDSectorLimitPoint::Nan;
m_center_edge_limit_point[2] = ON_SubDSectorLimitPoint::Nan;
m_center_edge_limit_point[3] = ON_SubDSectorLimitPoint::Nan;
for (unsigned int i = 0; i < 4; i++)
{
m_vertex_grid[i][0] = nullptr;
m_vertex_grid[i][1] = nullptr;
m_vertex_grid[i][2] = nullptr;
m_vertex_grid[i][3] = nullptr;
m_edge_grid[i][0] = nullptr;
m_edge_grid[i][1] = nullptr;
if (i < 3)
{
m_face_grid[i][0] = nullptr;
m_face_grid[i][1] = nullptr;
m_face_grid[i][2] = nullptr;
}
}
double* dst = &m_srf_cv1[0][0][0];
double* dst1 = dst + sizeof(m_srf_cv1) / sizeof(m_srf_cv1[0][0][0]);
while (dst < dst1)
*dst++ = ON_UNSET_VALUE;
}
}
static bool IsOrdinarySmoothQuadCornerVertex(
const ON_SubDVertex* corner_vertex
)
{
if ( nullptr == corner_vertex)
return ON_SUBD_RETURN_ERROR(false);
if ( 4 != corner_vertex->m_face_count)
return false;
if ( 4 != corner_vertex->m_edge_count)
return false;
if ( false == corner_vertex->IsSmooth() )
return false;
for (unsigned vfi = 0; vfi < 4; vfi++)
{
const ON_SubDFace* face = corner_vertex->m_faces[vfi];
if ( nullptr == face )
return ON_SUBD_RETURN_ERROR(false);
if ( 4 != face->m_edge_count )
return false;
}
for (unsigned vei = 0; vei < 4; vei++)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(corner_vertex->m_edges[vei].m_ptr);
if ( nullptr == e )
return ON_SUBD_RETURN_ERROR(false);
if ( false == e->IsSmooth(false) )
return false;
if ( 2 != e->m_face_count)
return ON_SUBD_RETURN_ERROR(false);
const unsigned int outer_vertex_index = (corner_vertex == e->m_vertex[0]) ? 1U : 0U;
const ON_SubDVertex* outer_vertex = e->m_vertex[outer_vertex_index];
if ( nullptr == outer_vertex || corner_vertex == outer_vertex )
return ON_SUBD_RETURN_ERROR(false);
if ( outer_vertex->IsSmooth() )
continue;
const double sector_coefficient = e->m_sector_coefficient[outer_vertex_index];
if ( !(0.5 == sector_coefficient) )
return false;
}
return true;
}
bool ON_SubDQuadNeighborhood::VertexGridIsExactCubicPatch(
const ON_2dex min_face_grid_dex,
const ON_2dex max_face_grid_dex,
unsigned int boundary_corner_index
) const
{
// Returns value for m_bIsCubicPatch
if (m_extraordinary_corner_vertex_count > 0)
return false;
if (m_exact_quadrant_patch_count < 4)
return false;
if (m_boundary_crease_count > 2)
return false;
int fcount_check;
if (0 == m_boundary_crease_count)
fcount_check = 9;
else if (1 == m_boundary_crease_count)
fcount_check = 6;
else if (2 == m_boundary_crease_count)
{
if ( boundary_corner_index >= 4 )
return false;
fcount_check = 4;
}
else
{
ON_SUBD_ERROR("Bug in this code.");
return false;
}
for (int i = min_face_grid_dex.i; i < max_face_grid_dex.i; i++)
{
for (int j = min_face_grid_dex.j; j < max_face_grid_dex.j; j++)
{
const ON_SubDFace* f = m_face_grid[i][j];
if (nullptr == f || 4 != f->m_edge_count)
{
ON_SUBD_ERROR("Bug in this code.");
return false;
}
fcount_check--;
}
}
if (0 != fcount_check)
{
ON_SUBD_ERROR("Bug in this code.");
return false;
}
// For the m_vertex_grid[][] to be used as an exact cubic bispan,
// the outer vertices need to be checked.
const ON_2dex min_vtx_grid_dex = min_face_grid_dex;
const ON_2dex max_vtx_grid_dex = { max_face_grid_dex.i + 1, max_face_grid_dex.j + 1 };
ON_2dex min_smooth_vtx_grid_dex = min_vtx_grid_dex;
ON_2dex max_smooth_vtx_grid_dex = max_vtx_grid_dex;
ON_2dex dex;
if (m_boundary_crease_count > 0)
{
ON_2dex crease_vtx_dex[8];
memset(crease_vtx_dex, 0, sizeof(crease_vtx_dex));
unsigned int crease_vtx_count = 0;
if (m_bBoundaryCrease[0])
{
dex.j = 1;
for (dex.i = min_vtx_grid_dex.i; dex.i < max_vtx_grid_dex.i && crease_vtx_count < 8; dex.i++)
crease_vtx_dex[crease_vtx_count++] = dex;
min_smooth_vtx_grid_dex.j++;
}
if (m_bBoundaryCrease[1])
{
dex.i = 2;
for (dex.j = min_vtx_grid_dex.j; dex.j < max_vtx_grid_dex.j && crease_vtx_count < 8; dex.j++)
crease_vtx_dex[crease_vtx_count++] = dex;
max_smooth_vtx_grid_dex.i--;
}
if (m_bBoundaryCrease[2])
{
dex.j = 2;
for (dex.i = min_vtx_grid_dex.i; dex.i < max_vtx_grid_dex.i && crease_vtx_count < 8; dex.i++)
crease_vtx_dex[crease_vtx_count++] = dex;
max_smooth_vtx_grid_dex.j--;
}
if (m_bBoundaryCrease[3])
{
dex.i = 1;
for (dex.j = min_vtx_grid_dex.j; dex.j < max_vtx_grid_dex.j && crease_vtx_count < 8; dex.j++)
crease_vtx_dex[crease_vtx_count++] = dex;
min_smooth_vtx_grid_dex.i++;
}
switch (m_boundary_crease_count)
{
case 1:
if (4 != crease_vtx_count)
{
ON_SUBD_ERROR("Invalid input or a bug above.");
return false;
}
break;
case 2:
if (6 != crease_vtx_count)
{
ON_SUBD_ERROR("Invalid input or a bug above.");
return false;
}
break;
default:
ON_SUBD_ERROR("Invalid input or a bug above.");
return false;
}
for (unsigned int i = 0; i < crease_vtx_count; i++)
{
dex = crease_vtx_dex[i];
const ON_SubDVertex* vertex = m_vertex_grid[dex.i][dex.j];
if (nullptr == vertex || false == vertex->IsCrease())
{
return false;
}
}
}
for (dex.i = min_smooth_vtx_grid_dex.i; dex.i < max_smooth_vtx_grid_dex.i; dex.i++ )
{
for (dex.j = min_smooth_vtx_grid_dex.j; dex.j < max_smooth_vtx_grid_dex.j; dex.j++)
{
const ON_SubDVertex* vertex = m_vertex_grid[dex.i][dex.j];
if (IsOrdinarySmoothQuadCornerVertex(vertex))
continue;
return false;
}
}
return true;
}
void ON_SubDQuadNeighborhood::SetPatchStatus(
const unsigned int fvi0
)
{
m_bIsCubicPatch = false;
const unsigned int delta_subdivision_level
= (m_current_subdivision_level > m_initial_subdivision_level)
? ((unsigned int)(m_current_subdivision_level - m_initial_subdivision_level))
: 0U;
ON_2dex min_face_grid_dex = { 0, 0 };
ON_2dex max_face_grid_dex = { 3, 3 };
m_boundary_crease_count = 0;
if (m_bBoundaryCrease[0])
{
m_boundary_crease_count++;
min_face_grid_dex.j++;
}
if (m_bBoundaryCrease[1])
{
m_boundary_crease_count++;
max_face_grid_dex.i--;
}
if (m_bBoundaryCrease[2])
{
m_boundary_crease_count++;
max_face_grid_dex.j--;
}
if (m_bBoundaryCrease[3])
{
m_boundary_crease_count++;
min_face_grid_dex.i++;
}
const unsigned int fvi1 = (fvi0+1)%4;
const unsigned int fvi2 = (fvi0+2)%4;
const unsigned int fvi3 = (fvi0+3)%4;
bool bCenterEdgeIsSmooth[4] = {};
bCenterEdgeIsSmooth[fvi0] = m_center_edges[fvi0]->IsSmooth(false);
bCenterEdgeIsSmooth[fvi1] = (0 == delta_subdivision_level) ? m_center_edges[fvi1]->IsSmooth(false) : true;
bCenterEdgeIsSmooth[fvi2] = (0 == delta_subdivision_level) ? m_center_edges[fvi2]->IsSmooth(false) : true;
bCenterEdgeIsSmooth[fvi3] = m_center_edges[fvi3]->IsSmooth(false);
bool bCenterEdgeIsCrease[4] = {};
bCenterEdgeIsCrease[fvi0] = bCenterEdgeIsSmooth[fvi0] ? false : m_center_edges[fvi0]->IsCrease(false);
bCenterEdgeIsCrease[fvi1] = (0 == delta_subdivision_level) ? m_center_edges[fvi1]->IsCrease(false) : false;
bCenterEdgeIsCrease[fvi2] = (0 == delta_subdivision_level) ? m_center_edges[fvi2]->IsCrease(false) : false;
bCenterEdgeIsCrease[fvi3] = bCenterEdgeIsSmooth[fvi3] ? false : m_center_edges[fvi3]->IsCrease(false);
bool bEdgeTagX = false;
for (unsigned int i = 0; i < 4; i++)
{
if (
nullptr != m_center_edges[i]
&& ON_SubD::EdgeTag::X != m_center_edges[i]->m_edge_tag
&& (bCenterEdgeIsSmooth[i] != bCenterEdgeIsCrease[i])
)
{
continue;
}
bEdgeTagX = true;
break;
}
////////////////////////////////////////////////////////////////////////////
//
// Set
// m_bExtraordinaryCornerVertex[], m_extraordinary_corner_vertex_count
// m_bExactQuadrantPatch[], and m_exact_quadrant_patch_count
//
unsigned int boundary_crease_corner_index = 86U;
unsigned int boundary_corner_corner_index = 86U;
bool bExtraordinaryCornerVertex[4] = {};
bExtraordinaryCornerVertex[fvi0] = true;
bExtraordinaryCornerVertex[fvi1] = (0 == delta_subdivision_level || bEdgeTagX);
bExtraordinaryCornerVertex[fvi2] = bExtraordinaryCornerVertex[fvi1];
bExtraordinaryCornerVertex[fvi3] = bExtraordinaryCornerVertex[fvi1];
if (false == bEdgeTagX)
{
const ON_SubDVertex* quad_vertex[4] = {
m_vertex_grid[1][1],
m_vertex_grid[2][1],
m_vertex_grid[2][2],
m_vertex_grid[1][2]
};
const bool bQuadVertexIsSmoothOrCrease[4] =
{
quad_vertex[0]->IsSmoothOrCrease(),
quad_vertex[1]->IsSmoothOrCrease(),
quad_vertex[2]->IsSmoothOrCrease(),
quad_vertex[3]->IsSmoothOrCrease()
};
for (unsigned int corner_index = 0; corner_index < 4; corner_index++)
{
if (false == bExtraordinaryCornerVertex[corner_index])
continue;
if (false == bQuadVertexIsSmoothOrCrease[corner_index] && false == quad_vertex[corner_index]->IsCorner())
continue;
if (false == bQuadVertexIsSmoothOrCrease[(corner_index+1)%4])
continue;
if (false == bQuadVertexIsSmoothOrCrease[(corner_index+3)%4])
continue;
if (bCenterEdgeIsSmooth[corner_index] && bCenterEdgeIsSmooth[(corner_index + 3) % 4])
{
if (IsOrdinarySmoothQuadCornerVertex(quad_vertex[corner_index]))
bExtraordinaryCornerVertex[corner_index] = false;
continue;
}
if (bCenterEdgeIsCrease[corner_index] && bCenterEdgeIsSmooth[(corner_index + 3) % 4])
{
if (quad_vertex[corner_index]->IsCrease())
{
const ON_SubDEdge* e = m_edge_grid[(corner_index + 3) % 4][1];
if (nullptr != e && e->IsCrease(false))
{
const ON_SubDFace* f = SideFace((corner_index + 3) % 4);
if (nullptr != f && 4 == f->m_edge_count)
bExtraordinaryCornerVertex[corner_index] = false;
}
}
continue;
}
if (bCenterEdgeIsSmooth[corner_index] && bCenterEdgeIsCrease[(corner_index + 3) % 4])
{
if (quad_vertex[corner_index]->IsCrease())
{
const ON_SubDEdge* e = m_edge_grid[corner_index][0];
if (nullptr != e && e->IsCrease(false))
{
const ON_SubDFace* f = SideFace(corner_index);
if (nullptr != f && 4 == f->m_edge_count)
bExtraordinaryCornerVertex[corner_index] = false;
}
}
continue;
}
if (bCenterEdgeIsCrease[corner_index] && bCenterEdgeIsCrease[(corner_index + 3) % 4])
{
if (
(quad_vertex[corner_index]->IsCrease() || quad_vertex[corner_index]->IsCorner())
&& quad_vertex[(corner_index + 1) % 4]->IsCrease()
&& quad_vertex[(corner_index + 3) % 4]->IsCrease()
&& IsOrdinarySmoothQuadCornerVertex(quad_vertex[(corner_index + 2) % 4])
)
{
const ON_SubDEdge* e1 = m_edge_grid[(corner_index + 1) % 4][0];
const ON_SubDEdge* e2 = m_edge_grid[(corner_index + 2) % 4][1];
if (nullptr != e1 && nullptr != e2 && e1->IsCrease(false) && e2->IsCrease(false))
{
if (delta_subdivision_level > 0)
{
if (quad_vertex[corner_index]->IsCrease())
{
boundary_crease_corner_index = corner_index;
bExtraordinaryCornerVertex[corner_index] = false;
}
else if ( quad_vertex[corner_index]->IsCorner() )
{
boundary_corner_corner_index = corner_index;
}
}
}
}
continue;
}
}
}
m_extraordinary_corner_vertex_count = 0;
for (unsigned int corner_index = 0; corner_index < 4; corner_index++)
{
m_bExtraordinaryCornerVertex[corner_index] = bExtraordinaryCornerVertex[corner_index];
if (bExtraordinaryCornerVertex[corner_index])
{
m_extraordinary_corner_vertex_count++;
}
}
m_exact_quadrant_patch_count = 0;
for (unsigned int corner_index = 0; corner_index < 4; corner_index++)
{
m_bExactQuadrantPatch[corner_index] = false;
if (m_boundary_crease_count > 2)
continue;
if (2 == m_boundary_crease_count)
{
if (boundary_crease_corner_index >= 4)
{
if ( boundary_corner_corner_index >= 4 || ((boundary_corner_corner_index+2)%4) != corner_index)
continue;
}
}
if (bExtraordinaryCornerVertex[corner_index])
continue;
if (bExtraordinaryCornerVertex[(corner_index + 1) % 4])
continue;
if (bExtraordinaryCornerVertex[(corner_index + 3) % 4])
continue;
m_bExactQuadrantPatch[corner_index] = true;
m_exact_quadrant_patch_count++;
}
// Set m_bIsCubicPatch
m_bIsCubicPatch = VertexGridIsExactCubicPatch(
min_face_grid_dex,
max_face_grid_dex,
boundary_crease_corner_index
);
}
bool ON_SubDQuadNeighborhood::Set(
const ON_SubDFace* center_quad_face
)
{
ON_SubDQuadNeighborhood::Clear(this, false);
if (nullptr == center_quad_face)
return true;
if (4 != center_quad_face->m_edge_count)
return ON_SUBD_RETURN_ERROR(false);
const ON_SubDVertex* qf_vertex[4];
bool bIsDartVertex[4];
bool bIsCreaseEdge[4];
for (unsigned int qfei = 0; qfei < 4; qfei++)
{
const ON__UINT_PTR eptr = center_quad_face->m_edge4[qfei].m_ptr;
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr);
if (nullptr == edge)
return ON_SUBD_RETURN_ERROR(false);
if (nullptr == edge->m_vertex[0])
return ON_SUBD_RETURN_ERROR(false);
if (nullptr == edge->m_vertex[1])
return ON_SUBD_RETURN_ERROR(false);
bIsCreaseEdge[qfei] = edge->IsCrease(false);
m_center_edges[qfei] = edge;
qf_vertex[qfei] = edge->m_vertex[ON_SUBD_EDGE_DIRECTION(eptr)];
bIsDartVertex[qfei] = qf_vertex[qfei]->IsDart();
}
for (unsigned int qfei = 0; qfei < 4; qfei++)
{
ON__UINT_PTR ev1 = 1-ON_SUBD_EDGE_DIRECTION(center_quad_face->m_edge4[qfei].m_ptr);
if ( qf_vertex[(qfei+1)%4] != m_center_edges[qfei]->m_vertex[ev1] )
return ON_SUBD_RETURN_ERROR(false);
}
const ON_SubDFace* qf_nbr_face[4] = {};
for (unsigned int qfei = 0; qfei < 4; qfei++)
{
if (bIsCreaseEdge[qfei] && false == bIsDartVertex[qfei] && false == bIsDartVertex[(qfei+1)%4])
{
m_bBoundaryCrease[qfei] = true;
m_boundary_crease_count++;
continue;
}
qf_nbr_face[qfei] = m_center_edges[qfei]->NeighborFace(center_quad_face, false);
}
ON_SubDSectorIterator sit0, sit1;
m_face_grid[1][1] = center_quad_face;
m_face_grid[1][0] = qf_nbr_face[0];
m_face_grid[2][1] = qf_nbr_face[1];
m_face_grid[1][2] = qf_nbr_face[2];
m_face_grid[0][1] = qf_nbr_face[3];
m_vertex_grid[1][1] = qf_vertex[0];
m_vertex_grid[2][1] = qf_vertex[1];
m_vertex_grid[2][2] = qf_vertex[2];
m_vertex_grid[1][2] = qf_vertex[3];
const ON_SubDFace* corner_faces[4] = {};
const ON_SubDVertex* outer_vertex[12] = {};
for (unsigned int qfei = 0; qfei < 4; qfei++)
{
const unsigned int qfei3 = (qfei+3)%4;
if ( nullptr == qf_nbr_face[qfei] && nullptr == qf_nbr_face[qfei3])
continue;
if (nullptr == sit0.Initialize(center_quad_face, 0, qfei))
return ON_SUBD_RETURN_ERROR(false);
if (qf_vertex[qfei] != sit0.CenterVertex())
return ON_SUBD_RETURN_ERROR(false);
sit1 = sit0;
const bool b4EdgedCorner = (4 == qf_vertex[qfei]->m_edge_count && qf_vertex[qfei]->m_face_count >= 3);
const ON_SubDEdge* e[2] = {};
const ON_SubDVertex* v[2] = {};
ON__UINT_PTR eptr;
for (;;)
{
if (nullptr != qf_nbr_face[qfei])
{
if (qf_nbr_face[qfei] != sit0.PrevFace(false))
return ON_SUBD_RETURN_ERROR(false);
eptr = sit0.CurrentEdgePtr(0).m_ptr;
e[0] = ON_SUBD_EDGE_POINTER(eptr);
if (nullptr == e[0])
return ON_SUBD_RETURN_ERROR(false);
v[0] = e[0]->m_vertex[1 - ON_SUBD_EDGE_DIRECTION(eptr)];
if (nullptr == v[0])
return ON_SUBD_RETURN_ERROR(false);
if (b4EdgedCorner)
{
corner_faces[qfei] = sit0.PrevFace(false == bIsDartVertex[qfei]);
if (nullptr != corner_faces[qfei])
{
eptr = sit0.CurrentEdgePtr(0).m_ptr;
e[1] = ON_SUBD_EDGE_POINTER(eptr);
if (nullptr == e[1])
return ON_SUBD_RETURN_ERROR(false);
v[1] = e[1]->m_vertex[1 - ON_SUBD_EDGE_DIRECTION(eptr)];
if (nullptr == v[1])
return ON_SUBD_RETURN_ERROR(false);
break;
}
}
}
if (nullptr == e[1] && nullptr != qf_nbr_face[qfei3])
{
if (qf_nbr_face[qfei3] != sit1.NextFace(false))
return ON_SUBD_RETURN_ERROR(false);
eptr = sit1.CurrentEdgePtr(1).m_ptr;
e[1] = ON_SUBD_EDGE_POINTER(eptr);
if (nullptr == e[1])
return ON_SUBD_RETURN_ERROR(false);
v[1] = e[1]->m_vertex[1 - ON_SUBD_EDGE_DIRECTION(eptr)];
if (nullptr == v[1])
return ON_SUBD_RETURN_ERROR(false);
if (b4EdgedCorner && nullptr == corner_faces[qfei])
{
corner_faces[qfei] = sit1.NextFace(false == bIsDartVertex[qfei3]);
if (nullptr != corner_faces[qfei] && nullptr == e[0] )
{
eptr = sit1.CurrentEdgePtr(1).m_ptr;
e[0] = ON_SUBD_EDGE_POINTER(eptr);
if (nullptr == e[0])
return ON_SUBD_RETURN_ERROR(false);
v[0] = e[0]->m_vertex[1 - ON_SUBD_EDGE_DIRECTION(eptr)];
if (nullptr == v[0])
return ON_SUBD_RETURN_ERROR(false);
break;
}
}
}
break;
}
if ( nullptr != corner_faces[qfei] )
outer_vertex[3 * qfei] = corner_faces[qfei]->QuadOppositeVertex(qf_vertex[qfei]);
if (nullptr != e[0])
{
m_edge_grid[qfei][0] = e[0];
outer_vertex[3 * qfei + 1] = v[0];
}
if (nullptr != e[1])
{
m_edge_grid[qfei3][1] = e[1];
outer_vertex[(3 * qfei + 11) % 12] = v[1];
}
}
m_face_grid[0][0] = corner_faces[0]; // lower left corner
m_face_grid[2][0] = corner_faces[1]; // lower right corner
m_face_grid[2][2] = corner_faces[2]; // upper right corner
m_face_grid[0][2] = corner_faces[3]; // upper left corner
m_vertex_grid[0][0] = outer_vertex[0]; // lower left corner
m_vertex_grid[1][0] = outer_vertex[1];
m_vertex_grid[2][0] = outer_vertex[2];
m_vertex_grid[3][0] = outer_vertex[3]; // lower right corner
m_vertex_grid[3][1] = outer_vertex[4];
m_vertex_grid[3][2] = outer_vertex[5];
m_vertex_grid[3][3] = outer_vertex[6]; // upper right corner
m_vertex_grid[2][3] = outer_vertex[7];
m_vertex_grid[1][3] = outer_vertex[8];
m_vertex_grid[0][3] = outer_vertex[9]; // upper left corner
m_vertex_grid[0][2] = outer_vertex[10];
m_vertex_grid[0][1] = outer_vertex[11];
m_initial_subdivision_level = 0;
m_current_subdivision_level = 0;
SetPatchStatus(0);
#if defined(ON_DEBUG)
IsValid(); // will trigger a break if "this" is not valid
#endif
return true;
}
const ON_SubDFace* ON_SubDQuadNeighborhood::CenterQuad() const
{
return m_face_grid[1][1];
}
const ON_SubDVertex* ON_SubDQuadNeighborhood::CenterVertex(
int vi
) const
{
if (0==vi)
return m_vertex_grid[1][1];
if (1==vi)
return m_vertex_grid[2][1];
if (2==vi)
return m_vertex_grid[2][2];
if (3==vi)
return m_vertex_grid[1][2];
return ON_SUBD_RETURN_ERROR(nullptr);
}
const ON_2dex ON_SubDQuadNeighborhood::CenterVertexDex(
int vi
)
{
if (0 == vi)
return ON_2dex(1, 1);
if (1==vi)
return ON_2dex(2, 1);
if (2==vi)
return ON_2dex(2, 2);
if (3==vi)
return ON_2dex(1, 2);
return ON_2dex(ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX);
}
const ON_SubDFace* ON_SubDQuadNeighborhood::SideFace(
unsigned int fei
) const
{
ON_2dex dex = ON_SubDQuadNeighborhood::GridDex(3,fei,1,0);
return m_face_grid[dex.i][dex.j];
}
const ON_SubDFace* ON_SubDQuadNeighborhood::CornerFace(
unsigned int fvi
) const
{
ON_2dex dex = ON_SubDQuadNeighborhood::GridDex(3,fvi,0,0);
return m_face_grid[dex.i][dex.j];
}
bool ON_SubDQuadNeighborhood::GetLimitSurfaceCV(
//double srf_cv[4][4][3]
double* srf_cv,
unsigned int srf_cv_grid_size // 4 or 5
) const
{
if (nullptr == m_face_grid[1][1] || false == m_bIsCubicPatch)
return ON_SUBD_RETURN_ERROR(false);
const double* srcP;
double *dstP;
// Get the central quad face corners
int Pcount = 0;
int i0, i1, j0, j1;
if (m_boundary_crease_count > 0)
{
if (m_boundary_crease_count >1)
return ON_SUBD_RETURN_ERROR(false);
if (m_bBoundaryCrease[0])
{
j0 = j1 = 0;
i0 = 0;
i1 = 3;
}
else if (m_bBoundaryCrease[1])
{
i0 = i1 = 3;
j0 = 0;
j1 = 3;
}
else if (m_bBoundaryCrease[2])
{
j0 = j1 = 3;
i0 = 0;
i1 = 3;
}
else if (m_bBoundaryCrease[3])
{
i0 = i1 = 0;
j0 = 0;
j1 = 3;
}
else
return ON_SUBD_RETURN_ERROR(false);
}
else
{
i0 = -1;
i1 = -1;
j0 = -1;
j1 = -1;
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (i >= i0 && i <= i1 && j >= j0 && j <= j1)
continue;
if (nullptr == m_vertex_grid[i][j])
return ON_SUBD_RETURN_ERROR(false);
//dstP = srf_cv[i][j];
dstP = srf_cv + 3*(i*srf_cv_grid_size+j);
srcP = m_vertex_grid[i][j]->m_P;
*dstP++ = *srcP++; *dstP++ = *srcP++; *dstP = *srcP;
Pcount++;
}
}
if ( 16 == Pcount)
return true;
if (12 == Pcount)
{
// crease case
const int di = (i0 == i1) ? (0 == i0 ? 1 : -1) : 0;
const int dj = (j0 == j1) ? (0 == j0 ? 1 : -1) : 0;
i1++;
j1++;
for (int i = i0; i < i1; i++)
{
for (int j = j0; j < j1; j++)
{
const ON_SubDVertex* v[2] = { m_vertex_grid[i + di][j + dj], m_vertex_grid[i + 2 * di][j + 2 * dj] };
if (nullptr == v[0] || nullptr == v[1])
return ON_SUBD_RETURN_ERROR(false);
if (ON_SubD::VertexTag::Crease != v[0]->m_vertex_tag)
return ON_SUBD_RETURN_ERROR(false);
//dstP = srf_cv[i][j];
dstP = srf_cv + 3*(i*srf_cv_grid_size+j);
dstP[0] = 2.0 * v[0]->m_P[0] - v[1]->m_P[0];
dstP[1] = 2.0 * v[0]->m_P[1] - v[1]->m_P[1];
dstP[2] = 2.0 * v[0]->m_P[2] - v[1]->m_P[2];
}
}
return true;
}
return ON_SUBD_RETURN_ERROR(false);
}
bool ON_SubDQuadNeighborhood::GetSubdivisionPoint(
const ON_SubDVertex* vertex,
double subdivision_point[3]
)
{
if (nullptr == subdivision_point)
return ON_SUBD_RETURN_ERROR(false);
for (;;)
{
if (nullptr==vertex)
break;
double Q[3];
if (!vertex->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,true,Q))
break;
subdivision_point[0] = Q[0];
subdivision_point[1] = Q[1];
subdivision_point[2] = Q[2];
}
subdivision_point[0] = ON_UNSET_VALUE;
subdivision_point[1] = ON_UNSET_VALUE;
subdivision_point[2] = ON_UNSET_VALUE;
return ON_SUBD_RETURN_ERROR(false);
}
bool ON_SubDQuadNeighborhood::GetSubdivisionPoint(
const ON_SubDEdge* edge,
double subdivision_point[3]
)
{
if (nullptr == subdivision_point)
return ON_SUBD_RETURN_ERROR(false);
for (;;)
{
if (nullptr==edge)
break;
double Q[3];
if (!edge->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,true,Q))
break;
subdivision_point[0] = Q[0];
subdivision_point[1] = Q[1];
subdivision_point[2] = Q[2];
}
subdivision_point[0] = ON_UNSET_VALUE;
subdivision_point[1] = ON_UNSET_VALUE;
subdivision_point[2] = ON_UNSET_VALUE;
return ON_SUBD_RETURN_ERROR(false);
}
bool ON_SubDQuadNeighborhood::GetSubdivisionPoint(
const ON_SubDFace* face,
double subdivision_point[3]
)
{
if (nullptr == subdivision_point)
return ON_SUBD_RETURN_ERROR(false);
for (;;)
{
if (nullptr==face)
break;
double Q[3];
if (!face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,true,Q))
break;
subdivision_point[0] = Q[0];
subdivision_point[1] = Q[1];
subdivision_point[2] = Q[2];
}
subdivision_point[0] = ON_UNSET_VALUE;
subdivision_point[1] = ON_UNSET_VALUE;
subdivision_point[2] = ON_UNSET_VALUE;
return ON_SUBD_RETURN_ERROR(false);
}
unsigned int ON_SubDQuadNeighborhood::SetLimitSubSurfaceExactCVs(
unsigned int quadrant_index
)
{
if (nullptr == m_face_grid[1][1] || quadrant_index > 4)
return ON_SUBD_RETURN_ERROR(0);
const bool bUseSavedSubdivisionPoint = true;
ON_2dex dex;
ON_2dex deltadex;
const ON_SubDFace* face;
unsigned int i;
double Q[3][3];
double* P1;
if (!ON_IsValid(m_srf_cv1[2][2][0]))
{
// all sub surfaces require inner 3x3 grid of subdivision points
face = m_face_grid[1][1];
if (4 != face->m_edge_count)
return ON_SUBD_RETURN_ERROR(0);
// The value of m_srf_cv1[2][2][0] is used to determine when the inner 3x3 grid is set,
// so its value must be set after the 8 cvs around it are successfully set.
// The value
// Q[2] = face subd point
// is calculated here because if face->GetSubdivisionPoint() fails,
// nothing below should succeed. The value of Q[2] is assigned to m_srf_cv1[2][2]
// after the 8 ring points are successfully calculated.
if (!face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[2]))
return ON_SUBD_RETURN_ERROR(0);
// faces_vertices[] face, in counterclockwise order.
// m_center_edges[] are the 4 edges of face in counterclockwise order with
// m_center_edges[0] connecting face_vertices[0] and face_vertices[1].
// However, it may be that face->Vertex(0) != face_vertices[0].
const ON_SubDVertex* faces_vertices[4] = { m_vertex_grid[1][1], m_vertex_grid[2][1], m_vertex_grid[2][2], m_vertex_grid[1][2] };
const ON_2dex srf_cv_dex[8] = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 3, 2 }, { 3, 3 }, { 2, 3 }, { 1, 3 }, { 1, 2 } };
for (unsigned int fei = 0; fei < 4; fei++)
{
if (nullptr == faces_vertices[fei])
return ON_SUBD_RETURN_ERROR(0);
if (!faces_vertices[fei]->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[0]))
return ON_SUBD_RETURN_ERROR(0);
if (nullptr == m_center_edges[fei])
return ON_SUBD_RETURN_ERROR(0);
if (!m_center_edges[fei]->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[1]))
return ON_SUBD_RETURN_ERROR(0);
dex = srf_cv_dex[2 * fei];
P1 = m_srf_cv1[dex.i][dex.j];
P1[0] = Q[0][0]; P1[1] = Q[0][1]; P1[2] = Q[0][2];
dex = srf_cv_dex[2 * fei + 1];
P1 = m_srf_cv1[dex.i][dex.j];
P1[0] = Q[1][0]; P1[1] = Q[1][1]; P1[2] = Q[1][2];
}
// m_srf_cv1[2][2][0] is used to determine when the inner 3x3 grid is set,
// so its value must be set after the 8 cvs around it are successfully set.
P1 = m_srf_cv1[2][2];
P1[0] = Q[2][0]; P1[1] = Q[2][1]; P1[2] = Q[2][2];
}
const unsigned int fvi_min = (4 == quadrant_index) ? 0 : quadrant_index;
const unsigned int fvi_max = (4 == quadrant_index) ? 4 : fvi_min+1;
unsigned int quadrant_count = 0;
for (unsigned int fvi = fvi_min; fvi < fvi_max; fvi++)
{
if (false == m_bExactQuadrantPatch[fvi])
continue;
quadrant_count++;
const unsigned int fvi3 = (fvi + 3) % 4;
for (unsigned int side_pass = 0; side_pass < 2; side_pass++)
{
const unsigned int side_fvi = (0 == side_pass) ? fvi : fvi3;
dex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 0);
deltadex = ON_SubDQuadNeighborhood::DeltaDex(side_fvi, 1, 0);
P1 = &m_srf_cv1[dex.i][dex.j][0];
if ( !ON_IsValid(P1[0]) )
{
bool bEvaluateCrease = m_bBoundaryCrease[side_fvi] || m_center_edges[side_fvi]->IsCrease(false);
if (bEvaluateCrease)
{
ON_2dex Adex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 1);
ON_2dex Bdex = ON_SubDQuadNeighborhood::GridDex(5, side_fvi, 1, 2);
for (i = 0; i < 3; i++)
{
const double* A = m_srf_cv1[Adex.i][Adex.j];
const double* B = m_srf_cv1[Bdex.i][Bdex.j];
Q[i][0] = 2.0*A[0] - B[0];
Q[i][1] = 2.0*A[1] - B[1];
Q[i][2] = 2.0*A[2] - B[2];
Adex.i += deltadex.i;
Adex.j += deltadex.j;
Bdex.i += deltadex.i;
Bdex.j += deltadex.j;
}
}
else if ( m_center_edges[side_fvi]->IsSmooth(false) )
{
const ON_SubDEdge* edge = m_edge_grid[side_fvi][0];
if (nullptr == edge)
return ON_SUBD_RETURN_ERROR(0);
if (!edge->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[0]))
return ON_SUBD_RETURN_ERROR(0);
const ON_2dex fdex = ON_SubDQuadNeighborhood::GridDex(3, side_fvi, 1, 0);
face = m_face_grid[fdex.i][fdex.j];
if (nullptr == face)
return ON_SUBD_RETURN_ERROR(0);
if (!face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[1]))
return ON_SUBD_RETURN_ERROR(0);
edge = m_edge_grid[side_fvi][1];
if (nullptr == edge)
return ON_SUBD_RETURN_ERROR(0);
if (!edge->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[2]))
return ON_SUBD_RETURN_ERROR(0);
}
for (i = 0; i < 3; i++)
{
P1 = m_srf_cv1[dex.i][dex.j];
P1[0] = Q[i][0]; P1[1] = Q[i][1]; P1[2] = Q[i][2];
dex.i += deltadex.i;
dex.j += deltadex.j;
}
}
}
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 0);
P1 = &m_srf_cv1[dex.i][dex.j][0];
if (!ON_IsValid(P1[0]))
{
if (m_bBoundaryCrease[fvi] && m_bBoundaryCrease[fvi3])
{
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 1);
Q[0][0] = m_srf_cv1[dex.i][dex.j][0];
Q[0][1] = m_srf_cv1[dex.i][dex.j][1];
Q[0][2] = m_srf_cv1[dex.i][dex.j][2];
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 2);
Q[1][0] = m_srf_cv1[dex.i][dex.j][0];
Q[1][1] = m_srf_cv1[dex.i][dex.j][1];
Q[1][2] = m_srf_cv1[dex.i][dex.j][2];
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 2);
Q[2][0] = m_srf_cv1[dex.i][dex.j][0];
Q[2][1] = m_srf_cv1[dex.i][dex.j][1];
Q[2][2] = m_srf_cv1[dex.i][dex.j][2];
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 1);
//const double c = 4.0;
//const double b = -2.0;
const double c = -8.0;
const double b = 4.0;
P1[0] = c*m_srf_cv1[dex.i][dex.j][0] + b*(Q[0][0] + Q[1][0]) + Q[2][0];
P1[1] = c*m_srf_cv1[dex.i][dex.j][1] + b*(Q[0][1] + Q[1][1]) + Q[2][1];
P1[2] = c*m_srf_cv1[dex.i][dex.j][2] + b*(Q[0][2] + Q[1][2]) + Q[2][2];
}
else if (m_bBoundaryCrease[fvi])
{
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 1);
Q[0][0] = m_srf_cv1[dex.i][dex.j][0];
Q[0][1] = m_srf_cv1[dex.i][dex.j][1];
Q[0][2] = m_srf_cv1[dex.i][dex.j][2];
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 2);
Q[1][0] = m_srf_cv1[dex.i][dex.j][0];
Q[1][1] = m_srf_cv1[dex.i][dex.j][1];
Q[1][2] = m_srf_cv1[dex.i][dex.j][2];
P1[0] = 2.0*Q[0][0] - Q[1][0];
P1[1] = 2.0*Q[0][1] - Q[1][1];
P1[2] = 2.0*Q[0][2] - Q[1][2];
}
else if (m_bBoundaryCrease[fvi3])
{
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 0);
Q[0][0] = m_srf_cv1[dex.i][dex.j][0];
Q[0][1] = m_srf_cv1[dex.i][dex.j][1];
Q[0][2] = m_srf_cv1[dex.i][dex.j][2];
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 2, 0);
Q[1][0] = m_srf_cv1[dex.i][dex.j][0];
Q[1][1] = m_srf_cv1[dex.i][dex.j][1];
Q[1][2] = m_srf_cv1[dex.i][dex.j][2];
P1[0] = 2.0*Q[0][0] - Q[1][0];
P1[1] = 2.0*Q[0][1] - Q[1][1];
P1[2] = 2.0*Q[0][2] - Q[1][2];
}
else
{
dex = ON_SubDQuadNeighborhood::GridDex(3, fvi, 0, 0);
const ON_SubDFace* face_ij = m_face_grid[dex.i][dex.j];
if (nullptr == face_ij)
return ON_SUBD_RETURN_ERROR(0);
if (!face_ij->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[0]))
return ON_SUBD_RETURN_ERROR(0);
P1[0] = Q[0][0]; P1[1] = Q[0][1]; P1[2] = Q[0][2];
}
}
}
return quadrant_count;
}
bool ON_SubDQuadNeighborhood::GetLimitSubSurfaceSinglePatchCV(
unsigned int fvi,
double srf_cv[4][4][3]
)
{
if (fvi >= 4)
return ON_SUBD_RETURN_ERROR(false);
if (false == m_bExactQuadrantPatch[fvi])
return ON_SUBD_RETURN_ERROR(false);
unsigned int quadrant_count = SetLimitSubSurfaceExactCVs(fvi);
if ( 1 != quadrant_count )
return ON_SUBD_RETURN_ERROR(false);
ON_2dex dex;
dex.i = 0;
dex.j = 0;
if (1 == fvi || 2 == fvi)
dex.i++;
if (2 == fvi || 3 == fvi)
dex.j++;
double* P1 = srf_cv[0][0];
for (unsigned int i = 0; i < 4; i++)
{
for (unsigned int j = 0; j < 4; j++)
{
//double* P1 = srf_cv[i][j];
const double* src = m_srf_cv1[i+dex.i][j+dex.j];
*P1++ = *src++;
*P1++ = *src++;
*P1++ = *src;
}
}
return true;
}
unsigned int ON_SubDQuadNeighborhood::ExtraordinaryCenterVertexIndex(
ON_SubD::VertexTag vertex_tag_filter,
unsigned int minimum_edge_count_filter
) const
{
for(;;)
{
if (1 != m_extraordinary_corner_vertex_count)
break;
if (1 != m_exact_quadrant_patch_count)
break;
const unsigned int extraordinary_vertex_index
= m_bExtraordinaryCornerVertex[0] ? 0
: (m_bExtraordinaryCornerVertex[1] ? 1
: (m_bExtraordinaryCornerVertex[2] ? 2 : 3));
if (false == (m_bExtraordinaryCornerVertex[extraordinary_vertex_index]))
break;
if (false == (m_bExactQuadrantPatch[(extraordinary_vertex_index + 2) % 4]))
break;
const ON_2dex dex = CenterVertexDex(extraordinary_vertex_index);
if (dex.i < 1 || dex.i > 2 || dex.j < 1 || dex.j > 2)
break;
if (nullptr == m_vertex_grid[dex.i][dex.j])
break;
if (ON_SubD::VertexTag::Corner != m_vertex_grid[dex.i][dex.j]->m_vertex_tag)
{
if (((unsigned int)(m_vertex_grid[dex.i][dex.j]->m_edge_count)) < minimum_edge_count_filter)
break;
if (
ON_SubD::VertexTag::Unset != vertex_tag_filter
&& m_vertex_grid[dex.i][dex.j]->m_vertex_tag != vertex_tag_filter
)
{
break;
}
}
return extraordinary_vertex_index;
}
return ON_UNSET_UINT_INDEX;
}
bool ON_SubDQuadNeighborhood::Internal_GetApproximateCV(
int i,
int j,
double unset_cv,
double approximate_cv[3]
) const
{
approximate_cv[0] = unset_cv;
approximate_cv[1] = unset_cv;
approximate_cv[2] = unset_cv;
const ON_SubDEdge* e = nullptr;
const ON_SubDFace* f = nullptr;
if (0 == j)
{
switch (i)
{
case 0:
if ( false == m_bExtraordinaryCornerVertex[0] )
f = m_face_grid[0][0];
break;
case 1:
e = m_edge_grid[0][0];
break;
case 2:
f = m_face_grid[1][0];
break;
case 3:
e = m_edge_grid[0][1];
break;
case 4:
if ( false == m_bExtraordinaryCornerVertex[1] )
f = m_face_grid[2][0];
break;
}
}
else if (4 == i)
{
switch (j)
{
// case 0: // i = 0; j = 0 handled above
case 1:
e = m_edge_grid[1][0];
break;
case 2:
f = m_face_grid[2][1];
break;
case 3:
e = m_edge_grid[1][1];
break;
case 4:
if ( false == m_bExtraordinaryCornerVertex[2] )
f = m_face_grid[2][2];
break;
}
}
else if (4 == j)
{
switch (i)
{
case 0:
if ( false == m_bExtraordinaryCornerVertex[3] )
f = m_face_grid[0][2];
break;
case 1:
e = m_edge_grid[2][1];
break;
case 2:
f = m_face_grid[1][2];
break;
case 3:
e = m_edge_grid[2][0];
break;
// case 4: // i = 4; j = 4 handled above
}
}
else if (0 == i)
{
switch (j)
{
// case 0: // i = 0; j = 0 handled above
case 1:
e = m_edge_grid[3][1];
break;
case 2:
f = m_face_grid[0][1];
break;
case 3:
e = m_edge_grid[3][0];
break;
// case 4: // i = 0; j = 4 handled above
}
}
bool bHaveApproximateCV;
const bool bUseSavedSubdivisionPoint = true;
if (nullptr != e)
{
const int extraordinary_vertex_index = ExtraordinaryCenterVertexIndex(ON_SubD::VertexTag::Crease, 4);
const ON_SubDVertex* extraordinary_vertex
= (extraordinary_vertex_index >= 0 && extraordinary_vertex_index < 4)
? CenterVertex(extraordinary_vertex_index)
: nullptr;
if (
(ON_SubD::EdgeTag::Smooth == e->m_edge_tag || ON_SubD::EdgeTag::Crease == e->m_edge_tag)
&& (e->m_vertex[0] == extraordinary_vertex || e->m_vertex[1] == extraordinary_vertex)
)
{
// extraordinary crease vertices
bHaveApproximateCV = false;
}
else
{
bHaveApproximateCV = e->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, approximate_cv);
}
}
else if ( nullptr != f && 4 == f->m_edge_count )
bHaveApproximateCV = f->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,bUseSavedSubdivisionPoint,approximate_cv);
else
{
bHaveApproximateCV = false;
}
return bHaveApproximateCV;
}
static bool CheckCV(const double* P)
{
if ( ((const ON_3dPoint*)P)->IsValid() )
return true;
ON_SUBD_ERROR("bogus cv");
return false;
}
static bool Internal_InterpCV(
double srf_cv[5][5][3],
const ON_2udex srf_unset_cv_dex,
const ON_2udex srf_patchcv00_dex,
ON_NurbsSurface& tmp,
const ON_SubDSectorLimitPoint* limit_point
)
{
if (nullptr == limit_point || false == limit_point->IsSet() )
return false;
const ON_3dPoint LP(limit_point->m_limitP);
if (false == LP.IsValid())
{
ON_SUBD_ERROR("limit_point->m_limitP is not valid.");
return false;
}
double* P1 = srf_cv[srf_unset_cv_dex.i][srf_unset_cv_dex.j];
if (!(ON_UNSET_VALUE == P1[0]))
{
ON_SUBD_ERROR("srf_unset_cv_dex parameter does not index an unset cv");
return false;
}
const ON_2udex patch_unset_cv_dex(srf_unset_cv_dex.i - srf_patchcv00_dex.i, srf_unset_cv_dex.j - srf_patchcv00_dex.j);
if (0 != patch_unset_cv_dex.i && 3 != patch_unset_cv_dex.i)
{
ON_SUBD_ERROR("Unable to correctly set patch_unset_cv_dex.i");
return false;
}
if (0 != patch_unset_cv_dex.j && 3 != patch_unset_cv_dex.j)
{
ON_SUBD_ERROR("Unable to correctly set patch_unset_cv_dex.j");
return false;
}
tmp.m_cv = srf_cv[srf_patchcv00_dex.i][srf_patchcv00_dex.j];
if (tmp.CV(patch_unset_cv_dex.i, patch_unset_cv_dex.j) != P1)
{
// There is a bug in this function or in input parameters.
ON_SUBD_ERROR("Unable to correctly set tmp.m_cv.");
tmp.m_cv = nullptr;
return false;
}
P1[0] = 0.0;
P1[1] = 0.0;
P1[2] = 0.0;
const double s = (0 == patch_unset_cv_dex.i) ? 0.0 : 1.0;
const double t = (0 == patch_unset_cv_dex.j) ? 0.0 : 1.0;
const ON_3dPoint A = tmp.PointAt(s,t);
tmp.m_cv = nullptr;
if (false == A.IsValid())
{
ON_SUBD_ERROR("tmp.PointAt(s,t) failed.");
P1[0] = ON_UNSET_VALUE;
P1[1] = ON_UNSET_VALUE;
P1[2] = ON_UNSET_VALUE;
return false;
}
// If C = unset cv, the tensor product B-spline coefficient of C is (1/6)*(1/6) = 1/36
// and A + 1/36*C = limitP. So, C = 36*(LP - A).
const ON_3dPoint C = 36.0*(LP - A);
if (false == C.IsValid())
{
ON_SUBD_ERROR("36*(LP - A).is not valid.");
P1[0] = ON_UNSET_VALUE;
P1[1] = ON_UNSET_VALUE;
P1[2] = ON_UNSET_VALUE;
return false;
}
P1[0] = C.x;
P1[1] = C.y;
P1[2] = C.z;
return true;
}
unsigned int ON_SubDQuadNeighborhood::GetLimitSubSurfaceMultiPatchCV(
bool bEnableApproximatePatch,
double srf_cv[5][5][3],
ON_SubDLimitNurbsFragment::BispanType patch_type[4]
)
{
// Each "patch" is a 4x4 grid of CVs and srf_cc are the cvs for a cubic NURBS with 2 x2 spans (5x5 cvs)
// Patch 0 has cvs with indices [0][0] to [3][3].
// Patch 1 has cvs with indices [1][0] to [4][3].
// Patch 2 has cvs with indices [1][1] to [4][4].
// Patch 3 has cvs with indices [0][1] to [3][4].
// this->m_srf_cv1[5][5][3] are the CVS we could set from SubD control NET.
// Unset CVS have coordinates with values ON_UNSET_VALUE
// This function fills in unset cvs, returns them in srf_cv[5][5][3],
// and sets patch_type[N] to indicate if all the CVs for that patch are set.
// It returns the total number of set patches.
const ON_2udex patch_cv00[4] = { ON_2udex(0,0),ON_2udex(1,0),ON_2udex(1,1),ON_2udex(0,1) };
if (nullptr != patch_type)
{
patch_type[0] = ON_SubDLimitNurbsFragment::BispanType::None;
patch_type[1] = ON_SubDLimitNurbsFragment::BispanType::None;
patch_type[2] = ON_SubDLimitNurbsFragment::BispanType::None;
patch_type[3] = ON_SubDLimitNurbsFragment::BispanType::None;
}
unsigned int quadrant_count = SetLimitSubSurfaceExactCVs(4U);
if ( quadrant_count <= 0 )
return ON_SUBD_RETURN_ERROR(0);
unsigned int patch_exact_cv_count[4] = {};
unsigned int patch_approx_cv_count[4] = {};
double* P1 = srf_cv[0][0];
const double* src = m_srf_cv1[0][0];
unsigned int exact_cv_count = 0;
unsigned int approx_cv_count = 0;
// Here "approximate" means the cv valud is not set in m_srf_cv1[][][] and
// the corresponding srf_cv[][][] value was set in this function.
// It also means the resulting cubic patches using that cv are close to but
// generally not exactly equal to the SubD surface location.
// The need for approximation happens because we have an exceptional SubD vertex on the central face.
// Recall that the SubD surface at the limit point of an exceptional vertex is typically G1 but not G2
// and that a bicubic NURBS surface with simple interior knots is G2. Hence, the bicubic NURBS surface
// in this exceptional case has to be an approximation. When there are no exceptional vertices,
// the bicubic NURBS surface is exactly equal to the SubD surface
// (that's one of the selling points of Catmull-Clark SubD).
//
unsigned char srf_cv_status[5][5] = {}; // 0 = unset, 1 = set, 2 = approximate from subD control poly, 3 = approximate from interpolation
// This loop counts the number of set cvs for each of the 4 patches and copies the cv locations
// from the input srv cv array this->m_srf_cv1[][][] to the output cv array srf_cv[][][].
for (unsigned int i = 0; i < 5U; i++)
{
for (unsigned int j = 0; j < 5U; j++, P1 += 3)
{
// patch_count = number of patches this cv is used by (1 patch for the m_srf_cv1[][][] grid corners up to 4 patches for the central 3x3 grid).
unsigned int active_patch_count = 0;
// indices of patches this cv is active on
unsigned int active_patch_index[4] = {};
for (unsigned int patch_dex = 0; patch_dex < 4; patch_dex++)
{
if (
i >= patch_cv00[patch_dex].i && i < patch_cv00[patch_dex].i+4
&& j >= patch_cv00[patch_dex].j && j < patch_cv00[patch_dex].j+4
)
active_patch_index[active_patch_count++] = patch_dex;
}
if (ON_UNSET_VALUE != src[0])
{
// input cv at this->m_srf_cv1[i][j][] is set - copy it.
srf_cv_status[i][j] = 1;
P1[0] = *src++;
P1[1] = *src++;
P1[2] = *src++;
CheckCV(P1);
exact_cv_count++;
for ( unsigned int k = 0; k < active_patch_count; k++ )
patch_exact_cv_count[active_patch_index[k]]++;
}
else
{
// input cv at this->m_srf_cv1[i][j][] is not set
// output srf_cv[i][j][] = (ON_UNSET_VALUE,ON_UNSET_VALUE,ON_UNSET_VALUE)
P1[0] = ON_UNSET_VALUE;
P1[1] = ON_UNSET_VALUE;
P1[2] = ON_UNSET_VALUE;
src += 3;
if (bEnableApproximatePatch)
{
// see if we can set the srf_cv[i][j][] using the SubD neighborhood edges or faces
// This works for patches that are far enough away from exceptional SubD vertices.
if (Internal_GetApproximateCV(i, j, ON_UNSET_VALUE, P1))
{
srf_cv_status[i][j] = 2;
CheckCV(P1);
approx_cv_count++;
for ( unsigned int k = 0; k < active_patch_count; k++ )
patch_approx_cv_count[active_patch_index[k]]++;
}
}
}
}
}
ON_SubDLimitNurbsFragment::BispanType pt[4] =
{
ON_SubDLimitNurbsFragment::BispanType::None,
ON_SubDLimitNurbsFragment::BispanType::None,
ON_SubDLimitNurbsFragment::BispanType::None,
ON_SubDLimitNurbsFragment::BispanType::None
};
unsigned int exact_quadrant_count = 0;
unsigned int approx_quadrant_count = 0;
unsigned int interp_quadrant_count = 0;
for (unsigned int patch_dex = 0; patch_dex < 4; patch_dex++)
{
const unsigned int patch_set_cv_count = patch_exact_cv_count[patch_dex] + patch_approx_cv_count[patch_dex];
if (16 != patch_set_cv_count)
{
if (patch_set_cv_count > 16)
{
ON_SUBD_ERROR("Bug in patch set cv counter during step 1.");
}
continue;
}
if (16 == patch_exact_cv_count[patch_dex] )
{
pt[patch_dex] = ON_SubDLimitNurbsFragment::BispanType::Exact;
exact_quadrant_count++;
}
else
{
// No interpolation is required to approximate the patch bispan for this quadrant.
pt[patch_dex] = ON_SubDLimitNurbsFragment::BispanType::Approximate;
approx_quadrant_count++;
}
}
if (quadrant_count != exact_quadrant_count)
{
ON_SUBD_ERROR("exact_quadrant_count != SetLimitSubSurfaceExactCVs()");
}
if (bEnableApproximatePatch && (exact_quadrant_count + approx_quadrant_count) < 4 )
{
// See if we can interpolate limit points to set some more srf_cv[][][] locations.
double knot[6] = { -2.0, -1.0, 0.0, 1.0, 2.0, 3.0 };
ON_NurbsSurface tmp;
tmp.m_dim = 3;
tmp.m_is_rat = 0;
tmp.m_order[0] = 4;
tmp.m_order[1] = 4;
tmp.m_cv_count[0] = 4;
tmp.m_cv_count[1] = 4;
tmp.m_knot[0] = knot;
tmp.m_knot[1] = knot;
const size_t cv_stride0 = &(srf_cv[1][0][0]) - &(srf_cv[0][0][0]); // should be 3*15 = 15
const size_t cv_stride1 = &(srf_cv[0][1][0]) - &(srf_cv[0][0][0]); // should be 3
tmp.m_cv_stride[0] = (int)cv_stride0;
tmp.m_cv_stride[1] = (int)cv_stride1;
// First see if patch 1 and patch 3 can be completed using
// this->m_center_edge*_limit_point locations
for ( unsigned int interp_patch_dex = 0; interp_patch_dex < 4; interp_patch_dex++)
{
if (exact_quadrant_count + approx_quadrant_count + interp_quadrant_count >= 4)
break; // all patches have been set
if (ON_SubDLimitNurbsFragment::BispanType::None != pt[interp_patch_dex])
continue; // already have this patch
if (15 != (patch_exact_cv_count[interp_patch_dex] + patch_approx_cv_count[interp_patch_dex]))
continue; // missing too many cvs for interopolation to work
ON_2udex srf_cv_dex[2] = {};
unsigned int edge_index[2] = {};
switch (interp_patch_dex)
{
case 0:
srf_cv_dex[0] = ON_2udex(0, 3); edge_index[0] = 3;
srf_cv_dex[1] = ON_2udex(3, 0); edge_index[1] = 0;
break;
case 1:
srf_cv_dex[0] = ON_2udex(1, 0); edge_index[0] = 0;
srf_cv_dex[1] = ON_2udex(4, 3); edge_index[1] = 1;
break;
case 2:
srf_cv_dex[0] = ON_2udex(4, 1); edge_index[0] = 1;
srf_cv_dex[1] = ON_2udex(1, 4); edge_index[1] = 2;
break;
case 3:
srf_cv_dex[0] = ON_2udex(3, 4); edge_index[0] = 2;
srf_cv_dex[1] = ON_2udex(0, 1); edge_index[1] = 3;
break;
default:
break;
}
const int n = (0 == srf_cv_status[srf_cv_dex[0].i][srf_cv_dex[0].j]) ? 0 : 1;
const ON_2udex srf_unset_cv_dex = srf_cv_dex[n];
if (0 != srf_cv_status[srf_unset_cv_dex.i][srf_unset_cv_dex.j])
continue; // the CV we would set using interpolation is already set.
if (false == m_bCenterEdgeLimitPoint[edge_index[n]])
continue; // We don't have a limit point to interpolate.
// We are missing exactly one patch cv and can set it using interpolation through LP
if (false == Internal_InterpCV(
srf_cv,
srf_unset_cv_dex,
patch_cv00[interp_patch_dex],
tmp,
&m_center_edge_limit_point[edge_index[n]]
))
{
continue;
}
srf_cv_status[srf_unset_cv_dex.i][srf_unset_cv_dex.j] = 3;
pt[interp_patch_dex] = ON_SubDLimitNurbsFragment::BispanType::Approximate;
interp_quadrant_count++;
approx_cv_count++;
for (unsigned int patch_dex = 0; patch_dex < 4; patch_dex++)
{
if (
srf_unset_cv_dex.i >= patch_cv00[patch_dex].i && srf_unset_cv_dex.i < patch_cv00[patch_dex].i + 4
&& srf_unset_cv_dex.j >= patch_cv00[patch_dex].j && srf_unset_cv_dex.j < patch_cv00[patch_dex].j + 4
)
{
const unsigned int patch_set_cv_count = patch_exact_cv_count[patch_dex] + patch_approx_cv_count[patch_dex];
if (patch_set_cv_count < 16)
patch_approx_cv_count[patch_dex]++;
else
{
ON_SUBD_ERROR("Bug in patch set cv counter during step 2.");
}
}
}
}
// See if interpolating a central quad limpt point will complete a patch.
for ( unsigned int interp_patch_dex = 0; interp_patch_dex < 4; interp_patch_dex++)
{
if (exact_quadrant_count + approx_quadrant_count + interp_quadrant_count >= 4)
break;
if (ON_SubDLimitNurbsFragment::BispanType::None != pt[interp_patch_dex])
continue; // already have this patch
if (15 != (patch_exact_cv_count[interp_patch_dex] + patch_approx_cv_count[interp_patch_dex]))
continue; // missing too many cvs for interopolation to work
const ON_2udex srf_unset_cv_dex(
(1 == interp_patch_dex || 2 == interp_patch_dex) ? 4 : 0,
(2 == interp_patch_dex || 3 == interp_patch_dex) ? 4 : 0
);
if (0 != srf_cv_status[srf_unset_cv_dex.i][srf_unset_cv_dex.j])
continue;
const ON_2udex vertex_grid_dex(0 == srf_unset_cv_dex.i ? 1 : 2, 0 == srf_unset_cv_dex.j ? 1 : 2);
const ON_SubDVertex* vertex = m_vertex_grid[vertex_grid_dex.i][vertex_grid_dex.j];
if (nullptr == vertex)
continue;
ON_SubDSectorLimitPoint vertex_limit_point = ON_SubDSectorLimitPoint::Unset;
const bool bUseSavedLimitPoint = true;
if (false == vertex->GetLimitPoint(
ON_SubD::SubDType::QuadCatmullClark,
m_face_grid[1][1],
bUseSavedLimitPoint,
vertex_limit_point
))
continue;
// Calculate value of the unset CV so the surface passes through the limit point.
if (false == Internal_InterpCV(
srf_cv,
srf_unset_cv_dex,
patch_cv00[interp_patch_dex],
tmp,
&vertex_limit_point
))
{
continue;
}
srf_cv_status[srf_unset_cv_dex.i][srf_unset_cv_dex.j] = 3;
pt[interp_patch_dex] = ON_SubDLimitNurbsFragment::BispanType::Approximate;
interp_quadrant_count++;
approx_cv_count++;
for (unsigned int patch_dex = 0; patch_dex < 4; patch_dex++)
{
if (
srf_unset_cv_dex.i >= patch_cv00[patch_dex].i && srf_unset_cv_dex.i < patch_cv00[patch_dex].i + 4
&& srf_unset_cv_dex.j >= patch_cv00[patch_dex].j && srf_unset_cv_dex.j < patch_cv00[patch_dex].j + 4
)
{
const unsigned int patch_set_cv_count = patch_exact_cv_count[patch_dex] + patch_approx_cv_count[patch_dex];
if (patch_set_cv_count < 16)
patch_approx_cv_count[patch_dex]++;
else
{
ON_SUBD_ERROR("Bug in patch set cv counter during step 2.");
}
}
}
}
// Not necessary at the time of writing, but will prevent crashes if tmp m_*_capacity values are
// corrupted by a bug.
tmp.m_cv = nullptr;
tmp.m_knot[0] = nullptr;
tmp.m_knot[1] = nullptr;
}
unsigned qcheck=0;
for (unsigned patch_dex = 0; patch_dex < 4; patch_dex++)
{
const unsigned int patch_set_cv_count = (patch_exact_cv_count[patch_dex] + patch_approx_cv_count[patch_dex]);
if (
ON_SubDLimitNurbsFragment::BispanType::Exact == pt[patch_dex]
|| ON_SubDLimitNurbsFragment::BispanType::Approximate == pt[patch_dex]
)
{
if (16 != patch_set_cv_count)
{
ON_SUBD_ERROR("Patch cv count bug 1.");
}
qcheck++;
}
else
{
if (patch_set_cv_count >= 16)
{
ON_SUBD_ERROR("Patch cv count bug 1.");
}
}
}
if (qcheck != interp_quadrant_count + approx_quadrant_count + exact_quadrant_count)
{
ON_SUBD_ERROR("patch_type[] values are not correct.");
}
if (nullptr != patch_type)
{
patch_type[0] = pt[0];
patch_type[1] = pt[1];
patch_type[2] = pt[2];
patch_type[3] = pt[3];
}
return (interp_quadrant_count + approx_quadrant_count + exact_quadrant_count);
}
static double ON_SubDQuadFaceTopology_CopySectorWeight(
const ON_SubDEdge* e0,
const ON_SubDVertex* e0v
)
{
if (nullptr == e0 || nullptr == e0v)
return ON_SUBD_RETURN_ERROR(false);
if (ON_SubD::EdgeTag::Smooth != e0->m_edge_tag && ON_SubD::EdgeTag::X != e0->m_edge_tag )
return ON_SubDSectorType::IgnoredSectorWeight;
if (e0v == e0->m_vertex[0])
return e0->m_sector_coefficient[0];
if (e0v == e0->m_vertex[1])
return e0->m_sector_coefficient[1];
return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE);
}
static ON_SubDEdge* ON_SubDQuadFaceTopology_SubdivideEdge(
ON_SubD_FixedSizeHeap& fsh,
ON_SubDVertex* qv1,
const ON_SubDVertex* qv0,
const ON_SubDEdge* e0
)
{
if (nullptr == qv1 || nullptr == e0)
return ON_SUBD_RETURN_ERROR(nullptr);
ON_SubDVertex* v1 = fsh.AllocateVertex(e0,ON_SubD::SubDType::QuadCatmullClark,true,4,4);
if (nullptr == v1)
return ON_SUBD_RETURN_ERROR(nullptr);
double v0_weight;
if ( e0->IsSmooth(true) && nullptr != qv0)
{
// qv1 is the subdivision point of qv0.
if ( qv1->m_vertex_tag != qv0->m_vertex_tag )
return ON_SUBD_RETURN_ERROR(nullptr);
if ( ON_SubD::VertexTag::Smooth == qv0->m_vertex_tag)
v0_weight = ON_SubDSectorType::IgnoredSectorWeight;
else
{
v0_weight = ON_SubDQuadFaceTopology_CopySectorWeight(e0, qv0);
if (false == ON_SubDSectorType::IsValidSectorWeightValue(v0_weight,false))
return ON_SUBD_RETURN_ERROR(nullptr);
}
}
else
v0_weight = ON_SubDSectorType::IgnoredSectorWeight;
const double v1_weight = ON_SubDSectorType::IgnoredSectorWeight;
ON_SubDEdge* e1 = fsh.AllocateEdge(qv1,v0_weight,v1,v1_weight);
if (nullptr == e1)
return ON_SUBD_RETURN_ERROR(nullptr);
if (e1->m_edge_tag != e0->m_edge_tag)
{
// On the first subdivision step,
// e0 with tag ON_SubD::EdgeTag::X turns into
// e1 with tag ON_SubD::EdgeTag::Smooth.
if ( ON_SubD::EdgeTag::Smooth != e1->m_edge_tag || ON_SubD::EdgeTag::X != e0->m_edge_tag)
return ON_SUBD_RETURN_ERROR(nullptr);
}
return e1;
}
static ON_SubDFace* ON_SubDQuadFaceTopology_SubdivideFace(
ON_SubD_FixedSizeHeap& fsh,
const ON_SubDFace* f0,
ON_SubDEdge* e1[2],
double at_crease_weight
)
{
ON_SubDVertex* v[4];
if (nullptr == f0 || nullptr == e1[0] || nullptr == e1[1])
return ON_SUBD_RETURN_ERROR(nullptr);
v[0] = const_cast<ON_SubDVertex*>(e1[0]->m_vertex[0]);
if (nullptr == v[0] || v[0] != e1[1]->m_vertex[0])
return ON_SUBD_RETURN_ERROR(nullptr);
v[1] = const_cast<ON_SubDVertex*>(e1[0]->m_vertex[1]);
if (nullptr == v[1] )
return ON_SUBD_RETURN_ERROR(nullptr);
v[3] = const_cast<ON_SubDVertex*>(e1[1]->m_vertex[1]);
if (nullptr == v[3] )
return ON_SUBD_RETURN_ERROR(nullptr);
if (v[1] == v[0] || v[3] == v[0] || v[1] == v[3])
return ON_SUBD_RETURN_ERROR(nullptr);
v[2] = fsh.AllocateVertex(f0,ON_SubD::SubDType::QuadCatmullClark,true,4,4);
if ( nullptr == v[2])
return ON_SUBD_RETURN_ERROR(nullptr);
const double v1_weight = (ON_SubD::VertexTag::Crease == v[1]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorWeight;
const double v2_weight = ON_SubDSectorType::IgnoredSectorWeight;
const double v3_weight = (ON_SubD::VertexTag::Crease == v[3]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorWeight;
ON_SubDEdge* e12 = fsh.AllocateEdge(v[1],v1_weight,v[2],v2_weight);
if ( nullptr == e12)
return ON_SUBD_RETURN_ERROR(nullptr);
ON_SubDEdge* e23 = fsh.AllocateEdge(v[2],v2_weight,v[3],v3_weight);
if ( nullptr == e23)
return ON_SUBD_RETURN_ERROR(nullptr);
ON_SubDEdgePtr f1_epts[4] = {
ON_SubDEdgePtr::Create(e1[0], 0),
ON_SubDEdgePtr::Create(e12, 0),
ON_SubDEdgePtr::Create(e23, 0),
ON_SubDEdgePtr::Create(e1[1], 1)
};
ON_SubDFace* f1 = fsh.AllocateQuad(f0->m_zero_face_id, f0->m_id, f1_epts);
if ( nullptr == f1)
return ON_SUBD_RETURN_ERROR(nullptr);
return f1;
}
bool ON_SubDQuadNeighborhood::Subdivide(
const unsigned int q0fvi,
ON_SubD_FixedSizeHeap& fsh,
class ON_SubDQuadNeighborhood* q1ft
) const
{
if ( nullptr == q1ft || this == q1ft)
return ON_SUBD_RETURN_ERROR(false);
if ( m_fsh == &fsh )
return ON_SUBD_RETURN_ERROR(false);
const ON_SubD::SubDType subd_type = ON_SubD::SubDType::QuadCatmullClark;
// When a smooth subdivision edge ends at a vertex that is a subdivision point
// of a creased original edge, this is the value to assign to the new
// edge's m_vertex_weight. The "2" is there because there would be 2
// sector faces if the subdivision was complete.
const double at_crease_weight = ON_SubDSectorType::CreaseSectorWeight(subd_type,2);
if ( m_fsh == q1ft->m_fsh)
q1ft->m_fsh = nullptr;
ON_SubDQuadNeighborhood::Clear(q1ft, false);
q1ft->m_fsh = nullptr;
q1ft->m_initial_subdivision_level = m_initial_subdivision_level;
q1ft->m_current_subdivision_level = m_current_subdivision_level + 1;
if (q0fvi > 3)
return ON_SUBD_RETURN_ERROR(false);
const ON_SubDFace* qf0 = CenterQuad();
if ( nullptr == qf0 || 4 != qf0->m_edge_count)
return ON_SUBD_RETURN_ERROR(false);
const unsigned int zero_face_id = qf0->m_zero_face_id;
const unsigned int parent_face_id = qf0->m_id;
const ON_SubDEdge* qf0_edges[4] = {
m_center_edges[q0fvi],
m_center_edges[(q0fvi+1)%4],
m_center_edges[(q0fvi+2)%4],
m_center_edges[(q0fvi+3)%4]};
if (nullptr == qf0_edges[0] || nullptr == qf0_edges[1] || nullptr == qf0_edges[2] || nullptr == qf0_edges[3])
return ON_SUBD_RETURN_ERROR(false);
const ON_SubDVertex* qf0_vertices[4] = {
CenterVertex(q0fvi),
CenterVertex((q0fvi+1)%4),
CenterVertex((q0fvi+2)%4),
CenterVertex((q0fvi+3)%4)};
if (nullptr == qf0_vertices[0] || nullptr == qf0_vertices[1] || nullptr == qf0_vertices[2] || nullptr == qf0_vertices[3])
return ON_SUBD_RETURN_ERROR(false);
const ON_SubDVertex* qv0 = qf0_vertices[0];
if ( nullptr == qv0 || qv0->m_face_count > qv0->m_edge_count)
return ON_SUBD_RETURN_ERROR(false);
const unsigned int N = qv0->m_edge_count;
if (N < ON_SubDSectorType::MinimumSectorEdgeCount(qv0->m_vertex_tag))
return ON_SUBD_RETURN_ERROR(false);
ON_SubDSectorIterator sit;
if ( nullptr == sit.Initialize(qf0,0,qv0) )
return ON_SUBD_RETURN_ERROR(false);
const bool bIsDartSector = (ON_SubD::VertexTag::Dart == qv0->m_vertex_tag);
const bool bIsCreaseOrCornerSector = qv0->IsCreaseOrCorner();
const bool bBoundaryCrease1[4] = {
m_bBoundaryCrease[q0fvi] || (bIsCreaseOrCornerSector && qf0_edges[0]->IsCrease(false)),
false,
false,
m_bBoundaryCrease[(q0fvi+3)%4] || (bIsCreaseOrCornerSector && qf0_edges[3]->IsCrease(false))
};
const bool bStopAtInternalCrease = (false == bIsDartSector);
if ( false == fsh.ReserveSubDWorkspace( subd_type, N) )
return ON_SUBD_RETURN_ERROR(false);
ON_SubDVertex* qv1 = fsh.AllocateVertex(qv0,subd_type,true,N,qv0->m_face_count);
if ( nullptr == qv1 )
return ON_SUBD_RETURN_ERROR(false);
//qv1->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad;
//qv1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial;
const ON_SubDEdge* edge0 = sit.CurrentEdge(0);
if ( nullptr == edge0 )
return ON_SUBD_RETURN_ERROR(false);
ON_SubDFace* face_grid1[3][3] = {};
ON_SubDVertex* vertex_grid1[4][4] = {};
ON_SubDEdge* edge_grid1[4][2] = {};
// edge1 = new edge from qv1 to edge0 subdivision point
ON_SubDEdge* edge1 = ON_SubDQuadFaceTopology_SubdivideEdge(fsh,qv1,qv0,edge0);
if (nullptr == edge1)
return ON_SUBD_RETURN_ERROR(false);
// prepare to rotate coutnerclockwise around qv0,
// subdividing edges and adding new faces
const ON_SubDEdge* e0[2] = {nullptr, edge0};
ON_SubDEdge* e1[2] = {nullptr, edge1};
const ON_SubDFace* f0 = qf0;
ON_SubDFace* f1 = nullptr;
bool bAtBoundaryCrease = false;
bool bFinished = false;
for (unsigned int i = 0; i < N; i++)
{
if (nullptr == f0)
return ON_SUBD_RETURN_ERROR(false);
e0[0] = e0[1];
e0[1] = sit.CurrentEdge(1);
if (nullptr == e0[1])
return ON_SUBD_RETURN_ERROR(false);
e1[0] = e1[1];
if (edge0 == e0[1])
e1[1] = edge1; // back to where we started
else
e1[1] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, qv1, qv0, e0[1]);
if (nullptr == e1[1])
return ON_SUBD_RETURN_ERROR(false);
f1 = ON_SubDQuadFaceTopology_SubdivideFace(fsh, f0, e1, at_crease_weight);
if (nullptr == f1 || 4 != f1->m_edge_count || qv1 != f1->Vertex(0))
return ON_SUBD_RETURN_ERROR(false);
if (1 == i)
{
face_grid1[0][1] = f1;
vertex_grid1[0][1] = const_cast<ON_SubDVertex*>(f1->Vertex(3));
vertex_grid1[0][2] = const_cast<ON_SubDVertex*>(f1->Vertex(2));
edge_grid1[3][0] = const_cast<ON_SubDEdge*>(f1->Edge(1));
edge_grid1[3][1] = const_cast<ON_SubDEdge*>(f1->Edge(3));
}
if (0 == i)
{
// central face in new grid
face_grid1[1][1] = f1;
vertex_grid1[1][1] = const_cast<ON_SubDVertex*>(f1->Vertex(0));
vertex_grid1[2][1] = const_cast<ON_SubDVertex*>(f1->Vertex(1));
vertex_grid1[2][2] = const_cast<ON_SubDVertex*>(f1->Vertex(2));
vertex_grid1[1][2] = const_cast<ON_SubDVertex*>(f1->Vertex(3));
if (bBoundaryCrease1[3])
{
bAtBoundaryCrease = true;
}
}
if (!bAtBoundaryCrease)
{
f0 = sit.NextFace(bStopAtInternalCrease);
if (nullptr == f0)
bAtBoundaryCrease = true;
if (e0[1] != sit.CurrentEdge(0))
return ON_SUBD_RETURN_ERROR(false);
}
if (bAtBoundaryCrease)
{
e1[1]->m_edge_tag = ON_SubD::EdgeTag::Crease;
break;
}
if (0==i)
continue;
if (f0 == qf0 || e0[1] == edge0 || sit.CurrentEdge(0) == edge0)
{
// got back to starting face
// If i+1 < N, it means the vertex is nonmanifold.
bFinished = (f0 == qf0 && e0[1] == edge0 && sit.CurrentEdge(0) == edge0 && qv1->m_face_count == qv1->m_edge_count);
break;
}
}
ON_SubDFace* face_grid1_10 = nullptr;
if (bAtBoundaryCrease)
{
if ( qv1->m_face_count < 1 || qv1->m_face_count + 1 != qv1->m_edge_count)
return ON_SUBD_RETURN_ERROR(false);
if (true == bBoundaryCrease1[0])
bFinished = true;
}
else if (bFinished)
{
face_grid1_10 = f1;
if ( qv1->m_face_count < 2 || qv1->m_face_count != qv1->m_edge_count)
return ON_SUBD_RETURN_ERROR(false);
}
else
return ON_SUBD_RETURN_ERROR(false);
if (bFinished)
{
if (4 == qv1->m_edge_count && 4 == qv1->m_face_count)
{
f1 = const_cast<ON_SubDFace*>(qv1->m_faces[2]);
if ( nullptr == f1 || qv1 != f1->Vertex(0) || 4 != f1->m_edge_count)
return ON_SUBD_RETURN_ERROR(false);
face_grid1[0][0] = f1;
vertex_grid1[0][0] = const_cast<ON_SubDVertex*>(f1->Vertex(2));
}
}
else if (false == bBoundaryCrease1[0])
{
if ( qf0 != sit.FirstFace())
return ON_SUBD_RETURN_ERROR(false);
f0 = sit.PrevFace(bStopAtInternalCrease);
if ( nullptr == f0 )
return ON_SUBD_RETURN_ERROR(false);
if ( edge0 != sit.CurrentEdge(1) )
return ON_SUBD_RETURN_ERROR(false);
const unsigned int edge_mark = qv1->m_edge_count;
e0[0] = edge0;
e0[1] = nullptr;
e1[0] = edge1;
e1[1] = nullptr;
f1 = nullptr;
for (unsigned int i = 0; i < N - edge_mark; i++)
{
e0[1] = e0[0];
e0[0] = sit.CurrentEdge(0);
if (nullptr == e0[0])
return ON_SUBD_RETURN_ERROR(false);
e1[1] = e1[0];
e1[0] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, qv1, qv0, e0[0]);
if (nullptr == e1[0])
return ON_SUBD_RETURN_ERROR(false);
f1 = ON_SubDQuadFaceTopology_SubdivideFace(fsh, f0, e1, at_crease_weight);
if (nullptr == f1 || 4 != f1->m_edge_count || qv1 != f1->Vertex(0))
return ON_SUBD_RETURN_ERROR(false);
if (0 == i)
face_grid1_10 = f1;
f0 = sit.PrevFace(bStopAtInternalCrease);
if (nullptr == f0 || ON_SubD::EdgeTag::Crease == e0[1]->m_edge_tag)
{
bFinished = (nullptr == f0 && ON_SubD::EdgeTag::Crease == e0[0]->m_edge_tag && qv1->m_face_count+1 == qv1->m_edge_count);
if (false == bFinished)
return ON_SUBD_RETURN_ERROR(false);
//qv1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; // edges no longer radially sorted
break;
}
}
}
if (false == bFinished)
return ON_SUBD_RETURN_ERROR(false);
if (nullptr != face_grid1_10)
{
f1 = face_grid1_10;
if (nullptr == f1 || vertex_grid1[1][1] != f1->Vertex(0) || vertex_grid1[2][1] != f1->Vertex(3) || 4 != f1->m_edge_count)
return ON_SUBD_RETURN_ERROR(false);
face_grid1[1][0] = f1;
vertex_grid1[1][0] = const_cast<ON_SubDVertex*>(f1->Vertex(1));
vertex_grid1[2][0] = const_cast<ON_SubDVertex*>(f1->Vertex(2));
edge_grid1[0][0] = const_cast<ON_SubDEdge*>(f1->Edge(0));
edge_grid1[0][1] = const_cast<ON_SubDEdge*>(f1->Edge(2));
}
// Add the 7 remaining elements to vertex_grid1[][]
if (false == bBoundaryCrease1[0])
{
vertex_grid1[3][0] = fsh.AllocateVertex(m_edge_grid[q0fvi][1],ON_SubD::SubDType::QuadCatmullClark,true,2,1);
if ( nullptr == vertex_grid1[3][0])
return ON_SUBD_RETURN_ERROR(false);
}
vertex_grid1[3][1] = fsh.AllocateVertex(qf0_vertices[1],ON_SubD::SubDType::QuadCatmullClark,true,3,2);
if ( nullptr == vertex_grid1[3][1])
return ON_SUBD_RETURN_ERROR(false);
vertex_grid1[3][2] = fsh.AllocateVertex(qf0_edges[1],ON_SubD::SubDType::QuadCatmullClark,true,3,2);
if ( nullptr == vertex_grid1[3][2])
return ON_SUBD_RETURN_ERROR(false);
vertex_grid1[3][3] = fsh.AllocateVertex(qf0_vertices[2],ON_SubD::SubDType::QuadCatmullClark,true,2,1);
if ( nullptr == vertex_grid1[3][3])
return ON_SUBD_RETURN_ERROR(false);
vertex_grid1[2][3] = fsh.AllocateVertex(qf0_edges[2],ON_SubD::SubDType::QuadCatmullClark,true,3,2);
if ( nullptr == vertex_grid1[2][3])
return ON_SUBD_RETURN_ERROR(false);
vertex_grid1[1][3] = fsh.AllocateVertex(qf0_vertices[3],ON_SubD::SubDType::QuadCatmullClark,true,3,2);
if ( nullptr == vertex_grid1[1][3])
return ON_SUBD_RETURN_ERROR(false);
if (false == bBoundaryCrease1[3])
{
vertex_grid1[0][3] = fsh.AllocateVertex(m_edge_grid[(q0fvi+3)%4][0],ON_SubD::SubDType::QuadCatmullClark,true,2,1);
if ( nullptr == vertex_grid1[0][3])
return ON_SUBD_RETURN_ERROR(false);
}
edge_grid1[1][0] = fsh.AllocateEdge(
vertex_grid1[2][1],
ON_SubDSectorType::IgnoredSectorWeight,
vertex_grid1[3][1],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[0], qf0_vertices[1])
);
if ( nullptr == edge_grid1[1][0])
return ON_SUBD_RETURN_ERROR(false);
edge_grid1[1][1] = fsh.AllocateEdge(
vertex_grid1[2][2],
ON_SubDSectorType::IgnoredSectorWeight,
vertex_grid1[3][2],
at_crease_weight // ignored unless vertex_grid1[3][2] is tagged as a crease
);
if ( nullptr == edge_grid1[1][1])
return ON_SUBD_RETURN_ERROR(false);
edge_grid1[2][0] = fsh.AllocateEdge(
vertex_grid1[2][2],
ON_SubDSectorType::IgnoredSectorWeight,
vertex_grid1[2][3],
at_crease_weight // ignored unless vertex_grid1[2][3] is tagged as a crease
);
if ( nullptr == edge_grid1[2][0])
return ON_SUBD_RETURN_ERROR(false);
edge_grid1[2][1] = fsh.AllocateEdge(
vertex_grid1[1][2],
ON_SubDSectorType::IgnoredSectorWeight,
vertex_grid1[1][3],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[3], qf0_vertices[3])
);
if ( nullptr == edge_grid1[2][1])
return ON_SUBD_RETURN_ERROR(false);
// Add the 5 remaining elements to face_grid1[][]
ON_SubDEdge* fedges[4];
ON_SubDEdgePtr feptrs[4];
if (false == bBoundaryCrease1[0])
{
fedges[0] = nullptr;
fedges[1] = nullptr;
fedges[2] = edge_grid1[1][0];
fedges[3] = edge_grid1[0][1];
if (nullptr == fedges[2] || nullptr == fedges[3] )
return ON_SUBD_RETURN_ERROR(false);
fedges[0] = fsh.AllocateEdge(
vertex_grid1[2][0],
ON_SubDSectorType::IgnoredSectorWeight,
vertex_grid1[3][0],
at_crease_weight // ignored unless vertex_grid1[3][0] is tagged as a crease
);
if (nullptr == fedges[0])
return ON_SUBD_RETURN_ERROR(false);
// m_edge_grid[q0fvi][1]
fedges[1] = fsh.AllocateEdge(
vertex_grid1[3][0],
ON_SubDSectorType::IgnoredSectorWeight,
vertex_grid1[3][1],
ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[q0fvi][1], qf0_vertices[1])
);
if (nullptr == fedges[1])
return ON_SUBD_RETURN_ERROR(false);
feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0);
feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0);
feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],1);
feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1);
face_grid1[2][0] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs);
if (nullptr == face_grid1[2][0])
return ON_SUBD_RETURN_ERROR(false);
}
// face_grid1[2][1]
fedges[0] = edge_grid1[1][0];
fedges[1] = nullptr;
fedges[2] = edge_grid1[1][1];
fedges[3] = ON_SUBD_EDGE_POINTER(face_grid1[1][1]->m_edge4[1].m_ptr);
if (nullptr == fedges[0] || nullptr == fedges[2] || nullptr == fedges[3] )
return ON_SUBD_RETURN_ERROR(false);
fedges[1] = fsh.AllocateEdge(
vertex_grid1[3][1],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[1]),
vertex_grid1[3][2],
ON_SubDSectorType::IgnoredSectorWeight
);
if (nullptr == fedges[1])
return ON_SUBD_RETURN_ERROR(false);
feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0);
feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0);
feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],1);
feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1);
face_grid1[2][1] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs);
if (nullptr == face_grid1[2][1])
return ON_SUBD_RETURN_ERROR(false);
// face_grid1[2][2]
fedges[0] = edge_grid1[1][1];
fedges[1] = nullptr;
fedges[2] = nullptr;
fedges[3] = edge_grid1[2][0];
if (nullptr == fedges[0] || nullptr == fedges[3] )
return ON_SUBD_RETURN_ERROR(false);
fedges[1] = fsh.AllocateEdge(
vertex_grid1[3][2],
ON_SubDSectorType::IgnoredSectorWeight,
vertex_grid1[3][3],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[2])
);
if (nullptr == fedges[1])
return ON_SUBD_RETURN_ERROR(false);
fedges[2] = fsh.AllocateEdge(
vertex_grid1[3][3],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[2]),
vertex_grid1[2][3],
ON_SubDSectorType::IgnoredSectorWeight
);
if (nullptr == fedges[2])
return ON_SUBD_RETURN_ERROR(false);
feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0);
feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0);
feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],0);
feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1);
face_grid1[2][2] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs);
if (nullptr == face_grid1[2][2])
return ON_SUBD_RETURN_ERROR(false);
// face_grid1[1][2]
fedges[0] = ON_SUBD_EDGE_POINTER(face_grid1[1][1]->m_edge4[2].m_ptr);
fedges[1] = edge_grid1[2][0];
fedges[2] = nullptr;
fedges[3] = edge_grid1[2][1];
if (nullptr == fedges[0] || nullptr == fedges[1] || nullptr == fedges[3] )
return ON_SUBD_RETURN_ERROR(false);
fedges[2] = fsh.AllocateEdge(
vertex_grid1[2][3],
ON_SubDSectorType::IgnoredSectorWeight,
vertex_grid1[1][3],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[3])
);
if (nullptr == fedges[2])
return ON_SUBD_RETURN_ERROR(false);
feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],1);
feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0);
feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],0);
feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1);
face_grid1[1][2] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs);
if (nullptr == face_grid1[1][2])
return ON_SUBD_RETURN_ERROR(false);
if (false == bBoundaryCrease1[3])
{
fedges[0] = edge_grid1[2][1];
fedges[1] = nullptr;
fedges[2] = nullptr;
fedges[3] = edge_grid1[3][0];
if (nullptr == fedges[0] || nullptr == fedges[3] )
return ON_SUBD_RETURN_ERROR(false);
fedges[1] = fsh.AllocateEdge(
vertex_grid1[1][3],
ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[(q0fvi+3)%4][0], qf0_vertices[3]),
vertex_grid1[0][3],
ON_SubDSectorType::IgnoredSectorWeight
);
if (nullptr == fedges[1])
return ON_SUBD_RETURN_ERROR(false);
fedges[2] = fsh.AllocateEdge(
vertex_grid1[0][3],
at_crease_weight, // ingored unless vertex_grid1[0][3] is tagged as a crease
vertex_grid1[0][2],
ON_SubDSectorType::IgnoredSectorWeight
);
if (nullptr == fedges[2])
return ON_SUBD_RETURN_ERROR(false);
feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0);
feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0);
feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],0);
feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1);
face_grid1[0][2] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs);
if (nullptr == face_grid1[0][2])
return ON_SUBD_RETURN_ERROR(false);
}
q1ft->m_fsh = &fsh;
const ON_2dex deltaj = ON_SubDQuadNeighborhood::DeltaDex(q0fvi,0,1);
ON_2dex dex;
q1ft->m_face_grid[1][1] = face_grid1[1][1];
for (unsigned int i = 0; i < 3; i++)
{
dex = ON_SubDQuadNeighborhood::GridDex(3, q0fvi, i, 0);
for (unsigned int j = 0; j < 3; j++)
{
q1ft->m_face_grid[dex.i][dex.j] = face_grid1[i][j];
dex.i += deltaj.i;
dex.j += deltaj.j;
}
}
for (unsigned int i = 0; i < 4; i++)
{
unsigned int j = (q0fvi + i) % 4;
q1ft->m_bBoundaryCrease[j] = bBoundaryCrease1[i];
q1ft->m_edge_grid[j][0] = edge_grid1[i][0];
q1ft->m_edge_grid[j][1] = edge_grid1[i][1];
q1ft->m_center_edges[j] = face_grid1[1][1]->Edge(i);
dex = ON_SubDQuadNeighborhood::GridDex(4,q0fvi,i,0);
for (j = 0; j < 4; j++)
{
q1ft->m_vertex_grid[dex.i][dex.j] = vertex_grid1[i][j];
dex.i += deltaj.i;
dex.j += deltaj.j;
}
}
q1ft->SetPatchStatus(q0fvi);
if ( m_bExtraordinaryCornerVertex[q0fvi] )
{
// evaluate extraordinary limit point as soon as possible and then save it for future use.
ON_SubDSectorLimitPoint limit_point;
const ON_SubD::SubDType subd_type_local_scope = ON_SubD::SubDType::QuadCatmullClark;
const ON_SubDFace* qv0_sector_face = this->m_face_grid[1][1];
const ON_SubDFace* qv1_sector_face = q1ft->m_face_grid[1][1];
if (subd_type_local_scope == qv0->SavedLimitPointType() && qv0->GetLimitPoint(subd_type_local_scope,qv0_sector_face,true,limit_point) && limit_point.IsSet())
{
limit_point.m_sector_face = qv1_sector_face; // qv1's sector face
limit_point.m_next_sector_limit_point = nullptr; // sector checks are required to get "first" face
qv1->SetSavedLimitPoint(subd_type_local_scope,limit_point);
}
else if (qv1->GetLimitPoint(subd_type_local_scope, qv1_sector_face, true, limit_point))
{
limit_point.m_sector_face = qv0_sector_face;
limit_point.m_next_sector_limit_point = nullptr; // sector checks are required to get "first" face
qv0->SetSavedLimitPoint(subd_type_local_scope,limit_point);
}
}
#if defined(ON_DEBUG)
q1ft->IsValid(); // will trigger a break if "this" is not valid
#endif
return true;
}
ON_2dex ON_SubDQuadNeighborhood::GridDex(
unsigned int grid_size,
unsigned int corner_index,
unsigned int i,
unsigned int j
)
{
ON_2dex dex;
switch (corner_index)
{
case 1:
dex.i = grid_size - 1 - j;
dex.j = i;
break;
case 2:
dex.i = grid_size - 1 - i;
dex.j = grid_size - 1 - j;
break;
case 3:
dex.i = j;
dex.j = grid_size - 1 - i;
break;
default:
dex.i = i;
dex.j = j;
}
return dex;
}
ON_2dex ON_SubDQuadNeighborhood::DeltaDex(
unsigned int corner_index,
int delta_i,
int delta_j
)
{
ON_2dex deltadex;
switch (corner_index)
{
case 1:
deltadex.i = -delta_j;
deltadex.j = delta_i;
break;
case 2:
deltadex.i = -delta_i;
deltadex.j = -delta_j;
break;
case 3:
deltadex.i = delta_j;
deltadex.j = -delta_i;
break;
default:
deltadex.i = delta_i;
deltadex.j = delta_j;
}
return deltadex;
}
bool ON_SubDFace::GetQuadLimitSurface(
size_t limit_surface_cv_stride0,
size_t limit_surface_cv_stride1,
double* limit_surface_cv
) const
{
double srf_cv[4][4][3];
if ( 4 != m_edge_count)
return false;
ON_SubDQuadNeighborhood qft;
if (false == qft.Set(this ))
return false;
if ( false == qft.m_bIsCubicPatch )
return false;
if (false == qft.GetLimitSurfaceCV(&srf_cv[0][0][0], 4U))
return false;
for (unsigned int i = 0; i < 4; i++)
{
double* dst_cv = limit_surface_cv + i*limit_surface_cv_stride0;
for (unsigned int j = 0; j < 4; j++)
{
const double* src_cv = srf_cv[i][j];
dst_cv[0] = src_cv[0];
dst_cv[1] = src_cv[1];
dst_cv[2] = src_cv[2];
dst_cv += limit_surface_cv_stride1;
}
}
return true;
}
bool ON_SubDFace::GetQuadLimitSurface(
class ON_NurbsSurface& nurbs_surface
) const
{
if (false == nurbs_surface.Create(
3, // dim
false, // is_rat
4, 4, // orders
4, 4 // cv_counts
))
{
return false;
}
if (false == GetQuadLimitSurface(nurbs_surface.m_cv_stride[0], nurbs_surface.m_cv_stride[1], nurbs_surface.m_cv))
return false;
// evaluation domain will be [0,1] x [0,1]
double k = -2.0;
for (unsigned int i = 0; i < 6; i++)
{
nurbs_surface.m_knot[0][i] = nurbs_surface.m_knot[1][i] = k;
k += 1.0;
}
return true;
}
bool ON_SubDFace::GetQuadLimitSurface(
class ON_BezierSurface& bezier_surface
) const
{
if (false == bezier_surface.Create(
3, // dim
false, // is_rat
4, 4 // orders
))
return false;
double knots[2][6] = { { -2.0, -1.0, 0.0, 1.0, 2.0, 3.0 }, { -2.0, -1.0, 0.0, 1.0, 2.0, 3.0 } };
ON_NurbsSurface nurbs_surface;
nurbs_surface.m_dim = 3;
nurbs_surface.m_is_rat = false;
nurbs_surface.m_order[0] = 4;
nurbs_surface.m_order[1] = 4;
nurbs_surface.m_cv_count[0] = 4;
nurbs_surface.m_cv_count[1] = 4;
nurbs_surface.m_cv_stride[0] = bezier_surface.m_cv_stride[0];
nurbs_surface.m_cv_stride[0] = bezier_surface.m_cv_stride[0];
nurbs_surface.m_cv = bezier_surface.m_cv;
nurbs_surface.m_cv_capacity = 0;
nurbs_surface.m_knot[0] = knots[0];
nurbs_surface.m_knot[1] = knots[1];
nurbs_surface.m_knot_capacity[0] = 0;
nurbs_surface.m_knot_capacity[1] = 0;
if (false == GetQuadLimitSurface(nurbs_surface))
return false;
if (false == nurbs_surface.ClampEnd(0, 2))
return false;
if (false == nurbs_surface.ClampEnd(1, 2))
return false;
return true;
}
ON_SubDFaceNeighborhood::~ON_SubDFaceNeighborhood()
{
ReserveCapacity(ON_SubD::SubDType::Unset,nullptr);
}
bool ON_SubDFaceNeighborhood::ReserveCapacity(
ON_SubD::SubDType subd_type,
const ON_SubDFace* face
)
{
m_face0 = nullptr;
m_subd_type = ON_SubD::SubDType::Unset;
m_center_vertex1 = nullptr;
m_face1_count = 0;
m_face1 = nullptr;
unsigned int v_capacity = 0;
unsigned int e_capacity = 0;
unsigned int f_capacity = 0;
unsigned int a_capacity = 0;
for (;;)
{
if (nullptr == face)
break;
const unsigned int N = face->m_edge_count;
if (N <= 2)
break;
// ordinary valence
//const unsigned short V0
// = (ON_SubD::FacetType::Quad == ON_SubD::FacetTypeFromSubDType(subd_type))
// ? 4
// : 6;
// set E = extraordinary vertex valence overhead
unsigned int S = 0;
//unsigned int E = 0;
const ON_SubDEdgePtr* edges = face->m_edge4;
ON__UINT_PTR edge_ptr;
const ON_SubDEdge* edge;
const ON_SubDVertex* vertex;
unsigned int i;
for (i = 0; i < N; i++)
{
if (4 == i)
{
if (nullptr == face->m_edgex)
return ON_SUBD_RETURN_ERROR(false);
edges = face->m_edgex - 4;
}
edge_ptr = edges[i].m_ptr;
edge = ON_SUBD_EDGE_POINTER(edge_ptr);
if (nullptr == edge)
break;
vertex = edge->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)];
if (nullptr == vertex)
break;
if (vertex->m_edge_count < 2 )
break;
if (vertex->m_edge_count < vertex->m_face_count )
break;
S += vertex->m_edge_count;
//if (vertex->m_edge_count > V0 )
// E += (vertex->m_edge_count - V0);
}
if (i != N)
break;
if (ON_SubD::SubDType::QuadCatmullClark == subd_type)
{
//v_capacity = 1 + 6*N + 2*E;
//e_capacity = 9*N + 3*E;
//f_capacity = 4*N + E;
//a_capacity = 36*N + 8*E;
f_capacity = S;
e_capacity = 3*S - N;
v_capacity = 2*S - 2*N + 1;
a_capacity = 4*f_capacity + 2*e_capacity + 2*v_capacity;
}
else if (ON_SubD::SubDType::TriLoopWarren == subd_type)
{
// todo -- add tri support
// (remember to add 4 to a_capacity for the ON_SubDFaceNeighborhood.m_face1[] array
// when N = 3).
break;
}
else
{
break;
}
return m_fsh.ReserveSubDWorkspace(v_capacity, e_capacity, f_capacity, a_capacity);
}
m_fsh.ReserveSubDWorkspace(0,0,0,0);
if (ON_SubD::SubDType::Unset == subd_type && nullptr == face )
return true;
return ON_SUBD_RETURN_ERROR(false);
}
bool ON_SubDFaceNeighborhood::Subdivide(
ON_SubD::SubDType subd_type,
const ON_SubDFace* face
)
{
if (false == ReserveCapacity(subd_type, face))
return ON_SUBD_RETURN_ERROR(false);
if (ON_SubD::SubDType::QuadCatmullClark == subd_type)
return ON_SubDFaceNeighborhood::QuadSubdivideHelper(face);
if (ON_SubD::SubDType::TriLoopWarren == subd_type)
return ON_SubDFaceNeighborhood::TriSubdivideHelper(face);
return ON_SUBD_RETURN_ERROR(false);
}
bool ON_SubDFaceNeighborhood::TriSubdivideHelper(
const ON_SubDFace* face
)
{
// todo - add tri support
return ON_SUBD_RETURN_ERROR(false);
}
////static bool RadialSort(
//// unsigned int vertex1_sort_mark,
//// ON_SubDVertex* vertex
//// )
////{
//// if (nullptr == vertex || vertex->m_face_count > vertex->m_edge_count)
//// return ON_SUBD_RETURN_ERROR(false);
////
//// if ( 0 == vertex1_sort_mark)
//// return true;
////
//// unsigned int e0_count = vertex1_sort_mark+1;
//// unsigned int f0_count= vertex1_sort_mark;
//// unsigned int e_count = vertex->m_edge_count;
//// unsigned int f_count = vertex->m_face_count;
//// if (e0_count > e_count || f0_count > f_count)
//// return ON_SUBD_RETURN_ERROR(false);
////
//// ON__UINT_PTR stack_buffer[40];
//// ON__UINT_PTR* buffer = 0;
//// if (e0_count <= sizeof(stack_buffer) / sizeof(stack_buffer[0]))
//// buffer = stack_buffer;
//// else
//// {
//// buffer = new (std::nothrow) ON__UINT_PTR[e0_count];
//// if ( nullptr == buffer)
//// return ON_SUBD_RETURN_ERROR(false);
//// }
////
//// unsigned int i, j;
//// ON__UINT_PTR* a;
////
//// a = (ON__UINT_PTR*)vertex->m_edges;
//// for (i = 0; i < e0_count; i++)
//// {
//// buffer[i] = a[i];
//// }
//// j = 0;
//// for (i = e0_count; i < e_count; i++)
//// {
//// a[j++] = a[i];
//// }
//// for (i = 0; i < e0_count; i++)
//// {
//// a[j++] = buffer[i];
//// }
////
//// a = (ON__UINT_PTR*)vertex->m_faces;
//// for (i = 0; i < f0_count; i++)
//// {
//// buffer[i] = a[i];
//// }
//// j = 0;
//// for (i = f0_count; i < f_count; i++)
//// {
//// a[j++] = a[i];
//// }
//// for (i = 0; i < f0_count; i++)
//// {
//// a[j++] = buffer[i];
//// }
////
//// if ( buffer != stack_buffer)
//// delete[] buffer;
//// vertex->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial;
//// return true;
////}
bool ON_SubDFaceNeighborhood::QuadSubdivideHelper(
const ON_SubDFace* face
)
{
// input face is valid and space is reserved.
const ON_SubD::SubDType subd_type = ON_SubD::SubDType::QuadCatmullClark;
// When a smooth subdivision edge ends at a vertex that is a subdivision point
// of a creased original edge, this is the value to assign to the new
// edge's m_vertex_weight. The "2" is there because there would be 2
// sector faces if the subdivision was complete.
const double at_crease2_weight = ON_SubDSectorType::CreaseSectorWeight(subd_type,2);
const unsigned int N = face->m_edge_count;
const unsigned int zero_face_id = face->m_zero_face_id;
const unsigned int parent_face_id = face->m_parent_face_id;
const bool bUseSavedSubdivisionPoint = true;
ON_SubDSectorLimitPoint limit_point;
// create central vertex
ON_SubDVertex* center_vertex1 = m_fsh.AllocateVertex(face,subd_type,bUseSavedSubdivisionPoint,N,N);
if ( nullptr == center_vertex1)
return ON_SUBD_RETURN_ERROR(false);
//center_vertex1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial;
ON_SubDVertex* ring_vertex1[2] = {};
const ON_SubDVertex* vertex0 = nullptr;
const ON_SubDEdge* prev_edge0 = nullptr;
ON__UINT_PTR prev_edge0_dir = 0;
const ON_SubDEdge* edge0 = nullptr;
ON__UINT_PTR edge0_dir = 0;
ON_SubDVertex* vertex1 = nullptr;
ON_SubDEdge* edge1 = nullptr;
ON_SubDFace* face1 = nullptr;
ON_SubDEdgePtr face1_eptrs[4] = {ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null};
const ON_SubDEdgePtr* face_m_edges = face->m_edge4;
for (unsigned int i = 0; i < N; i++, face_m_edges++)
{
prev_edge0 = edge0;
prev_edge0_dir = edge0_dir;
if (4 == i)
{
if (nullptr == face->m_edgex)
return ON_SUBD_RETURN_ERROR(false);
face_m_edges = face->m_edgex;
}
const ON__UINT_PTR edge0_ptr = face_m_edges->m_ptr;
edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr);
if (nullptr == edge0)
return ON_SUBD_RETURN_ERROR(false);
edge0_dir = ON_SUBD_EDGE_DIRECTION(edge0_ptr);
vertex0 = edge0->m_vertex[edge0_dir];
if (nullptr == vertex0)
return ON_SUBD_RETURN_ERROR(false);
if (vertex0->m_edge_count < 2)
return ON_SUBD_RETURN_ERROR(false);
// At this time (Jan, 2015) the primary purpose of creating a ON_SubDFaceNeighborhood
// is to get the limit mesh and limit cubic surfaces when the original face
// is not a quad. I need to calculate and save the limit point for extraordinary
// verticies while enough information is available to calculate it. Doing the calculation
// before calling m_fsh.AllocateVertex(), insures the information will be copied to
// vertex1;
if ( false == vertex0->GetLimitPoint(subd_type,face,true,limit_point) )
return ON_SUBD_RETURN_ERROR(false);
vertex1 = m_fsh.AllocateVertex(vertex0,subd_type,bUseSavedSubdivisionPoint,vertex0->m_edge_count,vertex0->m_edge_count);
if ( nullptr == vertex1)
return ON_SUBD_RETURN_ERROR(false);
if (nullptr != limit_point.m_sector_face)
{
// Original vertex had sectors and that will prevent
// m_fsh.AllocateVertex() from copying the limit point.
// vertex1 does not have sectors.
ON_SubDSectorLimitPoint saved_limit_point = limit_point;
saved_limit_point.m_sector_face = nullptr;
saved_limit_point.m_next_sector_limit_point = (ON_SubDSectorLimitPoint*)1; // causes unnecessary test to be skipped
vertex1->SetSavedLimitPoint(subd_type, saved_limit_point);
}
if (0 == i)
{
ring_vertex1[0] = vertex1;
}
else
{
if ( nullptr == prev_edge0)
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[0] = face1_eptrs[3].Reversed();
edge1 = m_fsh.AllocateEdge(
ring_vertex1[1],
ON_SubDSectorType::IgnoredSectorWeight,
vertex1,
ON_SubDQuadFaceTopology_CopySectorWeight(prev_edge0, vertex0)
);
if ( nullptr == edge1)
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1,0);
}
ring_vertex1[1] = vertex1;
// This vertex is either a crease (3 edges, 2 faces) or a smooth ordinary vertex (4 edges, 4 faces).
vertex1 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,4,4);
if ( nullptr == vertex1)
return ON_SUBD_RETURN_ERROR(false);
if (0 != i)
{
edge1 = m_fsh.AllocateEdge(
ring_vertex1[1],
ON_SubDQuadFaceTopology_CopySectorWeight(edge0,vertex0),
vertex1,
ON_SubDSectorType::IgnoredSectorWeight
);
if (nullptr == edge1)
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1,0);
}
ring_vertex1[1] = vertex1;
edge1 = m_fsh.AllocateEdge(
center_vertex1,
ON_SubDSectorType::IgnoredSectorWeight,
vertex1,
at_crease2_weight // ingored unless vertex1 is tagged as a crease
);
if ( nullptr == edge1)
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1,1);
if (0==i)
continue;
face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs);
if ( nullptr == face1)
return ON_SUBD_RETURN_ERROR(false);
}
// add the quad with diagonal from center to initial ring vertex
// to finish quad subdivision of the initial face.
face1_eptrs[0] = face1_eptrs[3].Reversed();
edge1 = m_fsh.AllocateEdge(
ring_vertex1[1],
ON_SubDSectorType::IgnoredSectorWeight,
ring_vertex1[0],
ON_SubDQuadFaceTopology_CopySectorWeight(edge0,face->Vertex(0))
);
if ( nullptr == edge1)
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1,0);
edge1 = m_fsh.AllocateEdge(
ring_vertex1[0],
ON_SubDQuadFaceTopology_CopySectorWeight(face->Edge(0),face->Vertex(0)),
const_cast<ON_SubDVertex*>(ring_vertex1[0]->m_next_vertex),
ON_SubDSectorType::IgnoredSectorWeight
);
if ( nullptr == edge1)
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1,0);
face1_eptrs[3] = center_vertex1->m_edges[0].Reversed();
face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs);
if ( nullptr == face1)
return ON_SUBD_RETURN_ERROR(false);
// order edges and faces radially counterclockwise with respect to the initial face's orientation.
vertex1 = center_vertex1;
for (unsigned int i = 0; i < N; i++)
{
if ( nullptr == vertex1)
{
// If this failure occurs, there is a bug in the code above
// or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex()
// sets m_next_vertex.
return ON_SUBD_RETURN_ERROR(false);
}
vertex1 = const_cast<ON_SubDVertex*>(vertex1->m_next_vertex);
if ( nullptr == vertex1)
{
// If this failure occurs, there is a bug in the code above
// or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex()
// sets m_next_vertex.
return ON_SUBD_RETURN_ERROR(false);
}
// vertex1 = subdivided corner vertex of initial face
if ( 2 != vertex1->m_edge_count || 1 != vertex1->m_face_count )
{
// If this failure occurs, there is a bug in the code above
// or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex()
// sets m_next_vertex.
return ON_SUBD_RETURN_ERROR(false);
}
if (0 == i && vertex1 != ring_vertex1[0])
{
// If this failure occurs, there is a bug in the code above
// or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex()
// sets m_next_vertex.
return ON_SUBD_RETURN_ERROR(false);
}
face1_eptrs[0] = vertex1->m_edges[0];
vertex1->m_edges[0] = vertex1->m_edges[1];
vertex1->m_edges[1] = face1_eptrs[0];
//vertex1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial;
vertex1 = const_cast<ON_SubDVertex*>(vertex1->m_next_vertex);
if ( nullptr == vertex1)
{
// If this failure occurs, there is a bug in the code above
// or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex()
// sets m_next_vertex.
return ON_SUBD_RETURN_ERROR(false);
}
// vertex1 = subdivided edge vertex of initial face
if ( 3 != vertex1->m_edge_count || 2 != vertex1->m_face_count )
{
// If this failure occurs, there is a bug in the code above
// or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex()
// sets m_next_vertex.
return ON_SUBD_RETURN_ERROR(false);
}
if (0 == i)
{
// edges of the first vertex of this type are created
// in a different order than subsequent vertices.
face1_eptrs[0] = vertex1->m_edges[0];
vertex1->m_edges[0] = vertex1->m_edges[1];
vertex1->m_edges[1] = face1_eptrs[0];
}
else
{
face1_eptrs[0] = vertex1->m_edges[0];
vertex1->m_edges[0] = vertex1->m_edges[2];
vertex1->m_edges[2] = face1_eptrs[0];
const ON_SubDFace* tmp_f = vertex1->m_faces[0];
vertex1->m_faces[0] = vertex1->m_faces[1];
vertex1->m_faces[1] = tmp_f;
}
//vertex1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial;
}
if ( vertex1 != ring_vertex1[1])
{
// If this failure occurs, there is a bug in the code above
// or somebody changed the way ON_SubD_FixedSizeHeap::AllocateVertex()
// sets m_next_vertex.
return ON_SUBD_RETURN_ERROR(false);
}
/////////////////////////////////////////////////////////////////////////////////
//
// At each corner of the original face, add the outer subdivision quads
//
// Create subdivision edges that radiate from the sides of the original faces
ON_SubDVertex* vertex1_trio[3] = { nullptr, nullptr, center_vertex1 };
ON_SubDEdge* edge1_quartet[4] = {};
face_m_edges = face->m_edge4;
for (unsigned int i = 0; i < N; i++, face_m_edges++)
{
vertex1_trio[2] = const_cast<ON_SubDVertex*>(vertex1_trio[2]->m_next_vertex->m_next_vertex);
if (4 == i)
face_m_edges = face->m_edgex;
edge0 = ON_SUBD_EDGE_POINTER(face_m_edges->m_ptr);
if ( false == edge0->IsSmooth(true) || edge0->m_face_count != 2 )
continue;
const ON_SubDFace* face0 = edge0->NeighborFace(face,true);
if ( nullptr == face0 )
return ON_SUBD_RETURN_ERROR(false);
vertex1 = m_fsh.AllocateVertex(face0,subd_type,bUseSavedSubdivisionPoint,3,2);
if ( nullptr == vertex1 )
return ON_SUBD_RETURN_ERROR(false);
edge1 = m_fsh.AllocateEdge(vertex1_trio[2],ON_SubDSectorType::IgnoredSectorWeight,vertex1,ON_SubDSectorType::IgnoredSectorWeight);
if ( nullptr == edge1 )
return ON_SUBD_RETURN_ERROR(false);
if ( i+1 == N)
edge1_quartet[3] = edge1;
}
// now visit each corner and add subdivision quads
ON_SubDSectorIterator sit;
const ON_SubDEdge* edge0_duo[2] = {nullptr, face->Edge(N-1) };
const ON_SubDFace* neighbor0_duo[2] = {nullptr, (nullptr == edge0_duo[1] ? nullptr : edge0_duo[1]->NeighborFace(face,true)) };
face_m_edges = face->m_edge4;
for (unsigned int i = 0; i < N; i++, face_m_edges++)
{
if (4==i)
face_m_edges = face->m_edgex;
// edge0_duo[0] = face->Edge(i-1)
// edge0_duo[1] = face->Edge(i)
// neighbor0_duo[0] = original face neighbor across edge0_duo[0]
// neighbor0_duo[1] = original face neighbor across edge0_duo[0]
// vertex0 = face->Vertex(i)
edge0_duo[0] = edge0_duo[1];
neighbor0_duo[0] = neighbor0_duo[1];
edge0_duo[1] = ON_SUBD_EDGE_POINTER(face_m_edges->m_ptr);
neighbor0_duo[1] = (nullptr == edge0_duo[1] ? nullptr : edge0_duo[1]->NeighborFace(face,true));
vertex0 = edge0_duo[1]->m_vertex[ON_SUBD_EDGE_DIRECTION(face_m_edges->m_ptr)];
// tests above edge0_duo[0], edge0_duo[1], and vertex0 are not null.
const double e0_w = ON_SubDQuadFaceTopology_CopySectorWeight(edge0_duo[0], vertex0);
const double e1_w = ON_SubDQuadFaceTopology_CopySectorWeight(edge0_duo[1], vertex0);
double sector_weight;
if (e0_w == e1_w)
sector_weight = e0_w;
else
{
if (ON_SubDSectorType::IgnoredSectorWeight == e0_w)
sector_weight = e1_w;
else if (ON_SubDSectorType::IgnoredSectorWeight == e1_w)
sector_weight = e0_w;
else if (ON_SubDSectorType::UnsetSectorWeight == e0_w)
sector_weight = e1_w;
else if (ON_SubDSectorType::UnsetSectorWeight == e1_w)
sector_weight = e0_w;
else
sector_weight = ON_UNSET_VALUE;
if (!(sector_weight >= 0.0 && sector_weight < 1.0))
sector_weight = ON_SubDSectorType::UnsetSectorWeight;
}
// vertex1_trio[0] = subdivision vertex on edge0_duo[0]
// vertex1_trio[1] = subdivision vertex on face->Vertex(i)
// vertex1_trio[2] = subdivision vertex on edge0_duo[1]
// edge1_quartet[0] = null or subdivision edge from vertex1_trio[0] to neighbor0_duo[0] subdivision point
// edge1_quartet[1] = subdivision edge from vertex1_trio[0] to vertex1_trio[1]
// edge1_quartet[2] = subdivision edge from vertex1_trio[1] to vertex1_trio[2]
// edge1_quartet[3] = null or subdivision edge from vertex1_trio[2] to neighbor0_duo[1] subdivision point
vertex1_trio[0] = vertex1_trio[2];
vertex1_trio[1] = const_cast< ON_SubDVertex* >(( 0 == i ) ? center_vertex1->m_next_vertex : vertex1_trio[0]->m_next_vertex);
vertex1_trio[2] = const_cast< ON_SubDVertex* >(vertex1_trio[1]->m_next_vertex);
edge1_quartet[0] = edge1_quartet[3];
edge1_quartet[1] = ON_SUBD_EDGE_POINTER(vertex1_trio[1]->m_edges[1].m_ptr);
edge1_quartet[2] = ON_SUBD_EDGE_POINTER(vertex1_trio[1]->m_edges[0].m_ptr);
edge1_quartet[3] = (nullptr == vertex1_trio[2]) ? nullptr : ON_SUBD_EDGE_POINTER(vertex1_trio[2]->m_edges[3].m_ptr);
if (edge0_duo[0]->IsCrease(false) && edge0_duo[1]->IsCrease(false))
{
// no outer quads at this corner
continue;
}
if (2 != edge0_duo[0]->m_face_count && 2 != edge0_duo[1]->m_face_count)
{
// no outer quads at this corner
continue;
}
if (vertex0->m_face_count <= 1 || vertex0->m_edge_count <= 2)
{
// error condition that we can tolerate
ON_SubDIncrementErrorCount();
continue;
}
/////////////////////////////////////////////////////////////////////////////////
//
// There is at least one outer subdivision quads around the inner corner subd quad at face->Vertex(i)
face1_eptrs[0] = ON_SubDEdgePtr::Null;
face1_eptrs[1] = ON_SubDEdgePtr::Null;
face1_eptrs[2] = ON_SubDEdgePtr::Null;
face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[1],1);
if ( vertex0 != sit.Initialize(face,0,i) )
return ON_SUBD_RETURN_ERROR(false);
const ON_SubDFace* face0 = sit.NextFace(true);
if ( edge0_duo[0] != sit.CurrentEdge(0) || face0 != neighbor0_duo[0])
return ON_SUBD_RETURN_ERROR(false);
if (nullptr != face0)
{
// construct the first subd quad counter-clockwise from the interior corner quad at face->Vertex(i).
edge0 = sit.CurrentEdge(1);
if ( nullptr == edge0 )
return ON_SUBD_RETURN_ERROR(false);
vertex1 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,3,2);
if ( nullptr == vertex1 )
return ON_SUBD_RETURN_ERROR(false);
edge1 = m_fsh.AllocateEdge(const_cast<ON_SubDVertex*>(edge1_quartet[0]->m_vertex[1]),ON_SubDSectorType::IgnoredSectorWeight,vertex1,at_crease2_weight);
if ( nullptr == edge1 )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1_quartet[1], 1);
face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1_quartet[0], 0);
face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1,0);
// vertex1_trio[1] is a subdivision of an input face vertex.
edge1 = m_fsh.AllocateEdge(vertex1_trio[1], sector_weight, vertex1, at_crease2_weight);
if ( nullptr == edge1 )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1,1);
face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs);
if ( nullptr == face1 )
return ON_SUBD_RETURN_ERROR(false);
}
else
{
// edge0_duo[0] is a crease
face1_eptrs[0] = ON_SubDEdgePtr::Null;
face1_eptrs[1] = ON_SubDEdgePtr::Null;
face1_eptrs[2] = ON_SubDEdgePtr::Null;
face1_eptrs[3] = ON_SubDEdgePtr::Null;
}
bool bFinishedCorner = false;
bool bCreases = false;
const unsigned int vertex_face_count = vertex0->m_face_count;
unsigned int vertex1_sort_mark = 0;
for (unsigned int sit_limit = 0; sit_limit < vertex_face_count; sit_limit++)
{
if ( nullptr != face0 )
face0 = sit.NextFace(true);
if (nullptr == face0)
{
if ( bCreases )
return ON_SUBD_RETURN_ERROR(false);
bCreases = true;
if (nullptr == neighbor0_duo[1])
{
// face->Edge(i) is a crease
bFinishedCorner = true;
break;
}
if ( nullptr == sit.Initialize(face,0,i))
return ON_SUBD_RETURN_ERROR(false);
face0 = sit.IncrementToCrease(-1);
if ( nullptr == face0 || face0 == face)
return ON_SUBD_RETURN_ERROR(false);
edge0 = sit.CurrentEdge(0);
if ( nullptr == edge0 )
return ON_SUBD_RETURN_ERROR(false);
vertex1_sort_mark = vertex1_trio[1]->m_face_count;
vertex1 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,2,1);
if ( nullptr == vertex1 )
return ON_SUBD_RETURN_ERROR(false);
edge1 = m_fsh.AllocateEdge(vertex1_trio[1],ON_SubDSectorType::IgnoredSectorWeight,vertex1,ON_SubDSectorType::IgnoredSectorWeight);
if ( nullptr == edge1 )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[0] = ON_SubDEdgePtr::Null;
face1_eptrs[1] = ON_SubDEdgePtr::Null;
face1_eptrs[2] = ON_SubDEdgePtr::Null;
face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1,1);
}
else if ( face0 == face)
return ON_SUBD_RETURN_ERROR(false);
edge0 = sit.CurrentEdge(1);
if ( nullptr == edge0 || edge0 == edge0_duo[0] )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[0] = face1_eptrs[3].Reversed();
face1_eptrs[1] = ON_SubDEdgePtr::Null;
face1_eptrs[2] = ON_SubDEdgePtr::Null;
face1_eptrs[3] = ON_SubDEdgePtr::Null;
if (face0 == neighbor0_duo[1])
{
// construct the last subd quad clockwise from the interior corner quad at face->Vertex(i).
if (edge0 != edge0_duo[1])
return ON_SUBD_RETURN_ERROR(false);
edge1 = ON_SUBD_EDGE_POINTER(face1_eptrs[0].m_ptr);
if ( nullptr == edge1 )
return ON_SUBD_RETURN_ERROR(false);
vertex1 = const_cast<ON_SubDVertex*>(edge1->m_vertex[1]);
if ( nullptr == vertex1 )
return ON_SUBD_RETURN_ERROR(false);
// vertex1 is from subdividing an edge that radiates out from the input face.
edge1 = m_fsh.AllocateEdge(
vertex1, at_crease2_weight,
const_cast<ON_SubDVertex*>(edge1_quartet[3]->m_vertex[1]),ON_SubDSectorType::IgnoredSectorWeight);
face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1,0);
face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1_quartet[3],1);
face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[2],1);
face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs);
if ( nullptr == face1 )
return ON_SUBD_RETURN_ERROR(false);
bFinishedCorner = true;
break;
}
if (edge0 == edge0_duo[1])
return ON_SUBD_RETURN_ERROR(false);
edge1 = ON_SUBD_EDGE_POINTER(face1_eptrs[0].m_ptr);
if ( nullptr == edge1 )
return ON_SUBD_RETURN_ERROR(false);
vertex1 = const_cast<ON_SubDVertex*>(edge1->m_vertex[1]);
if ( nullptr == vertex1 )
return ON_SUBD_RETURN_ERROR(false);
ON_SubDVertex* vertex2 = m_fsh.AllocateVertex(face0,subd_type,bUseSavedSubdivisionPoint,3,2);
if ( nullptr == vertex2 )
return ON_SUBD_RETURN_ERROR(false);
// vertex1 is from subdividing an edge that radiates out from the input face.
edge1 = m_fsh.AllocateEdge( vertex1, at_crease2_weight, vertex2, ON_SubDSectorType::IgnoredSectorWeight );
if ( nullptr == edge1 )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1,0);
ON_SubDVertex* vertex3 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,3,2);
if ( nullptr == vertex3 )
return ON_SUBD_RETURN_ERROR(false);
edge1 = m_fsh.AllocateEdge(vertex2,ON_SubDSectorType::IgnoredSectorWeight,vertex3,ON_SubDSectorType::IgnoredSectorWeight);
if ( nullptr == edge1 )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1,0);
edge1 = m_fsh.AllocateEdge(vertex1_trio[1],sector_weight,vertex3,ON_SubDSectorType::IgnoredSectorWeight);
if ( nullptr == edge1 )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1,1);
face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs);
if ( nullptr == face1 )
return ON_SUBD_RETURN_ERROR(false);
}
if ( !bFinishedCorner )
return ON_SUBD_RETURN_ERROR(false);
}
m_center_vertex1 = center_vertex1;
m_subd_type = subd_type;
m_face0 = face;
m_face1 = m_center_vertex1->m_faces;
m_face1_count = m_center_vertex1->m_face_count;
return true;
}
#endif