// // 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 . // //////////////////////////////////////////////////////////////// #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(e1[0].RelativeVertex(0)); if (nullptr == v[0] || v[0] != e1[1].RelativeVertex(0)) return ON_SUBD_RETURN_ERROR(nullptr); v[1] = const_cast(e1[0].RelativeVertex(1)); if (nullptr == v[1] ) return ON_SUBD_RETURN_ERROR(nullptr); v[3] = const_cast(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(f1->Vertex(3)); vertex_grid1[0][2] = const_cast(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(f1->Vertex(0)); vertex_grid1[2][1] = const_cast(f1->Vertex(1)); vertex_grid1[2][2] = const_cast(f1->Vertex(2)); vertex_grid1[1][2] = const_cast(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(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(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(f1->Vertex(1)); vertex_grid1[2][0] = const_cast(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(center_vertex1->m_next_vertex); ring_vertex1[2] = const_cast(ring_vertex1[1]->m_next_vertex); } else { ring_vertex1[0] = ring_vertex1[2]; ring_vertex1[1] = const_cast(ring_vertex1[0]->m_next_vertex); ring_vertex1[2] = const_cast(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(center_vertex1->m_next_vertex); ring_vertex1[2] = const_cast(ring_vertex1[1]->m_next_vertex); } else { ring_vertex1[0] = ring_vertex1[2]; ring_vertex1[1] = const_cast(ring_vertex1[0]->m_next_vertex); ring_vertex1[2] = const_cast(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(center_vertex1->m_next_vertex); ring_vertex1[2] = const_cast(ring_vertex1[1]->m_next_vertex); } else { ring_vertex1[0] = ring_vertex1[2]; ring_vertex1[1] = const_cast(ring_vertex1[0]->m_next_vertex); ring_vertex1[2] = const_cast(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(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(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(face1_eptrs[j-1].RelativeVertex(1)); if (nullptr != vertex1) break; vertex1 = const_cast(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; }