Files
opennurbs/opennurbs_subd_limit.cpp
2024-08-22 01:43:04 -07:00

3695 lines
115 KiB
C++

//
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
#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"
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_SubDEdgeTag::SmoothX is an error here
if ( false == quad_edge[fi]->IsCrease() )
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_SubDEdgeTag::SmoothX is permitted here
if ( 2 != quad_edge[fi]->m_face_count)
return ON_SUBD_RETURN_ERROR(false);
if (quad_edge[fi]->IsCrease())
{
unsigned int dart_count = 0;
unsigned int crease_count = 0;
if ( ON_SubDVertexTag::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_SubDVertexTag::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())
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())
continue;
if (e->IsCrease() && ON_SubDVertexTag::Dart == quad_vertex[fi]->m_vertex_tag)
continue;
}
if (false == e->IsCrease())
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;
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);
// Test for exact tag here - do not call e->IsSmooth() because this is a rare case where X tags need to be rejected.
if ( false == e->IsSmoothNotXNotSharp() )
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;
// If we get this far, then this edge has
// the standard smooth edge Catmull Clark subdivision point.
}
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;
if (m_sharp_edge_count > 0)
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;
// When the original SubD face is an n-gon, it is subdivided into quads and m_initial_subdivision_level = 1.
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]->IsSmoothNotX();
bCenterEdgeIsSmooth[fvi1] = (0 == delta_subdivision_level) ? m_center_edges[fvi1]->IsSmoothNotX() : true;
bCenterEdgeIsSmooth[fvi2] = (0 == delta_subdivision_level) ? m_center_edges[fvi2]->IsSmoothNotX() : true;
bCenterEdgeIsSmooth[fvi3] = m_center_edges[fvi3]->IsSmoothNotX();
bool bCenterEdgeIsCrease[4] = {};
bCenterEdgeIsCrease[fvi0] = bCenterEdgeIsSmooth[fvi0] ? false : m_center_edges[fvi0]->IsCrease();
bCenterEdgeIsCrease[fvi1] = (0 == delta_subdivision_level) ? m_center_edges[fvi1]->IsCrease() : false;
bCenterEdgeIsCrease[fvi2] = (0 == delta_subdivision_level) ? m_center_edges[fvi2]->IsCrease() : false;
bCenterEdgeIsCrease[fvi3] = bCenterEdgeIsSmooth[fvi3] ? false : m_center_edges[fvi3]->IsCrease();
bool bEdgeTagX = false;
for (unsigned int i = 0; i < 4; i++)
{
if (
nullptr != m_center_edges[i]
&& ON_SubDEdgeTag::SmoothX != m_center_edges[i]->m_edge_tag
&& (bCenterEdgeIsSmooth[i] != bCenterEdgeIsCrease[i])
)
{
continue;
}
bEdgeTagX = true;
break;
}
m_sharp_edge_count = 0;
bool bSharpQuadrant[4] = {};
for(unsigned int i = 0; i < 4; ++i)
{
if (nullptr != m_center_edges[i] && m_center_edges[i]->IsSharp())
{
++m_sharp_edge_count;
bSharpQuadrant[0] = true;
bSharpQuadrant[1] = true;
bSharpQuadrant[2] = true;
bSharpQuadrant[3] = true;
}
if (nullptr != this->m_edge_grid[i][0] && this->m_edge_grid[i][0]->IsSharp())
{
++m_sharp_edge_count;
bSharpQuadrant[i] = true;
bSharpQuadrant[(i + 1U) % 4U] = true;
bSharpQuadrant[(i + 3U) % 4U] = true;
}
if (nullptr != this->m_edge_grid[i][1] && this->m_edge_grid[i][1]->IsSharp())
{
++m_sharp_edge_count;
bSharpQuadrant[i % 4U] = true;
bSharpQuadrant[(i + 1U) % 4U] = true;
bSharpQuadrant[(i + 3U) % 4U] = true;
}
}
////////////////////////////////////////////////////////////////////////////
//
// 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];
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]
};
for (unsigned int corner_index = 0; corner_index < 4; corner_index++)
{
// Dale Lear 2024 Feb 28 Fix RH-80676
// Even when bSharpQuadrant[corner_index] is true from tests above,
// we have to call quad_vertex[corner_index]->VertexSharpness() to
// insure all sharp edges are taken into account.
if (quad_vertex[corner_index]->VertexSharpness() > 0.0)
{
bSharpQuadrant[corner_index] = true;
bSharpQuadrant[(corner_index + 1U) % 4U] = true;
bSharpQuadrant[(corner_index + 3U) % 4U] = true;
}
}
if (false == bEdgeTagX)
{
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 (quad_vertex[corner_index]->IsDart())
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())
{
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())
{
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() && e2->IsCrease())
{
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 (bSharpQuadrant[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();
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(ON_SubDSectorIterator::StopAt::Boundary))
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)
{
ON_SubDSectorIterator::StopAt stop_at
= bIsDartVertex[qfei]
? ON_SubDSectorIterator::StopAt::Boundary
: ON_SubDSectorIterator::StopAt::AnyCrease
;
corner_faces[qfei] = sit0.PrevFace(stop_at);
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(ON_SubDSectorIterator::StopAt::Boundary))
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])
{
ON_SubDSectorIterator::StopAt stop_at
= bIsDartVertex[qfei3]
? ON_SubDSectorIterator::StopAt::Boundary
: ON_SubDSectorIterator::StopAt::AnyCrease
;
corner_faces[qfei] = sit1.NextFace(stop_at);
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 = (unsigned char)(center_quad_face->m_level_zero_face_id > 0 ? center_quad_face->SubdivisionLevel() : 0U);
m_current_subdivision_level = m_initial_subdivision_level;
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_SubDVertexTag::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(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(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(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(
bool bEnableApproximatePatch,
unsigned int quadrant_index
)
{
// When subdivision_count >= 2, there should be 3 or 4 or more exact quadrants.
// When the original SubD face is an n-gon, it is subdivided into quads and m_initial_subdivision_level = 1.
const unsigned char subdivision_count
= (m_current_subdivision_level > m_initial_subdivision_level)
? m_current_subdivision_level
: 0; if (bEnableApproximatePatch && (subdivision_count < 2 || m_extraordinary_corner_vertex_count > 1))
{
ON_SUBD_ERROR("bEnableApproximatePatch should be false at this stage.");
bEnableApproximatePatch = false;
}
if (nullptr == m_face_grid[1][1] || 4 != m_face_grid[1][1]->m_edge_count || quadrant_index > 4)
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* center_vertices[4] = { m_vertex_grid[1][1], m_vertex_grid[2][1], m_vertex_grid[2][2], m_vertex_grid[1][2] };
for (unsigned int i = 0; i < 4; i++)
{
if (nullptr == center_vertices[i])
return ON_SUBD_RETURN_ERROR(0);
if (nullptr == m_center_edges[i])
return ON_SUBD_RETURN_ERROR(0);
}
ON_2dex dex;
ON_2dex deltadex;
const ON_SubDFace* face;
unsigned int i;
double Q[3][3];
double* P1[3];
if (!ON_IsValid(m_srf_cv1[2][2][0]))
{
// All sub surfaces require inner 3x3 grid of subdivision points
// In all cases these are the 9 subdivision points of the central quad
// 9 points
// = the quad face subdivision point at m_srf_cv1[2][2]
// + 4 edge subdivision pointsat m_srf_cv1[2][1], m_srf_cv1[3][2], m_srf_cv1[2][3], m_srf_cv1[1][2]
// + 4 vertex subdivision points at m_srf_cv1[1][1], m_srf_cv1[3][1], m_srf_cv1[3][3], m_srf_cv1[1][3]
face = m_face_grid[1][1];
// 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(Q[2]))
{
return ON_SUBD_RETURN_ERROR(0);
}
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 (!center_vertices[fei]->GetSubdivisionPoint( Q[0]))
return ON_SUBD_RETURN_ERROR(0);
if (!m_center_edges[fei]->GetSubdivisionPoint( Q[1]))
return ON_SUBD_RETURN_ERROR(0);
dex = srf_cv_dex[2 * fei];
P1[0] = m_srf_cv1[dex.i][dex.j];
P1[0][0] = Q[0][0]; P1[0][1] = Q[0][1]; P1[0][2] = Q[0][2];
dex = srf_cv_dex[2 * fei + 1];
P1[1] = m_srf_cv1[dex.i][dex.j];
P1[1][0] = Q[1][0]; P1[1][1] = Q[1][1]; P1[1][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[2] = m_srf_cv1[2][2];
P1[2][0] = Q[2][0]; P1[2][1] = Q[2][1]; P1[2][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 set_quadrant_count = 0;
unsigned int exact_quadrant_count = 0;
for (unsigned int fvi = fvi_min; fvi < fvi_max; fvi++)
{
if (m_bExactQuadrantPatch[fvi])
++exact_quadrant_count;
else if (false == bEnableApproximatePatch)
continue;
unsigned int set_cv_count = 9; // 3x3 grid is set
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[0] = &m_srf_cv1[dex.i][dex.j][0];
dex.i += deltadex.i;
dex.j += deltadex.j;
P1[1] = &m_srf_cv1[dex.i][dex.j][0];
dex.i += deltadex.i;
dex.j += deltadex.j;
P1[2] = &m_srf_cv1[dex.i][dex.j][0];
if (ON_IsValid(P1[0][0]) && ON_IsValid(P1[1][0]) && ON_IsValid(P1[2][0]))
{
set_cv_count += 3;
continue; // Already set
}
// m_bBoundaryCrease[] is true if m_center_edges[fvi]->IsCrease() is true AND neither end is a dart vertex.
// Using bCenterEdgeCrease0 is required to get correct results when bEnableApproximatePatch is true
const bool bCenterEdgeCrease = bEnableApproximatePatch ? m_center_edges[side_fvi]->IsCrease() : m_bBoundaryCrease[side_fvi];
if ( bCenterEdgeCrease )
{
// When approximation is being applied (m_bExactQuadrantPatch[fvi] is false and bEnableApproximatePatch is true),
// This technique insures the approximations on either side of the crease
// create a continuous edge.
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_bExactQuadrantPatch[fvi] || subdivision_count >= 2) && m_center_edges[side_fvi]->IsSmoothNotX() )
{
const ON_SubDEdge* edge = m_edge_grid[side_fvi][0];
if (nullptr == edge)
{
if (m_bExactQuadrantPatch[fvi])
return ON_SUBD_RETURN_ERROR(0);
Q[0][0] = ON_UNSET_VALUE;
}
else if (!edge->GetSubdivisionPoint(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)
{
if (m_bExactQuadrantPatch[fvi])
return ON_SUBD_RETURN_ERROR(0);
Q[1][0] = ON_UNSET_VALUE;
}
else if (!face->GetSubdivisionPoint(Q[1]))
return ON_SUBD_RETURN_ERROR(0);
edge = m_edge_grid[side_fvi][1];
if (nullptr == edge)
{
if (m_bExactQuadrantPatch[fvi])
return ON_SUBD_RETURN_ERROR(0);
Q[2][0] = ON_UNSET_VALUE;
}
else if (!edge->GetSubdivisionPoint(Q[2]))
return ON_SUBD_RETURN_ERROR(0);
}
else
{
// Need more subdivisions before these can be set.
continue;
}
for (i = 0; i < 3; i++)
{
if (ON_IsValid(P1[i][0]))
{
++set_cv_count;
continue;
}
if (ON_IsValid(Q[i][0]))
{
++set_cv_count;
P1[i][0] = Q[i][0];
P1[i][1] = Q[i][1];
P1[i][2] = Q[i][2];
}
}
}
if (
m_bExactQuadrantPatch[fvi]
||
(false == m_bExtraordinaryCornerVertex[fvi] && bEnableApproximatePatch))
{
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 0, 0);
P1[0] = &m_srf_cv1[dex.i][dex.j][0];
if (ON_IsValid(P1[0][0]))
{
++set_cv_count;
}
else
{
if ( center_vertices[fvi]->IsSmooth() )
{
if (4 == center_vertices[fvi]->m_edge_count && 4 == center_vertices[fvi]->m_face_count)
{
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(Q[0]))
return ON_SUBD_RETURN_ERROR(0);
++set_cv_count;
P1[0][0] = Q[0][0]; P1[0][1] = Q[0][1]; P1[0][2] = Q[0][2];
}
}
else if (center_vertices[fvi]->IsCrease())
{
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];
if (ON_IsValid(Q[0][0]) && ON_IsValid(Q[1][0]) && ON_IsValid(Q[2][0]))
{
dex = ON_SubDQuadNeighborhood::GridDex(5, fvi, 1, 1);
if (ON_IsValid(m_srf_cv1[dex.i][dex.j][0]))
{
const double c = -8.0;
const double b = 4.0;
++set_cv_count;
P1[0][0] = c * m_srf_cv1[dex.i][dex.j][0] + b * (Q[0][0] + Q[1][0]) + Q[2][0];
P1[0][1] = c * m_srf_cv1[dex.i][dex.j][1] + b * (Q[0][1] + Q[1][1]) + Q[2][1];
P1[0][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] && false == m_bBoundaryCrease[fvi3])
{
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];
if (ON_IsValid(Q[0][0]) && ON_IsValid(Q[1][0]))
{
++set_cv_count;
P1[0][0] = 2.0 * Q[0][0] - Q[1][0];
P1[0][1] = 2.0 * Q[0][1] - Q[1][1];
P1[0][2] = 2.0 * Q[0][2] - Q[1][2];
}
}
else if (false == m_bBoundaryCrease[fvi] && 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];
if (ON_IsValid(Q[0][0]) && ON_IsValid(Q[1][0]))
{
++set_cv_count;
P1[0][0] = 2.0 * Q[0][0] - Q[1][0];
P1[0][1] = 2.0 * Q[0][1] - Q[1][1];
P1[0][2] = 2.0 * Q[0][2] - Q[1][2];
}
}
else if (m_bExactQuadrantPatch[fvi])
{
ON_SUBD_ERROR("Why is this happening?");
}
}
}
}
if (16 == set_cv_count)
{
// quadrant with index fvi is set;
++set_quadrant_count;
}
}
if (exact_quadrant_count > set_quadrant_count)
return ON_SUBD_RETURN_ERROR(set_quadrant_count);
return set_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);
const unsigned int set_quadrant_count = SetLimitSubSurfaceExactCVs(false,fvi);
if ( 1 != set_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_SubDVertexTag 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_SubDVertexTag::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_SubDVertexTag::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)
{
if (nullptr != m_center_edges[0] && m_center_edges[0]->IsSmooth())
{
switch (i)
{
case 0:
if (false == m_bExtraordinaryCornerVertex[0])
{
const ON_SubDVertex* v = this->CenterVertex(0);
if (nullptr != v && v->IsSmooth())
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])
{
const ON_SubDVertex* v = this->CenterVertex(1);
if (nullptr != v && v->IsSmooth())
f = m_face_grid[2][0];
}
break;
}
}
}
else if (4 == i)
{
if (nullptr != m_center_edges[1] && m_center_edges[1]->IsSmooth())
{
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])
{
const ON_SubDVertex* v = this->CenterVertex(2);
if (nullptr != v && v->IsSmooth())
f = m_face_grid[2][2];
}
break;
}
}
}
else if (4 == j)
{
if (nullptr != m_center_edges[2] && m_center_edges[2]->IsSmooth())
{
switch (i)
{
case 0:
if (false == m_bExtraordinaryCornerVertex[3])
{
const ON_SubDVertex* v = this->CenterVertex(3);
if (nullptr != v && v->IsSmooth())
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)
{
if (nullptr != m_center_edges[3] && m_center_edges[3]->IsSmooth())
{
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;
if (nullptr != e)
{
const int extraordinary_vertex_index = ExtraordinaryCenterVertexIndex(ON_SubDVertexTag::Crease, 4);
const ON_SubDVertex* extraordinary_vertex
= (extraordinary_vertex_index >= 0 && extraordinary_vertex_index < 4)
? CenterVertex(extraordinary_vertex_index)
: nullptr;
if ( e->m_vertex[0] == extraordinary_vertex || e->m_vertex[1] == extraordinary_vertex )
{
// an extraordinary crease vertex is on this edge
bHaveApproximateCV = false;
}
else
{
bHaveApproximateCV = e->GetSubdivisionPoint(approximate_cv);
}
}
else if ( nullptr != f && 4 == f->m_edge_count )
bHaveApproximateCV = f->GetSubdivisionPoint(approximate_cv);
else
{
bHaveApproximateCV = false;
}
return bHaveApproximateCV;
}
static bool ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(
const ON_SubDEdge* e0,
const ON_SubDVertex* e0v,
ON_SubDEdge* e1,
const ON_SubDVertex* e1v
)
{
if (nullptr == e0 || nullptr == e0v || nullptr == e1 || nullptr == e1v)
return ON_SUBD_RETURN_ERROR(false);
if (e0->IsSharp())
{
const bool bReversed = ((e0->m_vertex[0] == e0v) ? 0 : 1) != ((e1->m_vertex[0] == e1v) ? 0 : 1);
e1->SetSharpnessForExperts(e0->SubdivideSharpness(e0v, bReversed));
}
else
e1->ClearSharpnessForExperts();
return true;
}
static bool ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(
const ON_SubDEdge* e0,
const ON_SubDVertex* e0v,
ON_SubDEdgePtr e1,
const ON_SubDVertex* e1v
)
{
return ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(e0, e0v, e1.Edge(), e1v);
}
static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge(
ON_SubD_FixedSizeHeap& fsh,
bool bUseFindOrAllocate,
ON_SubDVertex* qv1,
const ON_SubDVertex* qv0,
const ON_SubDEdge* e0
)
{
if (nullptr == qv1 || nullptr == e0)
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
ON_SubDVertex* v1 = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate,e0);
if (nullptr == v1)
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
double v0_weight;
if ( e0->IsSmooth() && nullptr != qv0)
{
// qv1 is the subdivision point of qv0.
if ( qv1->m_vertex_tag != qv0->m_vertex_tag )
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
if ( ON_SubDVertexTag::Smooth == qv0->m_vertex_tag)
v0_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
else
{
v0_weight = ON_SubDSectorType::CopyEdgeSectorCoefficient(e0, qv0, ON_UNSET_VALUE);
if (false == ON_SubDSectorType::IsValidSectorCoefficientValue(v0_weight,false))
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
}
}
else
v0_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
const double v1_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
ON_SubDEdgePtr ep1 = fsh.AllocateEdge(bUseFindOrAllocate,qv1,v0_weight,v1,v1_weight);
ON_SubDEdge* e1 = ep1.Edge();
if (nullptr == e1)
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
if (e1->m_edge_tag != e0->m_edge_tag)
{
// On the first subdivision step,
// e0 with tag ON_SubDEdgeTag::SmoothX turns into
// e1 with tag ON_SubDEdgeTag::Smooth.
if ( ON_SubDEdgeTag::Smooth != e1->m_edge_tag || ON_SubDEdgeTag::SmoothX != e0->m_edge_tag)
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
}
ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(e0, qv0, e1, qv1);
return ep1;
}
static ON_SubDFace* ON_SubDQuadFaceTopology_SubdivideFace(
ON_SubD_FixedSizeHeap& fsh,
const ON_SubDFace* f0,
ON_SubDEdgePtr e1[2],
double at_crease_weight
)
{
ON_SubDVertex* v[4];
if (nullptr == f0 || nullptr == e1[0].Edge() || nullptr == e1[1].Edge())
return ON_SUBD_RETURN_ERROR(nullptr);
v[0] = const_cast<ON_SubDVertex*>(e1[0].RelativeVertex(0));
if (nullptr == v[0] || v[0] != e1[1].RelativeVertex(0))
return ON_SUBD_RETURN_ERROR(nullptr);
v[1] = const_cast<ON_SubDVertex*>(e1[0].RelativeVertex(1));
if (nullptr == v[1] )
return ON_SUBD_RETURN_ERROR(nullptr);
v[3] = const_cast<ON_SubDVertex*>(e1[1].RelativeVertex(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.FindOrAllocateVertex(f0);
if ( nullptr == v[2])
return ON_SUBD_RETURN_ERROR(nullptr);
const double v1_weight = (ON_SubDVertexTag::Crease == v[1]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorCoefficient;
const double v2_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
const double v3_weight = (ON_SubDVertexTag::Crease == v[3]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorCoefficient;
ON_SubDEdgePtr e12 = fsh.AllocateEdge(v[1],v1_weight,v[2],v2_weight);
if ( nullptr == e12.Edge())
return ON_SUBD_RETURN_ERROR(nullptr);
// e12 is interior to f0 and has no sharpness
ON_SubDEdgePtr e23 = fsh.AllocateEdge(v[2],v2_weight,v[3],v3_weight);
if ( nullptr == e23.Edge())
return ON_SUBD_RETURN_ERROR(nullptr);
// e23 is interior to f0 and has no sharpness
ON_SubDEdgePtr f1_epts[4] = { e1[0], e12, e23, e1[1].Reversed() };
ON_SubDFace* f1 = fsh.AllocateQuad(f0->m_level_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);
// 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::CreaseSectorCoefficient(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_level_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 )
return ON_SUBD_RETURN_ERROR(false);
if (nullptr == qv0 || qv0->m_face_count > qv0->m_edge_count)
{
if (ON_SubDVertexTag::Corner == qv0->m_vertex_tag)
{
// nonmanifold case can have face_count > edge_count
if ( qv0->m_edge_count < 3 )
return ON_SUBD_RETURN_ERROR(false);
}
else
{
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);
// When qv0 is a valence 2 vertex with triangular faces, we need to use find or allocate.
const bool bUseFindOrAllocate = (2 == N && 3 == qv0->MinimumFaceEdgeCount());
ON_SubDSectorIterator sit;
if ( nullptr == sit.Initialize(qf0,0,qv0) )
return ON_SUBD_RETURN_ERROR(false);
const bool bIsDartSector = (ON_SubDVertexTag::Dart == qv0->m_vertex_tag);
const bool bIsCreaseOrCornerSector = qv0->IsCreaseOrCorner();
const bool bBoundaryCrease1[4] = {
m_bBoundaryCrease[q0fvi] || (bIsCreaseOrCornerSector && qf0_edges[0]->IsCrease()),
false,
false,
m_bBoundaryCrease[(q0fvi+3)%4] || (bIsCreaseOrCornerSector && qf0_edges[3]->IsCrease())
};
//const bool bStopAtInternalCrease = (false == bIsDartSector);
const ON_SubDSectorIterator::StopAt stop_at
= (bIsDartSector)
? ON_SubDSectorIterator::StopAt::Boundary
: ON_SubDSectorIterator::StopAt::AnyCrease
;
if ( false == fsh.ReserveSubDWorkspace(qf0) )
return ON_SUBD_RETURN_ERROR(false);
ON_SubDVertex* qv1 = fsh.AllocateVertex(qv0,N);
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_SubDEdgePtr edge_grid1[4][2] = {};
// edge1 = new edge from qv1 to edge0 subdivision point
ON_SubDEdgePtr edgep1 = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, bUseFindOrAllocate,qv1,qv0,edge0);
if (edgep1.IsNull())
return ON_SUBD_RETURN_ERROR(false);
// prepare to rotate counterclockwise around qv0,
// subdividing edges and adding new faces
const ON_SubDEdge* e0[2] = {nullptr, edge0};
ON_SubDEdgePtr e1[2] = {ON_SubDEdgePtr::Null, edgep1};
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] = edgep1; // back to where we started
else
e1[1] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, bUseFindOrAllocate, qv1, qv0, e0[1]);
if (e1[1].IsNull())
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] = f1->EdgePtr(1);
edge_grid1[3][1] = f1->EdgePtr(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(stop_at);
if (nullptr == f0)
bAtBoundaryCrease = true;
if (e0[1] != sit.CurrentEdge(0))
return ON_SUBD_RETURN_ERROR(false);
}
if (bAtBoundaryCrease)
{
e1[1].Edge()->m_edge_tag = ON_SubDEdgeTag::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(stop_at);
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] = edgep1;
e1[1] = ON_SubDEdgePtr::Null;
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, bUseFindOrAllocate, qv1, qv0, e0[0]);
if (e1[0].IsNull())
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(stop_at);
if (nullptr == f0 || ON_SubDEdgeTag::Crease == e0[1]->m_edge_tag)
{
bFinished = (nullptr == f0 && ON_SubDEdgeTag::Crease == e0[0]->m_edge_tag && qv1->m_face_count+1 == qv1->m_edge_count);
if (false == bFinished)
return ON_SUBD_RETURN_ERROR(false);
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] = f1->EdgePtr(0);
edge_grid1[0][1] = f1->EdgePtr(2);
}
// Add the 7 remaining elements to vertex_grid1[][]
if (false == bBoundaryCrease1[0])
{
// When the level 0 vertex is valence 2 and the neighbouring faces are triangles,
// this vertex needs to be added to the hash table
vertex_grid1[3][0] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, m_edge_grid[q0fvi][1]);
if ( nullptr == vertex_grid1[3][0])
return ON_SUBD_RETURN_ERROR(false);
}
vertex_grid1[3][1] = fsh.AllocateVertex(qf0_vertices[1],3);
if ( nullptr == vertex_grid1[3][1])
return ON_SUBD_RETURN_ERROR(false);
vertex_grid1[3][2] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, qf0_edges[1]);
if ( nullptr == vertex_grid1[3][2])
return ON_SUBD_RETURN_ERROR(false);
vertex_grid1[3][3] = fsh.AllocateVertex(qf0_vertices[2],2);
if ( nullptr == vertex_grid1[3][3])
return ON_SUBD_RETURN_ERROR(false);
vertex_grid1[2][3] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, qf0_edges[2]);
if ( nullptr == vertex_grid1[2][3])
return ON_SUBD_RETURN_ERROR(false);
vertex_grid1[1][3] = fsh.AllocateVertex(qf0_vertices[3],3);
if ( nullptr == vertex_grid1[1][3])
return ON_SUBD_RETURN_ERROR(false);
if (false == bBoundaryCrease1[3])
{
vertex_grid1[0][3] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, m_edge_grid[(q0fvi + 3) % 4][0]);
if ( nullptr == vertex_grid1[0][3])
return ON_SUBD_RETURN_ERROR(false);
}
edge_grid1[1][0] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[2][1],
ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[3][1],
ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[0], qf0_vertices[1], ON_UNSET_VALUE)
);
if ( edge_grid1[1][0].IsNull())
return ON_SUBD_RETURN_ERROR(false);
ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[0], qf0_vertices[1], edge_grid1[1][0], vertex_grid1[3][1]);
edge_grid1[1][1] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[2][2],
ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[3][2],
at_crease_weight // ignored unless vertex_grid1[3][2] is tagged as a crease
);
if (edge_grid1[1][1].IsNull())
return ON_SUBD_RETURN_ERROR(false);
// NOTE: edge_grid1[1][1] is interior to input face f0 and has zero sharpness
edge_grid1[2][0] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[2][2],
ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[2][3],
at_crease_weight // ignored unless vertex_grid1[2][3] is tagged as a crease
);
if (edge_grid1[2][0].IsNull())
return ON_SUBD_RETURN_ERROR(false);
// NOTE: edge_grid1[2][0] is interior to input face f0 and has zero sharpness
edge_grid1[2][1] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[1][2],
ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[1][3],
ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[3], qf0_vertices[3], ON_UNSET_VALUE)
);
if (edge_grid1[2][1].IsNull())
return ON_SUBD_RETURN_ERROR(false);
ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[3], qf0_vertices[3], edge_grid1[2][1], vertex_grid1[1][3]);
// Add the 5 remaining elements to face_grid1[][]
ON_SubDEdgePtr fedges[4];
if (false == bBoundaryCrease1[0])
{
fedges[0] = ON_SubDEdgePtr::Null;
fedges[1] = ON_SubDEdgePtr::Null;
fedges[2] = edge_grid1[1][0];
fedges[3] = edge_grid1[0][1];
if (fedges[2].IsNull() || fedges[3].IsNull() )
return ON_SUBD_RETURN_ERROR(false);
fedges[0] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[2][0],
ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[3][0],
at_crease_weight // ignored unless vertex_grid1[3][0] is tagged as a crease
);
if (fedges[0].IsNull())
return ON_SUBD_RETURN_ERROR(false);
// NOTE: fedges[0] is interior to an input face and cannot be sharp.
// m_edge_grid[q0fvi][1]
fedges[1] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[3][0],
ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[3][1],
ON_SubDSectorType::CopyEdgeSectorCoefficient(m_edge_grid[q0fvi][1], qf0_vertices[1], ON_UNSET_VALUE)
);
if (fedges[1].IsNull())
return ON_SUBD_RETURN_ERROR(false);
ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(m_edge_grid[q0fvi][1], qf0_vertices[1], fedges[1], vertex_grid1[3][1]);
face_grid1[2][0] = fsh.AllocateQuad(zero_face_id, parent_face_id,
fedges[0],
fedges[1],
fedges[2].Reversed(),
fedges[3].Reversed()
);
if (nullptr == face_grid1[2][0])
return ON_SUBD_RETURN_ERROR(false);
}
// face_grid1[2][1]
fedges[0] = edge_grid1[1][0];
fedges[1] = ON_SubDEdgePtr::Null;
fedges[2] = edge_grid1[1][1];
fedges[3] = face_grid1[1][1]->m_edge4[1];
if ( fedges[0].IsNull() || fedges[2].IsNull() || fedges[3].IsNull() )
return ON_SUBD_RETURN_ERROR(false);
fedges[1] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[3][1],
ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[1], qf0_vertices[1], ON_UNSET_VALUE),
vertex_grid1[3][2],
ON_SubDSectorType::IgnoredSectorCoefficient
);
if (fedges[1].IsNull())
return ON_SUBD_RETURN_ERROR(false);
ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[1], qf0_vertices[1], fedges[1], vertex_grid1[3][1]);
face_grid1[2][1] = fsh.AllocateQuad(
zero_face_id,
parent_face_id,
fedges[0],
fedges[1],
fedges[2].Reversed(),
fedges[3].Reversed()
);
if (nullptr == face_grid1[2][1])
return ON_SUBD_RETURN_ERROR(false);
// face_grid1[2][2]
fedges[0] = edge_grid1[1][1];
fedges[1] = ON_SubDEdgePtr::Null;
fedges[2] = ON_SubDEdgePtr::Null;
fedges[3] = edge_grid1[2][0];
if ( fedges[0].IsNull() || fedges[3].IsNull() )
return ON_SUBD_RETURN_ERROR(false);
fedges[1] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[3][2],
ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[3][3],
ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[1], qf0_vertices[2], ON_UNSET_VALUE)
);
if (fedges[1].IsNull())
return ON_SUBD_RETURN_ERROR(false);
ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[1], qf0_vertices[2], fedges[1], vertex_grid1[3][3]);
fedges[2] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[3][3],
ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[2], qf0_vertices[2], ON_UNSET_VALUE),
vertex_grid1[2][3],
ON_SubDSectorType::IgnoredSectorCoefficient
);
if (fedges[2].IsNull())
return ON_SUBD_RETURN_ERROR(false);
ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[2], qf0_vertices[2], fedges[2], vertex_grid1[3][3]);
face_grid1[2][2] = fsh.AllocateQuad(
zero_face_id,
parent_face_id,
fedges[0],
fedges[1],
fedges[2],
fedges[3].Reversed()
);
if (nullptr == face_grid1[2][2])
return ON_SUBD_RETURN_ERROR(false);
// face_grid1[1][2]
fedges[0] = face_grid1[1][1]->m_edge4[2];
fedges[1] = edge_grid1[2][0];
fedges[2] = ON_SubDEdgePtr::Null;
fedges[3] = edge_grid1[2][1];
if (fedges[0].IsNull() || fedges[1].IsNull() ||fedges[3].IsNull() )
return ON_SUBD_RETURN_ERROR(false);
fedges[2] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[2][3],
ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[1][3],
ON_SubDSectorType::CopyEdgeSectorCoefficient(qf0_edges[2], qf0_vertices[3], ON_UNSET_VALUE)
);
if (fedges[2].IsNull())
return ON_SUBD_RETURN_ERROR(false);
ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(qf0_edges[2], qf0_vertices[3], fedges[2], vertex_grid1[1][3]);
face_grid1[1][2] = fsh.AllocateQuad(
zero_face_id, parent_face_id,
fedges[0].Reversed(),
fedges[1],
fedges[2],
fedges[3].Reversed()
);
if (nullptr == face_grid1[1][2])
return ON_SUBD_RETURN_ERROR(false);
if (false == bBoundaryCrease1[3])
{
fedges[0] = edge_grid1[2][1];
fedges[1] = ON_SubDEdgePtr::Null;
fedges[2] = ON_SubDEdgePtr::Null;
fedges[3] = edge_grid1[3][0];
if ( fedges[0].IsNull() || fedges[3].IsNull() )
return ON_SUBD_RETURN_ERROR(false);
fedges[1] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[1][3],
ON_SubDSectorType::CopyEdgeSectorCoefficient(m_edge_grid[(q0fvi+3)%4][0], qf0_vertices[3], ON_UNSET_VALUE),
vertex_grid1[0][3],
ON_SubDSectorType::IgnoredSectorCoefficient
);
if ( fedges[1].IsNull())
return ON_SUBD_RETURN_ERROR(false);
ON_SubDQuadFaceTopology_SetSubdividedEdgeSharpness(m_edge_grid[(q0fvi + 3) % 4][0], qf0_vertices[3], fedges[1], vertex_grid1[1][3]);
fedges[2] = fsh.AllocateEdge(
bUseFindOrAllocate,
vertex_grid1[0][3],
at_crease_weight, // ingored unless vertex_grid1[0][3] is tagged as a crease
vertex_grid1[0][2],
ON_SubDSectorType::IgnoredSectorCoefficient
);
if ( fedges[2].IsNull())
return ON_SUBD_RETURN_ERROR(false);
// NOTE: fedges[2] is interior to an input face and cannot be sharp.
face_grid1[0][2] = fsh.AllocateQuad(
zero_face_id, parent_face_id,
fedges[0],
fedges[1],
fedges[2],
fedges[3].Reversed()
);
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].Edge();
q1ft->m_edge_grid[j][1] = edge_grid1[i][1].Edge();
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_SubDSectorSurfacePoint limit_point;
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 (
qv0->SurfacePointIsSet()
&& qv0->GetSurfacePoint(qv0_sector_face,limit_point)
&& limit_point.IsSet(true)
)
{
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->SetSavedSurfacePoint(true,limit_point);
}
else if (qv1->GetSurfacePoint( qv1_sector_face, 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->SetSavedSurfacePoint(true,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::GetQuadSurface(
double* limit_surface_cv,
size_t limit_surface_cv_stride0,
size_t limit_surface_cv_stride1
) 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::GetQuadSurface(
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 == GetQuadSurface(nurbs_surface.m_cv, nurbs_surface.m_cv_stride[0], nurbs_surface.m_cv_stride[1]))
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::GetQuadSurface(
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 == GetQuadSurface(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(nullptr);
}
bool ON_SubDFaceNeighborhood::ReserveCapacity(
const ON_SubDFace* face
)
{
m_face0 = nullptr;
m_center_vertex1 = nullptr;
m_face1_count = 0;
m_face1 = nullptr;
// reserve enough room to store the subdivided face and all neighboring components.
return m_fsh.ReserveSubDWorkspace(face);
}
bool ON_SubDFaceNeighborhood::Subdivide(
const ON_SubDFace* face
)
{
if (false == ReserveCapacity(face))
return ON_SUBD_RETURN_ERROR(false);
return ON_SubDFaceNeighborhood::QuadSubdivideHelper(face);
}
bool ON_SubDFaceNeighborhood::QuadSubdivideHelper(
const ON_SubDFace* face
)
{
// input face is valid and space is reserved.
//ON_DEBUG_SubDBreakPoint(face);
// 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::CreaseSectorCoefficient(2);
const unsigned int N = face->m_edge_count;
const unsigned int level_zero_face_id = face->m_level_zero_face_id;
const unsigned int parent_face_idX = face->m_id;
ON_SubDSectorSurfacePoint limit_point;
// create central vertex
ON_SubDVertex* center_vertex1 = m_fsh.FindOrAllocateVertex(face);
if ( nullptr == center_vertex1)
return ON_SUBD_RETURN_ERROR(false);
if ( 1 != center_vertex1->m_id)
return ON_SUBD_RETURN_ERROR(false);
const ON_SubDVertex* vertex0 = face->Vertex(0);
if ( nullptr == vertex0 )
return ON_SUBD_RETURN_ERROR(false);
ON_SubDEdgePtr edge0_ptr = ON_SubDEdgePtr::Null;
const ON_SubDEdge* edge0 = nullptr;
ON_SubDVertex* vertex1 = nullptr;
ON_SubDEdgePtr edge1 = ON_SubDEdgePtr::Null;
// Calculate 2*N subdivision vertices on the boundary of input "face"
// and N subdivision edge that radiate from center_vertex1 to the input face edge's subdivision points.
// This loop also verifies that all input vertex and edge pointers are not null and have the
// expected properties so the rest of this function can dispense with checking.
const ON_SubDEdgePtr* face_m_edges = face->m_edge4;
for (unsigned int i = 0; i < N; i++, face_m_edges++)
{
if (4 == i)
{
if (nullptr == face->m_edgex)
return ON_SUBD_RETURN_ERROR(false);
face_m_edges = face->m_edgex;
}
edge0_ptr = *face_m_edges;
edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr.m_ptr);
if (nullptr == edge0 || nullptr == edge0->m_vertex[0] || nullptr == edge0->m_vertex[1])
return ON_SUBD_RETURN_ERROR(false);
vertex0 = edge0_ptr.RelativeVertex(0);
if (vertex0->m_edge_count < 2)
return ON_SUBD_RETURN_ERROR(false);
// One of the main reasons for creating a ON_SubDFaceNeighborhood is to calculate the
// limit mesh and limit cubic surfaces when the original face is not a quad.
// For these calculations, we need to calculate and save the limit point for extraordinary
// vertices 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->GetSurfacePoint(face,limit_point) )
return ON_SUBD_RETURN_ERROR(false);
vertex1 = m_fsh.AllocateVertex(vertex0,vertex0->m_edge_count);
if ( nullptr == vertex1)
return ON_SUBD_RETURN_ERROR(false);
if ( 2*i+2 != vertex1->m_id )
return ON_SUBD_RETURN_ERROR(false);
if (nullptr != limit_point.m_sector_face || false == vertex1->SurfacePointIsSet() )
{
// While there may be multiple sectors around vertex0, the only limit point
// that matters is this local subdivision is the one for vertex0 in the sector containing the input face.
limit_point.m_next_sector_limit_point = (ON_SubDSectorSurfacePoint*)1; // causes unnecessary test to be skipped
limit_point.m_sector_face = nullptr;
vertex1->SetSavedSurfacePoint( false, limit_point);
}
// This vertex is either a crease (3 edges, 2 faces) or a smooth ordinary vertex (4 edges, 4 faces).
vertex1 = m_fsh.AllocateVertex(edge0);
if ( nullptr == vertex1)
return ON_SUBD_RETURN_ERROR(false);
if ( 2*i+3 != vertex1->m_id )
return ON_SUBD_RETURN_ERROR(false);
edge1 = m_fsh.AllocateEdge(
center_vertex1,
ON_SubDSectorType::IgnoredSectorCoefficient,
vertex1,
at_crease2_weight // ignored unless vertex1 is tagged as a crease
);
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
// edge1 is interior to face and has no sharpness
if ( 0 != edge1.EdgeDirection() )
return ON_SUBD_RETURN_ERROR(false);
if ( i+1 != edge1.EdgeId() )
return ON_SUBD_RETURN_ERROR(false);
}
// ring_vertex1 and last_ring_vertex1 are used to repeatedly iterate through subdivision vertices on the boundary
// of the original face.
// i = "corner" index (= <= i < N.
// ring_vertex1[3] = last vertex in the boundary and is used for initialization when i = 0.
// ring_vertex1[0] = subdivision vertex on input face->Edge(i-1)
// ring_vertex1[1] = subdivision vertex on input face->Vertex(i)
// ring_vertex1[2] = subdivision vertex on input face->Edge(i)
ON_SubDVertex* ring_vertex1[4] = { nullptr, nullptr, nullptr, vertex1 };
// Calculate the 2*N subdivision edges on the boundary of input "face"
// and N subdivision faces.
ON_SubDFace* face1 = nullptr;
ON_SubDEdgePtr face1_eptrs[4] = {ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null,edge1.Reversed()};
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;
ON_SubDEdgePtr prev_edge0_ptr = edge0_ptr;
edge0_ptr = *face_m_edges;
vertex0 = edge0_ptr.RelativeVertex(0);
if (0 == i)
{
ring_vertex1[0] = ring_vertex1[3];
ring_vertex1[1] = const_cast<ON_SubDVertex*>(center_vertex1->m_next_vertex);
ring_vertex1[2] = const_cast<ON_SubDVertex*>(ring_vertex1[1]->m_next_vertex);
}
else
{
ring_vertex1[0] = ring_vertex1[2];
ring_vertex1[1] = const_cast<ON_SubDVertex*>(ring_vertex1[0]->m_next_vertex);
ring_vertex1[2] = const_cast<ON_SubDVertex*>(ring_vertex1[1]->m_next_vertex);
}
face1_eptrs[0] = face1_eptrs[3].Reversed();
face1_eptrs[3] = center_vertex1->m_edges[i].Reversed();
edge1 = m_fsh.AllocateEdge(
ring_vertex1[0],
ON_SubDSectorType::IgnoredSectorCoefficient,
ring_vertex1[1],
ON_SubDSectorType::CopyEdgeSectorCoefficient(prev_edge0_ptr.Edge(), vertex0, ON_UNSET_VALUE)
);
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
edge1.SetRelativeSharpness(prev_edge0_ptr.RelativeSharpness(false).Subdivided(1));
face1_eptrs[1] = edge1;
edge1 = m_fsh.AllocateEdge(
ring_vertex1[1],
ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0_ptr.Edge(), vertex0, ON_UNSET_VALUE),
ring_vertex1[2],
ON_SubDSectorType::IgnoredSectorCoefficient
);
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
edge1.SetRelativeSharpness(edge0_ptr.RelativeSharpness(false).Subdivided(0));
face1_eptrs[2] = edge1;
face1 = m_fsh.AllocateQuad(level_zero_face_id, parent_face_idX, face1_eptrs);
if ( nullptr == face1)
return ON_SUBD_RETURN_ERROR(false);
}
/////////////////////////////////////////////////////////////////////////////////
// First, radially sort the subdivision vertex edge lists.
// This sorting is required for calculations later in this function.
//
// Then, add a subdivision edge from the input face's edge's subdivision point ("ring_vertex1")
// to the neighboring "level 0" face subdivision point. When this subdivision edge
// is added, it is critical below that it be in ring_vertex1->m_edges[3]
//
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_ptr = *face_m_edges;
edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr.m_ptr);
vertex0 = edge0_ptr.RelativeVertex(0);
// ring_vertex1[0] = subdivision vertex on input face->Edge(i-1)
// ring_vertex1[1] = subdivision vertex on input face->Vertex(i) = vertex0
// ring_vertex1[2] = subdivision vertex on input face->Edge(i) = edge0
if (0 == i)
{
ring_vertex1[0] = ring_vertex1[3];
ring_vertex1[1] = const_cast<ON_SubDVertex*>(center_vertex1->m_next_vertex);
ring_vertex1[2] = const_cast<ON_SubDVertex*>(ring_vertex1[1]->m_next_vertex);
}
else
{
ring_vertex1[0] = ring_vertex1[2];
ring_vertex1[1] = const_cast<ON_SubDVertex*>(ring_vertex1[0]->m_next_vertex);
ring_vertex1[2] = const_cast<ON_SubDVertex*>(ring_vertex1[1]->m_next_vertex);
}
// validity checks.
// ring_vertex1[0] counts vary
if ( 2 != ring_vertex1[1]->m_edge_count || 1 != ring_vertex1[1]->m_face_count)
return ON_SUBD_RETURN_ERROR(false);
if ( 3 != ring_vertex1[2]->m_edge_count || 2 != ring_vertex1[2]->m_face_count )
return ON_SUBD_RETURN_ERROR(false);
// swap edges at the corner vertex
face1_eptrs[0] = ring_vertex1[1]->m_edges[0];
ring_vertex1[1]->m_edges[0] = ring_vertex1[1]->m_edges[1];
ring_vertex1[1]->m_edges[1] = face1_eptrs[0];
if ((N-1) == i)
{
face1_eptrs[0] = ring_vertex1[2]->m_edges[1];
face1_eptrs[1] = ring_vertex1[2]->m_edges[0];
face1_eptrs[2] = ring_vertex1[2]->m_edges[2];
}
else
{
face1_eptrs[0] = ring_vertex1[2]->m_edges[2];
face1_eptrs[1] = ring_vertex1[2]->m_edges[0];
face1_eptrs[2] = ring_vertex1[2]->m_edges[1];
}
ring_vertex1[2]->m_edges[0] = face1_eptrs[0];
ring_vertex1[2]->m_edges[1] = face1_eptrs[1];
ring_vertex1[2]->m_edges[2] = face1_eptrs[2];
const ON_SubDFace* neighbor_face0 = edge0->NeighborFacePtr(face, false).Face();
if (nullptr == neighbor_face0)
{
if (false == edge0->IsHardCrease())
{
// Either input face or edge0 is damaged, but this error can be tolerated.
ON_SubDIncrementErrorCount();
}
continue;
}
// when the vertex0 has two faces and two edges, the 2nd edge will get the vertex1 created
// the previous time through this loop because the hash table lookup will find it.
vertex1 = m_fsh.FindOrAllocateVertex(neighbor_face0);
if (nullptr == vertex1)
return ON_SUBD_RETURN_ERROR(false);
edge1 = m_fsh.AllocateEdge(
ring_vertex1[2],
at_crease2_weight, // ignored unless ring_vertex1[0] is tagged as a crease
vertex1,
ON_SubDSectorType::IgnoredSectorCoefficient
);
if ( edge1.IsNull())
return ON_SUBD_RETURN_ERROR(false);
// edge1 is interior to neighbor_face0 and has no sharpness
}
/////////////////////////////////////////////////////////////////////////////////
//
// At each corner of the original face, add the outer subdivision quads as needed.
//
const ON_SubDEdge* edge0_duo[2] = {nullptr, face->Edge(N-1) };
const ON_SubDFace* neighbor0_duo[2] = {nullptr, edge0_duo[1]->NeighborFace(face,false) };
bool edge0_duo_bIsHardCrease[2] = { false, edge0_duo[1]->IsHardCrease() };
bool edge0_duo_bIsDartCrease[2] = { false, edge0_duo_bIsHardCrease[1] ? false : edge0_duo[1]->IsDartCrease() };
ON_SubDEdge* edge1_quartet[4] = {nullptr,nullptr,nullptr, ((4==ring_vertex1[2]->m_edge_count)?(ON_SUBD_EDGE_POINTER(ring_vertex1[2]->m_edges[3].m_ptr)):nullptr) };
ON_SubDSectorIterator sit;
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)
edge0_duo[0] = edge0_duo[1];
edge0_duo[1] = ON_SUBD_EDGE_POINTER(face_m_edges->m_ptr);
// vertex0 = face->Vertex(i)
vertex0 = edge0_duo[1]->m_vertex[ON_SUBD_EDGE_DIRECTION(face_m_edges->m_ptr)];
if ( nullptr == vertex0 || vertex0->m_edge_count < 2)
return ON_SUBD_RETURN_ERROR(false);
edge0_duo_bIsHardCrease[0] = edge0_duo_bIsHardCrease[1];
edge0_duo_bIsDartCrease[0] = edge0_duo_bIsDartCrease[1];
edge0_duo_bIsHardCrease[1] = edge0_duo[1]->IsHardCrease();
edge0_duo_bIsDartCrease[1] = edge0_duo_bIsHardCrease[1] ? false : edge0_duo[1]->IsDartCrease();
// neighbor0_duo[0] = original face neighbor across edge0_duo[0]
// neighbor0_duo[1] = original face neighbor across edge0_duo[1]
neighbor0_duo[0] = neighbor0_duo[1];
neighbor0_duo[1] = edge0_duo[1]->NeighborFace(face, false);
// ring_vertex1[0] = subdivision vertex on input face->Edge(i-1) = edge0_duo[0]
// ring_vertex1[1] = subdivision vertex on input face->Vertex(i) = vertex0
// ring_vertex1[2] = subdivision vertex on input face->Edge(i) = edge0_duo[1]
if (0 == i)
{
ring_vertex1[0] = ring_vertex1[3];
ring_vertex1[1] = const_cast<ON_SubDVertex*>(center_vertex1->m_next_vertex);
ring_vertex1[2] = const_cast<ON_SubDVertex*>(ring_vertex1[1]->m_next_vertex);
}
else
{
ring_vertex1[0] = ring_vertex1[2];
ring_vertex1[1] = const_cast<ON_SubDVertex*>(ring_vertex1[0]->m_next_vertex);
ring_vertex1[2] = const_cast<ON_SubDVertex*>(ring_vertex1[1]->m_next_vertex);
}
// edge1_quartet[0] = null or subdivision edge from ring_vertex1[0] to neighbor0_duo[0] subdivision point
// edge1_quartet[1] = subdivision edge from ring_vertex1[0] to ring_vertex1[1]
// edge1_quartet[2] = subdivision edge from ring_vertex1[1] to ring_vertex1[2]
// edge1_quartet[3] = null or subdivision edge from ring_vertex1[2] to neighbor0_duo[1] subdivision point
edge1_quartet[0] = edge1_quartet[3];
edge1_quartet[1] = ON_SUBD_EDGE_POINTER(ring_vertex1[1]->m_edges[1].m_ptr);
edge1_quartet[2] = ON_SUBD_EDGE_POINTER(ring_vertex1[1]->m_edges[0].m_ptr);
edge1_quartet[3] = (4==ring_vertex1[2]->m_edge_count) ? (ON_SUBD_EDGE_POINTER(ring_vertex1[2]->m_edges[3].m_ptr)) : nullptr;
if (edge0_duo_bIsHardCrease[0] && edge0_duo_bIsHardCrease[1])
{
// no outer quads at this corner
continue;
}
if ( false == edge0_duo_bIsHardCrease[0] && false == edge0_duo_bIsDartCrease[0] && false == edge0_duo[0]->IsSmooth() )
{
// This is an error condition that we can tolerate and still get a partial result.
ON_SubDIncrementErrorCount();
continue;
}
if ( false == edge0_duo_bIsHardCrease[1] && false == edge0_duo_bIsDartCrease[1] && false == edge0_duo[1]->IsSmooth() )
{
// This is an error condition that we can tolerate and still get a partial result.
ON_SubDIncrementErrorCount();
continue;
}
if (nullptr == neighbor0_duo[0] && nullptr == neighbor0_duo[1])
{
// This is an error condition that we can tolerate and still get a partial result.
ON_SubDIncrementErrorCount();
continue;
}
if (nullptr == neighbor0_duo[0] || nullptr == edge1_quartet[0])
{
if (false == edge0_duo_bIsHardCrease[0])
{
// This is an error condition that we can tolerate and still get a partial result.
ON_SubDIncrementErrorCount();
continue;
}
}
if (nullptr == neighbor0_duo[1] || nullptr == edge1_quartet[3])
{
if (false == edge0_duo_bIsHardCrease[1])
{
// This is an error condition that we can tolerate and still get a partial result.
ON_SubDIncrementErrorCount();
continue;
}
}
if ( neighbor0_duo[0] == neighbor0_duo[1]
&& 2 == vertex0->m_edge_count
&& 2 == vertex0->m_face_count
)
{
// vertex0 is a valence 2 vertex
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_quartet[3],1);
face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[2],1);
face1 = m_fsh.AllocateQuad(level_zero_face_id, parent_face_idX, face1_eptrs);
if ( nullptr == face1)
return ON_SUBD_RETURN_ERROR(false);
continue;
}
if (
neighbor0_duo[0] == neighbor0_duo[1]
|| vertex0->m_face_count <= 1
|| vertex0->m_edge_count <= 2
)
{
// This is an error condition that we can tolerate and still get a partial result.
ON_SubDIncrementErrorCount();
continue;
}
/////////////////////////////////////////////////////////////////////////////////
//
// There is at least one outer subdivision quads around the inner corner subd quad at face->Vertex(i)
if ( vertex0 != sit.Initialize(face,0,i) )
return ON_SUBD_RETURN_ERROR(false);
if (
vertex0 != sit.CenterVertex()
|| face != sit.CurrentFace()
|| (edge0_duo[0] != sit.CurrentEdge(1))
|| (edge0_duo[1] != sit.CurrentEdge(0))
)
{
return ON_SUBD_RETURN_ERROR(false);
}
const unsigned int vertex_face_count = vertex0->m_face_count;
const ON_SubDFace* face0 = nullptr;
ON_SubDVertex* face1_corners[4] = {ring_vertex1[1],nullptr,nullptr,nullptr};
if (vertex0->IsCreaseOrCorner() && edge0_duo_bIsDartCrease[0] && edge0_duo_bIsDartCrease[1])
{
// special case
if ( vertex_face_count < 3 )
return ON_SUBD_RETURN_ERROR(false);
if ( nullptr == neighbor0_duo[0] || nullptr == neighbor0_duo[1] )
return ON_SUBD_RETURN_ERROR(false);
face0 = sit.PrevFace(ON_SubDSectorIterator::StopAt::HardCrease);
if ( face0 != neighbor0_duo[1] )
return ON_SUBD_RETURN_ERROR(false);
edge0 = sit.CurrentEdge(0);
if ( nullptr == edge0 )
return ON_SUBD_RETURN_ERROR(false);
const bool b3FaceCase = (neighbor0_duo[0] == edge0->NeighborFace(face0, false));
face1_corners[1] = m_fsh.FindOrAllocateVertex(edge0);
face1_corners[2] = const_cast<ON_SubDVertex*>(edge1_quartet[3]->m_vertex[1]);
face1_corners[3] = ring_vertex1[2];
edge1 = m_fsh.FindOrAllocateEdge(face1_corners[0], ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0, vertex0, ON_UNSET_VALUE), face1_corners[1], at_crease2_weight);
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[0] = edge1;
edge1 = m_fsh.FindOrAllocateEdge(face1_corners[1], at_crease2_weight, face1_corners[2], ON_SubDSectorType::IgnoredSectorCoefficient);
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[1] = edge1;
face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1_quartet[3],1);
face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[2],1);
face1 = m_fsh.AllocateQuad(level_zero_face_id, parent_face_idX, face1_eptrs);
if ( nullptr == face1 )
return ON_SUBD_RETURN_ERROR(false);
face0 = sit.NextFace(ON_SubDSectorIterator::StopAt::HardCrease);
if ( face0 != face )
return ON_SUBD_RETURN_ERROR(false);
face0 = sit.NextFace(ON_SubDSectorIterator::StopAt::HardCrease);
if ( face0 != neighbor0_duo[0] )
return ON_SUBD_RETURN_ERROR(false);
if (b3FaceCase)
vertex1 = face1_corners[1];
else
{
edge0 = sit.CurrentEdge(1);
if ( nullptr == edge0 )
return ON_SUBD_RETURN_ERROR(false);
vertex1 = m_fsh.FindOrAllocateVertex(edge0);
}
if ( nullptr == vertex1 )
return ON_SUBD_RETURN_ERROR(false);
face1_corners[3] = vertex1;
face1_corners[1] = ring_vertex1[0];
face1_corners[2] = const_cast<ON_SubDVertex*>(edge1_quartet[0]->m_vertex[1]);
if (b3FaceCase)
{
face1_eptrs[3] = face1_eptrs[0].Reversed();
}
else
{
edge1 = m_fsh.FindOrAllocateEdge(face1_corners[0], ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0, vertex0, ON_UNSET_VALUE), face1_corners[3], at_crease2_weight );
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[3] = edge1.Reversed();
}
face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1_quartet[1], 1);
face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1_quartet[0], 0);
edge1 = m_fsh.FindOrAllocateEdge(face1_corners[2], ON_SubDSectorType::IgnoredSectorCoefficient, face1_corners[3], at_crease2_weight);
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[2] = edge1;
face1 = m_fsh.AllocateQuad(level_zero_face_id, parent_face_idX, face1_eptrs);
if ( nullptr == face1 )
return ON_SUBD_RETURN_ERROR(false);
continue;
}
// general case
if (vertex0->IsCreaseOrCorner() && nullptr != neighbor0_duo[1])
{
// Back up (go clockwise) to an appropriate starting point.
// Here the stop at = ON_SubDSectorIterator::StopAt::HardCrease because we are hopping
// over an edge of the input face and the "other vertex" might be a dart.
face0 = sit.PrevFace(ON_SubDSectorIterator::StopAt::Boundary);
if (face0 != neighbor0_duo[1])
return ON_SUBD_RETURN_ERROR(false);
for (unsigned int sit_limit = 0; sit_limit <= vertex_face_count && face != face0; sit_limit++)
{
const ON_SubDFace* prev_face0 = face0;
face0 = sit.PrevFace(ON_SubDSectorIterator::StopAt::Boundary);
if (prev_face0 == face0)
return ON_SUBD_RETURN_ERROR(false);
if (nullptr == face0)
{
// Hit a boundary. Begin ccw iteration here
if (nullptr == prev_face0)
return ON_SUBD_RETURN_ERROR(false);
if (vertex0 != sit.Initialize(prev_face0, 0, vertex0))
return ON_SUBD_RETURN_ERROR(false);
if (prev_face0 != sit.CurrentFace())
return ON_SUBD_RETURN_ERROR(false);
break;
}
if (face0 == face || face0 == neighbor0_duo[0])
{
// beginning ccw iteration at input face will work fine.
if (vertex0 != sit.Initialize(face, 0, i))
return ON_SUBD_RETURN_ERROR(false);
face0 = nullptr;
break;
}
}
if (nullptr != face0)
return ON_SUBD_RETURN_ERROR(false);
}
const bool bStopAtInputFace = (face == sit.CurrentFace());
if (bStopAtInputFace)
{
if (nullptr == neighbor0_duo[0])
return ON_SUBD_RETURN_ERROR(false);
face0 = sit.NextFace(ON_SubDSectorIterator::StopAt::Boundary);
if (face0 != neighbor0_duo[0])
return ON_SUBD_RETURN_ERROR(false);
}
// This information is needed when vertex0 is a crease vertex, one of the neighboring
// input face vertices is a dart, and sit is starting at a hard crease outside of the
// input face.
const ON_SubDEdge* sit_first_edge0 = sit.CurrentEdge(0);
if ( nullptr == sit_first_edge0 )
return ON_SUBD_RETURN_ERROR(false);
ON_SubDEdgePtr sit_first_eptr1 = ON_SubDEdgePtr::Null;
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;
for (unsigned int sit_limit = 0; sit_limit < vertex_face_count; sit_limit++)
{
if (0 != sit_limit)
{
face0 = sit.NextFace(ON_SubDSectorIterator::StopAt::Boundary);
if (nullptr == face0)
{
bFinishedCorner = true;
break;
}
edge0 = sit.CurrentEdge(0);
if ( nullptr == edge0 )
return ON_SUBD_RETURN_ERROR(false);
}
else
{
face0 = sit.CurrentFace();
if (nullptr == face0)
return ON_SUBD_RETURN_ERROR(false);
}
if (face == face0)
{
if ( bStopAtInputFace )
{
bFinishedCorner = true;
break;
}
if (vertex0->IsCreaseOrCorner() && edge0_duo_bIsHardCrease[0])
{
if (edge0_duo[0] == sit.CurrentEdge(1))
{
bFinishedCorner = true;
break;
}
}
face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1_quartet[2], 0);
face1_eptrs[1] = ON_SubDEdgePtr::Null;
face1_eptrs[2] = ON_SubDEdgePtr::Null;
face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[1], 0);
continue;
}
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[0])
{
face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1_quartet[1], 1);
face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1_quartet[0], 0);
}
if (face0 == neighbor0_duo[1])
{
face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1_quartet[3], 1);
face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[2], 1);
}
else if ((vertex_face_count-1) == sit_limit && sit_first_edge0 == sit.CurrentEdge(1) )
{
face1_eptrs[3] = sit_first_eptr1.Reversed();
}
face1_corners[1] = nullptr;
face1_corners[2] = nullptr;
face1_corners[3] = nullptr;
for (int j = 1; j < 4;j++)
{
vertex1 = nullptr;
for(;;)
{
vertex1 = const_cast<ON_SubDVertex*>(face1_eptrs[j-1].RelativeVertex(1));
if (nullptr != vertex1)
break;
vertex1 = const_cast<ON_SubDVertex*>(face1_eptrs[j].RelativeVertex(0));
if (nullptr != vertex1)
break;
switch (j)
{
case 1:
{
edge0 = sit.CurrentEdge(0);
if (nullptr==edge0)
return ON_SUBD_RETURN_ERROR(false);
vertex1 = m_fsh.FindOrAllocateVertex(edge0);
}
break;
case 2:
vertex1 = m_fsh.FindOrAllocateVertex(face0);
break;
case 3:
{
edge0 = sit.CurrentEdge(1);
if (nullptr == edge0)
return ON_SUBD_RETURN_ERROR(false);
vertex1 = m_fsh.FindOrAllocateVertex(edge0);
}
break;
}
break;
}
if ( nullptr == vertex1 )
return ON_SUBD_RETURN_ERROR(false);
face1_corners[j] = vertex1;
}
for (int j = 0; j < 4; j++)
{
if (false == face1_eptrs[j].IsNull())
continue;
edge1 = ON_SubDEdgePtr::Null;
switch (j)
{
case 0:
edge0 = sit.CurrentEdge(0);
if (nullptr == edge0)
return ON_SUBD_RETURN_ERROR(false);
edge1 = m_fsh.FindOrAllocateEdge(
face1_corners[0],
ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0, vertex0, ON_UNSET_VALUE),
face1_corners[1],
at_crease2_weight
);
if ( 0 == sit_limit && edge0 == sit_first_edge0 )
sit_first_eptr1 = edge1;
if (edge0->IsSharp())
edge1.SetRelativeSharpness(edge0->SubdivideSharpness(vertex0, vertex0 == edge0->m_vertex[1]));
break;
case 1:
edge1 = m_fsh.FindOrAllocateEdge(
face1_corners[1],
at_crease2_weight,
face1_corners[2],
ON_SubDSectorType::IgnoredSectorCoefficient
);
break;
case 2:
edge1 = m_fsh.FindOrAllocateEdge(
face1_corners[2],
ON_SubDSectorType::IgnoredSectorCoefficient,
face1_corners[3],
at_crease2_weight
);
break;
case 3:
{
edge0 = sit.CurrentEdge(1);
if (nullptr == edge0)
return ON_SUBD_RETURN_ERROR(false);
edge1 = m_fsh.FindOrAllocateEdge(
face1_corners[3],
at_crease2_weight,
face1_corners[0],
ON_SubDSectorType::CopyEdgeSectorCoefficient(edge0, vertex0, ON_UNSET_VALUE)
);
if (edge0->IsSharp())
edge1.SetRelativeSharpness(edge0->SubdivideSharpness(vertex0, vertex0 == edge0->m_vertex[0]));
}
break;
}
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[j] = edge1;
}
face1 = m_fsh.AllocateQuad(level_zero_face_id, parent_face_idX, face1_eptrs);
if ( nullptr == face1 )
return ON_SUBD_RETURN_ERROR(false);
}
for(;;)
{
if (bFinishedCorner)
break;
edge0 = sit.CurrentEdge(1);
if (nullptr != edge0 && edge0->IsHardCrease())
break;
return ON_SUBD_RETURN_ERROR(false);
}
}
m_center_vertex1 = center_vertex1;
m_face0 = face;
m_face1 = m_center_vertex1->m_faces;
m_face1_count = m_center_vertex1->m_face_count;
return true;
}
unsigned int ON_SubDVertexSurfacePointCoefficient::SurfacePointVertexId() const
{
return (nullptr != m_limit_point_vertex) ? m_limit_point_vertex->m_id : 0U;
}
unsigned int ON_SubDVertexSurfacePointCoefficient::RingVertexId() const
{
return (nullptr != m_ring_vertex) ? m_ring_vertex->m_id : 0U;
}
double ON_SubDVertexSurfacePointCoefficient::Coefficient() const
{
return m_c;
}
//#if 0
//static void DebugAt(const ON_SubDVertexSurfacePointCoefficient& c)
//{
// if (856 == c.LimitPointVertexId() && 857==c.RingVertexId())
// ON_TextLog::Null.Print("breakpoint here");
//}
//#endif
const ON_SubDVertexSurfacePointCoefficient ON_SubDVertexSurfacePointCoefficient::Create(
const ON_SubDVertex* limit_point_vertex,
const ON_SubDVertex* ring_vertex,
double x
)
{
ON_SubDVertexSurfacePointCoefficient c;
c.m_limit_point_vertex = limit_point_vertex;
c.m_ring_vertex = ring_vertex;
c.m_c = x;
//DebugAt(c)
return c;
}
int ON_SubDVertexSurfacePointCoefficient::CompareSurfacePointVertexId(
const ON_SubDVertexSurfacePointCoefficient* lhs,
const ON_SubDVertexSurfacePointCoefficient* rhs
)
{
const ON_SubDVertex* lhs_v = (nullptr == lhs) ? nullptr : lhs->m_limit_point_vertex;
const ON_SubDVertex* rhs_v = (nullptr == rhs) ? nullptr : rhs->m_limit_point_vertex;
return ON_SubDComponentBase::CompareId(lhs_v, rhs_v);
}
int ON_SubDVertexSurfacePointCoefficient::CompareRingVertexId(
const ON_SubDVertexSurfacePointCoefficient* lhs,
const ON_SubDVertexSurfacePointCoefficient* rhs
)
{
const ON_SubDVertex* lhs_v = (nullptr == lhs) ? nullptr : lhs->m_ring_vertex;
const ON_SubDVertex* rhs_v = (nullptr == rhs) ? nullptr : rhs->m_ring_vertex;
return ON_SubDComponentBase::CompareId(lhs_v, rhs_v);
}
int ON_SubDVertexSurfacePointCoefficient::CompareSurfacePointAndRingVertexId(
const ON_SubDVertexSurfacePointCoefficient* lhs,
const ON_SubDVertexSurfacePointCoefficient* rhs
)
{
const ON_SubDVertex* lhs_v = (nullptr == lhs) ? nullptr : lhs->m_limit_point_vertex;
const ON_SubDVertex* rhs_v = (nullptr == rhs) ? nullptr : rhs->m_limit_point_vertex;
int rc = ON_SubDComponentBase::CompareId(lhs_v, rhs_v);
if (0 == rc)
{
lhs_v = (nullptr == lhs) ? nullptr : lhs->m_ring_vertex;
rhs_v = (nullptr == rhs) ? nullptr : rhs->m_ring_vertex;
rc = ON_SubDComponentBase::CompareId(lhs_v, rhs_v);
}
return rc;
}
int ON_SubDVertexSurfacePointCoefficient::CompareRingAndSurfacePointVertexId(
const ON_SubDVertexSurfacePointCoefficient* lhs,
const ON_SubDVertexSurfacePointCoefficient* rhs
)
{
const ON_SubDVertex* lhs_v = (nullptr == lhs) ? nullptr : lhs->m_ring_vertex;
const ON_SubDVertex* rhs_v = (nullptr == rhs) ? nullptr : rhs->m_ring_vertex;
int rc = ON_SubDComponentBase::CompareId(lhs_v, rhs_v);
if (0 == rc)
{
lhs_v = (nullptr == lhs) ? nullptr : lhs->m_limit_point_vertex;
rhs_v = (nullptr == rhs) ? nullptr : rhs->m_limit_point_vertex;
rc = ON_SubDComponentBase::CompareId(lhs_v, rhs_v);
}
return rc;
}