mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 19:46:08 +08:00
3760 lines
121 KiB
C++
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
|