#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" /* $NoKeywords: $ */ /* // // Copyright (c) 1993-2014 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 . // //////////////////////////////////////////////////////////////// */ bool ON_SubDFaceRegionBreakpoint( unsigned int level0_face_id, const class ON_SubDComponentRegionIndex& region_index ) { #if defined(ON_DEBUG) if ( 11 != level0_face_id ) { return false; } const unsigned short region_pattern[] = { 3, 3 }; const unsigned short region_pattern_count = (unsigned short)(sizeof(region_pattern) / sizeof(region_pattern[0])); if (region_index.m_subdivision_count < region_pattern_count) return false; for (unsigned short i = 0; i < region_pattern_count; i++) { if (region_index.m_index[i] != region_pattern[i]) return false; } return true;// <- breakpoint here (or above) #else return false; #endif } bool ON_SubDComponentRegionBreakpoint(const ON_SubDComponentRegion* component_region) { #if defined(ON_DEBUG) if (nullptr != component_region) { switch (component_region->m_level0_component.ComponentType()) { case ON_SubDComponentPtr::Type::Face: return ON_SubDFaceRegionBreakpoint(component_region->m_level0_component_id, component_region->m_region_index); break; case ON_SubDComponentPtr::Type::Edge: break; case ON_SubDComponentPtr::Type::Vertex: break; default: break; } } #endif return false; } bool ON_SubDLimitNurbsFragment::IsEmpty() const { return 0 == SetBispanCount(); } unsigned int ON_SubDLimitNurbsFragment::MaximumBispanCount() const { if (ON_SubDLimitNurbsFragment::Type::BicubicSingle == m_type) return 1; if (ON_SubDLimitNurbsFragment::Type::BicubicQuadrant == m_type) return 4; return 0; } unsigned int ON_SubDLimitNurbsFragment::SetBispanCount() const { unsigned int set_bispan_count = 0; const unsigned int imax = MaximumBispanCount(); for (unsigned int i = 0; i < imax; i++) { if ( ON_SubDLimitNurbsFragment::BispanType::Exact == m_bispan_type[i] || ON_SubDLimitNurbsFragment::BispanType::Approximate == m_bispan_type[i] ) { set_bispan_count++; } } return set_bispan_count; } unsigned int ON_SubDLimitNurbsFragment::UnsetBispanCount() const { return MaximumBispanCount() - SetBispanCount(); } static bool Internal_CheckNurbsSurfaceCVs( const ON_NurbsSurface& s ) { for (int i = 0; i < s.m_cv_count[0]; i++) { for (int j = 0; j < s.m_cv_count[1]; j++) { double * cv = s.CV(i, j); for (unsigned k = 0; k < 3; k++) { if (!ON_IsValid(cv[k])) { return ON_SUBD_RETURN_ERROR(false); } } } } return true; } bool ON_SubDLimitNurbsFragment::IsApproximate() const { const unsigned int imax = MaximumBispanCount(); for (unsigned int i = 0; i < imax; i++) { if (ON_SubDLimitNurbsFragment::BispanType::Approximate == m_bispan_type[i]) return true; } return false; } ON_NurbsSurface* ON_SubDLimitNurbsFragment::GetSurface( ON_NurbsSurface* destination_surface ) const { const unsigned int bispan_count = SetBispanCount(); if (bispan_count != MaximumBispanCount()) return nullptr; const double knots[7] = { -2,-1,0,1,2,3,4 }; ON_NurbsSurface patch_srf; patch_srf.m_dim = 3; patch_srf.m_is_rat = 0; patch_srf.m_order[0] = 4; patch_srf.m_order[1] = 4; patch_srf.m_knot[0] = (double*)knots; patch_srf.m_knot[1] = (double*)knots; patch_srf.m_cv_stride[0] = 5 * 3; patch_srf.m_cv_stride[1] = 3; patch_srf.m_cv_count[0] = (1 == bispan_count) ? 4 : 5; patch_srf.m_cv_count[1] = patch_srf.m_cv_count[0]; patch_srf.m_cv = (double*)m_patch_cv[0][0]; ON_NurbsSurface* surface = nullptr; if (destination_surface) { surface = destination_surface; *surface = patch_srf; } else { surface = new ON_NurbsSurface(patch_srf); } Internal_CheckNurbsSurfaceCVs(*surface); return surface; } ON_NurbsSurface* ON_SubDLimitNurbsFragment::GetQuadrantSurface( unsigned int quadrant_index, ON_NurbsSurface* destination_surface ) const { if (quadrant_index >= 4) return nullptr; if ( ON_SubDLimitNurbsFragment::BispanType::Exact != m_bispan_type[quadrant_index] && ON_SubDLimitNurbsFragment::BispanType::Approximate != m_bispan_type[quadrant_index] ) return nullptr; //const ON_2dex cvdex[4] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 } }; const ON_2dex cvdex( (1 == quadrant_index || 2 == quadrant_index) ? 1 : 0, (2 == quadrant_index || 3 == quadrant_index) ? 1 : 0 ); const double knots[7] = {-2,-1,0,1,2,3,4}; ON_NurbsSurface patch_srf; patch_srf.m_dim = 3; patch_srf.m_is_rat = 0; patch_srf.m_order[0] = 4; patch_srf.m_order[1] = 4; patch_srf.m_knot[0] = (double*)(knots+cvdex.i); patch_srf.m_knot[1] = (double*)(knots+cvdex.j); patch_srf.m_cv_stride[0] = 5*3; patch_srf.m_cv_stride[1] = 3; patch_srf.m_cv_count[0] = 4; patch_srf.m_cv_count[1] = 4; patch_srf.m_cv = (double*)m_patch_cv[cvdex.i][cvdex.j]; ON_NurbsSurface* surface = nullptr; if (destination_surface) { surface = destination_surface; *surface = patch_srf; } else { surface = new ON_NurbsSurface(patch_srf); } Internal_CheckNurbsSurfaceCVs(*surface); return surface; } const ON_SubDComponentRegion Internal_CreateSubdivisionEdgeRegion(unsigned short subdivision_count, bool bReversedEdge ) { return ON_SubDComponentRegion::CreateSubdivisionRegion(ON_SubDComponentPtr::Type::Edge, bReversedEdge, subdivision_count, true); } class Internal_SubQuadTransientComponents { public: Internal_SubQuadTransientComponents(ON_SubDFaceIterator& fit) { // The edge_transient_vertex_id[] array stores ON_2udex // that are used to record the transient vertex id // for a level 0 edge subdivision point. // ON_2udex.i = level 0 edge id // ON_2udex.j = transient vertex id to use for the edge subdivision vertex // // These are required when an edge is shared by 2 N-gons with N != 4 because // each N-gon must use the same transient vertex id in order for the // final NURBS surface to be easily and quickly joined into an ON_Brep. m_edge_transient_vertex_id_map.Reserve(64); ON_2udex u; u.i = 0; u.j = 0; for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) { const unsigned int edge_count = face->EdgeCount(); if (edge_count <= 0 || 4 == edge_count) continue; for (unsigned int fei = 0; fei < edge_count; fei++) { const ON_SubDEdge* e = face->Edge(fei); if (nullptr == e) continue; u.i = e->m_id; m_edge_transient_vertex_id_map.Append(u); } } const unsigned int count0 = m_edge_transient_vertex_id_map.UnsignedCount(); if (count0 <= 0) return; m_edge_transient_vertex_id_map.QuickSort(ON_2udex::DictionaryCompare); unsigned int count1 = 0; unsigned int prev_edge_id = 0; ON_2udex* a = m_edge_transient_vertex_id_map.Array(); for (unsigned int i = 0; i < count0; i++) { u = a[i]; if (u.i <= prev_edge_id) continue; // paired edge prev_edge_id = u.i; u.j = ON_SubDComponentRegion::NewTransientId(); a[count1++] = u; } m_edge_transient_vertex_id_map.SetCount(count1); } ~Internal_SubQuadTransientComponents() = default; public: static const Internal_SubQuadTransientComponents Empty; public: void Initialize( const ON_SubDFace* face ) { m_face = nullptr; m_edge_count = 0; if (m_edge_transient_vertex_id_map.UnsignedCount() <= 0) return; if (nullptr == face) return; const unsigned short edge_count = face->m_edge_count; if (edge_count < 3 || 4 == edge_count) return; // Used to create edge regions for subdivision edges because their is no "real" // edge to reference. // We need a unique id and region so merging and brep joining work correctly. m_edge_count = edge_count; m_fei = edge_count-1; m_face = face; m_radial_edge_region[2] = ON_SubDComponentRegion::CreateSubdivisionRegion(ON_SubDComponentPtr::Type::Edge, false, 1, true); m_radial_edge_region[1] = m_radial_edge_region[2]; m_face_center_vertex_id = ON_SubDComponentRegion::NewTransientId(); m_radial_vertex_id[2] = Internal_EdgeTransientVertexId(); m_radial_vertex_id[1] = m_radial_vertex_id[2]; } void NextSubQuad() { if (0 == m_edge_count || nullptr == m_face || 0 == m_edge_transient_vertex_id_map.UnsignedCount() ) return; m_fei = (m_fei + 1) % m_edge_count; const bool bCreateSubdivisionEdgeRegion = (m_fei + 1 < m_edge_count); m_radial_edge_region[0] = m_radial_edge_region[1]; m_radial_edge_region[1] = bCreateSubdivisionEdgeRegion ? ON_SubDComponentRegion::CreateSubdivisionRegion(ON_SubDComponentPtr::Type::Edge, false, 1, true) : m_radial_edge_region[2]; m_radial_vertex_id[0] = m_radial_vertex_id[1]; m_radial_vertex_id[1] = bCreateSubdivisionEdgeRegion ? Internal_EdgeTransientVertexId() : m_radial_vertex_id[2]; } public: const ON_SubDFace* m_face = nullptr; ON_SubDComponentRegion m_radial_edge_region[3] = {}; unsigned int m_face_center_vertex_id = 0; unsigned int m_radial_vertex_id[3] = {}; unsigned short m_edge_count = 0; unsigned short m_fei = 0; ON_SimpleArray< ON_2udex > m_edge_transient_vertex_id_map; private: Internal_SubQuadTransientComponents() = delete; Internal_SubQuadTransientComponents(const Internal_SubQuadTransientComponents&) = delete; Internal_SubQuadTransientComponents& operator= (const Internal_SubQuadTransientComponents&) = delete; private: unsigned int Internal_EdgeTransientVertexId() const { const ON_SubDEdge* e = (m_edge_count>0) ? m_face->Edge((m_fei+1)%m_edge_count) : nullptr; if (nullptr == e) return 0; const ON_2udex key(e->m_id, 0); int i = m_edge_transient_vertex_id_map.BinarySearch(&key, ON_2udex::CompareFirstIndex); return (i >= 0) ?m_edge_transient_vertex_id_map[i].j : 0; } }; static void Internal_SetLevel0FaceAndEdgeRegion( const ON_SubDFace* face, unsigned int qi, const Internal_SubQuadTransientComponents& face_transient_components, ON_SubDFaceRegion& face_region ) { face_region = ON_SubDFaceRegion::Empty; const unsigned int N = face->EdgeCount(); face_region.m_face_region.SetLevel0Face(face); if ( 4 == N ) { for (unsigned int fei = 0; fei < 4; fei++) face_region.m_edge_region[fei].SetLevel0EdgePtr(face->EdgePtr(fei)); for (unsigned int fvi = 0; fvi < 4; fvi++) { const ON_SubDVertex* v = face->Vertex(fvi); if (nullptr != v) face_region.m_vertex_id[fvi] = v->m_id; } } else if (N >= 3 && qi < N) { face_region.Push(qi); // original N-gon (N != 4) was subdivided into N quads. const ON_SubDVertex* v = face->Vertex((qi+1)%N); if (nullptr != v) face_region.m_vertex_id[2] = v->m_id; face_region.m_vertex_id[0] = face_transient_components.m_face_center_vertex_id; face_region.m_vertex_id[1] = face_transient_components.m_radial_vertex_id[0]; face_region.m_vertex_id[3] = face_transient_components.m_radial_vertex_id[1]; face_region.m_edge_region[0] = face_transient_components.m_radial_edge_region[0]; face_region.m_edge_region[1].SetLevel0EdgePtr(face->EdgePtr(qi)); face_region.m_edge_region[1].PushAdjusted(1); face_region.m_edge_region[2].SetLevel0EdgePtr(face->EdgePtr((qi+1)%N)); face_region.m_edge_region[2].PushAdjusted(0); face_region.m_edge_region[3] = face_transient_components.m_radial_edge_region[1]; face_region.m_edge_region[3].m_level0_component = face_region.m_edge_region[3].m_level0_component.SetMark(); } else { ON_SUBD_ERROR("Unexpected parameters."); } } const ON_SubDComponentRegion ON_SubDComponentRegion::Create( const class ON_SubDFace* level0_face ) { ON_SubDComponentRegion r; r.m_level0_component = ON_SubDComponentPtr::Create(level0_face); r.m_level0_component_id = (nullptr != level0_face ? level0_face->m_id : 0); return r; } const ON_SubDComponentRegion ON_SubDComponentRegion::Create( unsigned int component_id, ON_SubDComponentPtr::Type component_type, bool bComponentMark ) { ON_SubDComponentRegion r; r.m_level0_component = ON_SubDComponentPtr::CreateNull(component_type, bComponentMark); r.m_level0_component_id = component_id; return r; } const ON_SubDComponentRegion ON_SubDComponentRegion::CreateSubdivisionRegion( ON_SubDComponentPtr::Type component_type, bool bComponentMark, unsigned short subdivision_count, bool bAssignTransientId ) { ON_SubDComponentRegion r; r.m_region_index = ON_SubDComponentRegionIndex::Unset; r.m_region_index.m_subdivision_count = subdivision_count; r.m_level0_component = ON_SubDComponentPtr::CreateNull(component_type, bComponentMark); if (bAssignTransientId) { r.m_level0_component_id = ON_SubDComponentRegion::NewTransientId(); } return r; } ////ON_SubDComponentRegion ON_SubDComponentRegion::Reverse() const ////{ //// ON_SubDComponentRegion r(*this); //// r.m_level0_component = r.m_level0_component.ToggleMark(); //// if (r.m_subdivision_count > 0) //// { //// const int c = (int)(sizeof(m_region_index) / sizeof(m_region_index[0])); //// int i = (int)(r.m_subdivision_count - 1); //// int j = 0; //// while (j < c && 0xFFFF == r.m_region_index[j]) //// j++; //// for ( /*empty init*/; j < i && j < c; ++j,--i) //// { //// if (i < c) //// { //// unsigned short x = r.m_region_index[i]; //// r.m_region_index[i] = r.m_region_index[j]; //// r.m_region_index[j] = x; //// } //// else //// { //// r.m_region_index[j] = 0; //// } //// } //// } //// return r; ////} //// ////ON_SubDComponentRegion ON_SubDComponentRegion::ReverseIfMarked() const ////{ //// return //// 0 != m_level0_component.ComponentMark() //// ? Reverse() //// : *this; ////} int ON_SubDComponentRegion::CompareTypeIdMark( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) { if (lhs == rhs) return 0; if (nullptr == rhs) return 1; if (nullptr == lhs) return -1; int rc = ON_SubDComponentPtr::CompareType(&lhs->m_level0_component, &rhs->m_level0_component); if (0 != rc) return rc; if (lhs->m_level0_component_id < rhs->m_level0_component_id) return -1; if (lhs->m_level0_component_id > rhs->m_level0_component_id) return 1; rc = (0 != lhs->m_level0_component.ComponentMark() ? (int)1 : (int)0) - (0 != lhs->m_level0_component.ComponentMark() ? (int)1 : (int)0); if (0 != rc) return rc; return 0; } int ON_SubDComponentRegionIndex::Compare( const ON_SubDComponentRegionIndex* lhs, const ON_SubDComponentRegionIndex* rhs ) { if (lhs == rhs) return 0; if (nullptr == rhs) return 1; if (nullptr == lhs) return -1; if (lhs->m_subdivision_count < rhs->m_subdivision_count) return -1; if (lhs->m_subdivision_count > rhs->m_subdivision_count) return 1; return ON_SubDComponentRegionIndex::CompareMinimumSubregion(lhs, rhs); } int ON_SubDComponentRegionIndex::CompareMinimumSubregion( const ON_SubDComponentRegionIndex* lhs, const ON_SubDComponentRegionIndex* rhs ) { if (lhs == rhs) return 0; if (nullptr == lhs) return 1; if (nullptr == rhs) return -1; unsigned short subdivision_count0 = (lhs->m_subdivision_count < rhs->m_subdivision_count) ? lhs->m_subdivision_count : rhs->m_subdivision_count; if (subdivision_count0 > ON_SubDComponentRegionIndex::IndexCapacity) subdivision_count0 = ON_SubDComponentRegionIndex::IndexCapacity; for (unsigned short i = 0; i < subdivision_count0; i++) { if (lhs->m_index[i] < rhs->m_index[i]) return -1; if (lhs->m_index[i] > rhs->m_index[i]) return 1; } return 0; } int ON_SubDComponentRegion::CompareTypeIdMarkMinimumSubregion( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) { if (lhs == rhs) return 0; const int rc = ON_SubDComponentRegion::CompareTypeIdMark(lhs, rhs); if (0 != rc) return rc; return ON_SubDComponentRegionIndex::CompareMinimumSubregion( &lhs->m_region_index, &rhs->m_region_index); } int ON_SubDComponentRegion::CompareTypeIdMarkSubregion( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) { if (lhs == rhs) return 0; int rc = ON_SubDComponentRegion::CompareTypeIdMark(lhs, rhs); if (0 == rc) { rc = ON_SubDComponentRegionIndex::CompareMinimumSubregion(&lhs->m_region_index, &rhs->m_region_index); if (0 == rc) { if (lhs->m_region_index.m_subdivision_count < rhs->m_region_index.m_subdivision_count) rc = -1; else if (lhs->m_region_index.m_subdivision_count > rhs->m_region_index.m_subdivision_count) rc = 1; } } return rc; } int ON_SubDComponentRegion::Compare( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) { if (lhs == rhs) return 0; const int rc = ON_SubDComponentRegion::CompareTypeIdMarkSubregion(lhs, rhs); if (0 != rc) return rc; if (lhs->m_level0_component.m_ptr < rhs->m_level0_component.m_ptr) return -1; if (lhs->m_level0_component.m_ptr > rhs->m_level0_component.m_ptr) return 1; return 0; } void ON_SubDComponentRegion::SetLevel0Component( ON_SubDComponentPtr component_ptr ) { const class ON_SubDComponentBase* component_base = component_ptr.ComponentBase(); if (nullptr != component_base) { m_level0_component = component_ptr; m_level0_component_id = component_base->m_id; } else { m_level0_component = ON_SubDComponentPtr::Null; m_level0_component_id = 0; } m_region_index = ON_SubDComponentRegionIndex::Zero; } void ON_SubDComponentRegion::SetLevel0Face( const ON_SubDFace* face ) { SetLevel0Component(ON_SubDComponentPtr::Create(face)); } void ON_SubDComponentRegion::SetLevel0EdgePtr( const ON_SubDEdgePtr edge_ptr ) { SetLevel0Component(ON_SubDComponentPtr::Create(edge_ptr)); } void ON_SubDComponentRegion::SetLevel0Vertex( const ON_SubDVertex* vertex ) { SetLevel0Component(ON_SubDComponentPtr::Create(vertex)); } void ON_SubDComponentRegionIndex::Push( unsigned int region_index ) { if ( region_index > 0xFFFFU ) region_index = 0xFFFFU; if ( m_subdivision_count < ON_SubDComponentRegionIndex::IndexCapacity ) m_index[m_subdivision_count] = (unsigned short)region_index; ++m_subdivision_count; } void ON_SubDComponentRegionIndex::Pop() { if (m_subdivision_count > 0) { m_subdivision_count--; if ( m_subdivision_count < ON_SubDComponentRegionIndex::IndexCapacity) m_index[m_subdivision_count] = 0; } } void ON_SubDComponentRegion::PushAdjusted( unsigned int region_index ) { if ( ON_SubDComponentPtr::Type::Edge == m_level0_component.ComponentType() && 0 != m_level0_component.ComponentMark() && region_index <= 1 ) { region_index = 1 - region_index; } PushAbsolute(region_index); } void ON_SubDComponentRegion::PushAbsolute( unsigned int region_index ) { m_region_index.Push(region_index); ON_SubDComponentRegionBreakpoint(this); } void ON_SubDComponentRegion::Pop() { m_region_index.Pop(); } unsigned short ON_SubDComponentRegion::SubdivisionCount() const { return m_region_index.m_subdivision_count; } unsigned short ON_SubDComponentRegionIndex::Index( unsigned short i ) const { return (i < m_subdivision_count && i < ON_SubDComponentRegionIndex::IndexCapacity) ? m_index[i] : 0xFFFF; } void ON_SubDFaceRegion::Push(unsigned int quadrant_index) { m_face_region.PushAbsolute(quadrant_index); if (quadrant_index >= 0 && quadrant_index < 4) { m_edge_region[quadrant_index].PushAdjusted(0); // 1st half of this edge relative to face's orientation (adjusted to edge's orientation) m_edge_region[(quadrant_index + 1) % 4] = ON_SubDComponentRegion::CreateSubdivisionRegion(ON_SubDComponentPtr::Type::Edge, true, m_edge_region[quadrant_index].SubdivisionCount(), false); m_edge_region[(quadrant_index + 2) % 4] = ON_SubDComponentRegion::CreateSubdivisionRegion(ON_SubDComponentPtr::Type::Edge, false, m_edge_region[quadrant_index].SubdivisionCount(), false); m_edge_region[(quadrant_index + 3) % 4].PushAdjusted(1); // 2nd half of this edge relative to face's orientation (adjusted to edge's orientation) } const int surviving_vi = ((4 != m_level0_edge_count) && (1 == m_face_region.SubdivisionCount())) ? 2 : quadrant_index; m_vertex_id[(surviving_vi+1)%4] = 0; m_vertex_id[(surviving_vi+2)%4] = 0; m_vertex_id[(surviving_vi+3)%4] = 0; } bool ON_SubDComponentRegion::IsEmptyRegion() const { return ON_SubDComponentPtr::Type::Unset == m_level0_component.ComponentType() && m_level0_component.IsNull() && 0 == m_level0_component_id && 0 == SubdivisionCount(); } bool ON_SubDFaceRegion::IsValid( bool bSilentError ) const { if (m_face_region.IsEmptyRegion()) { for (int ei = 0; ei < 4; ei++) { if (false == m_edge_region[ei].IsEmptyRegion()) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region is empty and m_edge_region[] is not empty."); return false; } } for (int vi = 0; vi < 4; vi++) { if (0 != m_vertex_id[vi]) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region is empty and m_vertex_id[] is not zero."); return false; } } return true; } const ON_SubDComponentPtr::Type face_type = m_face_region.m_level0_component.ComponentType(); if (ON_SubDComponentPtr::Type::Face != face_type) { if (false == bSilentError) ON_SUBD_ERROR("Invalid m_face_region."); return false; } if (false == m_face_region.IsPersistentId()) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region.IsPersistentId() is false"); return false; } const ON_SubDFace* face = m_face_region.m_level0_component.Face(); if (nullptr == face ) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region.m_level0_component.Face() is nullptr."); return false; } if (face->m_id != m_face_region.m_level0_component_id) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected value for m_face_region.m_level0_component_id"); return false; } const unsigned int edge_count = (nullptr != face) ? face->EdgeCount() : 0; const bool bIsQuad = (4 == edge_count); if (false == bIsQuad) { if (0 == m_face_region.SubdivisionCount()) { if (false == bSilentError) ON_SUBD_ERROR("m_face_region is not a quad and 0 = m_subdivision_count."); return false; } if (((unsigned int)m_face_region.m_region_index.m_index[0]) >= edge_count) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected value in face_region.m_region_index[0]."); return false; } } bool bPeristentVertex[4] = { bIsQuad, bIsQuad, true, bIsQuad }; bool bPeristentEdge[4] = { bIsQuad, true, true, bIsQuad }; for (unsigned short i = bIsQuad?0:1; i < m_face_region.SubdivisionCount() && i < ON_SubDComponentRegionIndex::IndexCapacity; i++) { const unsigned short r = m_face_region.m_region_index.m_index[i]; if (r >= 4) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected value in face_region.m_region_index[]."); return false; } bPeristentVertex[(r+1)%4] = false; bPeristentVertex[(r+2)%4] = false; bPeristentVertex[(r+3)%4] = false; bPeristentEdge[(r+1)%4] = false; bPeristentEdge[(r+2)%4] = false; if (false == bPeristentVertex[r] && false == bPeristentEdge[r] && false == bPeristentEdge[(r + 3) % 4] ) break; } for (int ei = 0; ei < 4; ei++) { const bool bEmptyEdge = m_edge_region[ei].IsEmptyRegion(); if (bEmptyEdge) { if ( bPeristentEdge[ei]) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected empty edge in m_edge_region[]."); return false; } continue; } const ON_SubDComponentPtr::Type edge_type = m_edge_region[ei].m_level0_component.ComponentType(); if (ON_SubDComponentPtr::Type::Edge != edge_type) { if (false == bSilentError) ON_SUBD_ERROR("Invalid m_face_region."); return false; } if ( m_edge_region[ei].SubdivisionCount() != m_face_region.SubdivisionCount() ) { if (false == bSilentError) ON_SUBD_ERROR("m_edge_region[].m_subdivision_count != m_face_region.m_subdivision_count."); return false; } if (bPeristentEdge[ei]) { if (false == m_edge_region[ei].IsPersistentId()) { if (false == bSilentError) ON_SUBD_ERROR("m_edge_region[] missing a persistent edge id."); return false; } const ON_SubDEdge* edge = m_edge_region[ei].m_level0_component.Edge(); if (nullptr == edge) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected value for m_edge_region[].m_level0_component.Edge()"); return false; } if (edge->m_id != m_edge_region[ei].m_level0_component_id) { if (false == bSilentError) ON_SUBD_ERROR("Unexpected value for m_edge_region[].m_level0_component_id"); return false; } } else { if ( false == m_edge_region[ei].IsTransientId() ) { if (false == bSilentError) ON_SUBD_ERROR("m_edge_region[] missing a transient edge id."); return false; } } } for (int vi = 0; vi < 4; vi++) { if (bPeristentVertex[vi]) { if (false == ON_SubDComponentRegion::IsPersistentId(m_vertex_id[vi])) { if (false == bSilentError) ON_SUBD_ERROR("m_vertex_id[] missing a persistent vertex id."); return false; } } else if ( 0 != m_vertex_id[vi] ) { if (false == ON_SubDComponentRegion::IsTransientId(m_vertex_id[vi])) { if (false == bSilentError) ON_SUBD_ERROR("m_vertex_id[] missing a transient vertex id."); return false; } } } return true; } bool ON_SubDComponentRegion::IsTransientId() const { return ON_SubDComponentRegion::IsTransientId(m_level0_component_id); } bool ON_SubDComponentRegion::IsPersistentId() const { return ON_SubDComponentRegion::IsPersistentId(m_level0_component_id); } static unsigned int Internal_TransientIdHelper( bool bReset ) { static unsigned int src = 0; // shoule be a "atomic" unsigned - but not critical if (bReset) { src = 0; return 0; } unsigned int transient_id = ++src; if (0 != (ON_SubDComponentRegion::TransientIdBit & transient_id)) { src = 1; transient_id = 1; // because other threads may have modified src. } transient_id |= ON_SubDComponentRegion::TransientIdBit; return transient_id; } void ON_SubDComponentRegion::ResetTransientId() { Internal_TransientIdHelper(true); } const unsigned int ON_SubDComponentRegion::NewTransientId() { return Internal_TransientIdHelper(false); } bool ON_SubDComponentRegion::IsPersistentId(unsigned int id) { return (0 != id) && (0 == (ON_SubDComponentRegion::TransientIdBit & id)); } unsigned int ON_SubDComponentRegion::TransientId(unsigned int id) { return (0 != (ON_SubDComponentRegion::TransientIdBit & id)) ? ((~ON_SubDComponentRegion::TransientIdBit) & id) : 0; } bool ON_SubDComponentRegion::IsTransientId(unsigned int id) { return 0 != (ON_SubDComponentRegion::TransientIdBit & id) && 0 != ((~ON_SubDComponentRegion::TransientIdBit) & id); } static wchar_t* Internal_AppendUnsigned( //wchar_t prefix1, //wchar_t prefix2, unsigned int i, wchar_t* s, wchar_t* s1 ) { //if (0 != prefix1 && s < s1) // *s++ = prefix1; //if (0 != prefix2 && s < s1) // *s++ = prefix2; const bool bTransientId = (0 != (ON_SubDComponentRegion::TransientIdBit & i) ); if (bTransientId) { if (s < s1) *s++ = '<'; i &= ~ON_SubDComponentRegion::TransientIdBit; } wchar_t buffer[64]; wchar_t* sdigit = buffer; wchar_t* sdigit1 = sdigit + (sizeof(buffer)/sizeof(buffer[0])); for ( *sdigit++ = 0; sdigit < sdigit1; sdigit++ ) { *sdigit = (wchar_t)('0' + (i%10)); i /= 10; if (0 == i) { while ( s < s1 && 0 != (*s = *sdigit--) ) s++; break; } } if (bTransientId) { if (s < s1) *s++ = '>'; } if (s <= s1) *s = 0; return s; } wchar_t* ON_SubDComponentPtr::ToString( wchar_t* s, size_t s_capacity ) const { if (s_capacity <= 0 || nullptr == s) return nullptr; *s = 0; wchar_t* s1 = s + (s_capacity - 1); *s1 = 0; if (s < s1) { if (0 == m_ptr) { if (s + 7 < s1) { *s++ = 'N'; *s++ = 'u'; *s++ = 'l'; *s++ = 'l'; *s++ = 'P'; *s++ = 't'; *s++ = 'r'; } } else { wchar_t c; switch (ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: c = 'v'; break; case ON_SubDComponentPtr::Type::Edge: if ( s+2 < s1 ) *s++ = (ComponentMark()) ? '-' : '+'; c = 'e'; break; case ON_SubDComponentPtr::Type::Face: c = 'f'; break; case ON_SubDComponentPtr::Type::Unset: c = 0; break; default: c = 0; break; } if (0 == c) { *s++ = '?'; } else { *s++ = c; if (IsNull() && s + 6 < s1) { *s++ = '['; *s++ = 'n'; *s++ = 'u'; *s++ = 'l'; *s++ = 'l'; *s++ = ']'; } } } } if (nullptr != s && s <= s1) *s = 0; return s; }; const ON_wString ON_SubDComponentRegionIndex::ToString() const { wchar_t buffer[32]; if (nullptr != ToString(buffer, sizeof(buffer) / sizeof(buffer[0]))) return ON_wString(buffer); return ON_wString::EmptyString; } wchar_t* ON_SubDComponentRegionIndex::ToString( wchar_t* s, size_t s_capacity ) const { if (s_capacity <= 0 || nullptr == s) return nullptr; *s = 0; wchar_t* s1 = s + (s_capacity - 1); *s1 = 0; if (s < s1) { for (unsigned short i = 0; i < m_subdivision_count && nullptr != s && s < s1; i++) { if (s < s1) *s++ = '.'; if (i >= ON_SubDComponentRegionIndex::IndexCapacity) { // more subdivision levels that m_region_index[] can record. if (s < s1) *s++ = '_'; break; } if (0xFFFF == m_index[i]) { // This is component was added during a subdivision // and did not exist at level i. if (s < s1) *s++ = 'x'; } else { // portion of component subdivided at level i s = Internal_AppendUnsigned(m_index[i], s, s1); } } } if (nullptr != s && s <= s1) *s = 0; return s; } const ON_wString ON_SubDComponentPtr::ToString() const { wchar_t buffer[32]; if (nullptr != ToString(buffer, sizeof(buffer) / sizeof(buffer[0]))) return ON_wString(buffer); return ON_wString::EmptyString; } wchar_t* ON_SubDComponentRegion::ToString( wchar_t* s, size_t s_capacity ) const { if (s_capacity <= 0 || nullptr == s) return nullptr; *s = 0; wchar_t* s1 = s + (s_capacity - 1); *s1 = 0; if (s < s1) { s = m_level0_component.ToString(s, s_capacity); if (nullptr != s && s < s1) s = Internal_AppendUnsigned(m_level0_component_id, s, s1); } if (nullptr != s && s < s1) s = m_region_index.ToString(s, 1 + (s1 - s)); if (nullptr != s && s <= s1) *s = 0; return s; } const ON_wString ON_SubDComponentRegion::ToString() const { wchar_t buffer[128]; if (nullptr != ToString(buffer, sizeof(buffer) / sizeof(buffer[0]))) return ON_wString(buffer); return ON_wString::EmptyString; } ON__UINT32 ON_SubDComponentRegionIndex::ToCompressedRegionIndex() const { return ON_SubDComponentRegionIndex::ToCompressedRegionIndex( m_subdivision_count, m_index); } const ON_SubDComponentRegionIndex ON_SubDComponentRegionIndex::FromCompressedRegionIndex( ON__UINT32 compressed_region_index ) { ON_SubDComponentRegionIndex ri; ON_SubDComponentRegionIndex::FromCompressedRegionIndex(compressed_region_index, &ri.m_subdivision_count, ri.m_index); return ri; } ON__UINT32 ON_SubDComponentRegionIndex::ToCompressedRegionIndex( unsigned short subdivision_count, const unsigned short* region_index ) { ON__UINT32 rc = (subdivision_count >= 255) ? 255 : (ON__UINT32)subdivision_count; rc <<= 24; if (nullptr != region_index && subdivision_count > 0) { ON__UINT32 idx = (region_index[0] >= 255) ? 255 : (ON__UINT32)region_index[0]; idx <<= 16; ON__UINT32 shift = 14; for (unsigned short i = 1; i < subdivision_count && i < ON_SubDComponentRegionIndex::IndexCapacity && shift <= 14; i++) { ON__UINT32 bits = (ON__UINT32)region_index[i]; if (bits > 3) bits = 3; idx |= (bits << shift); shift -= 2; } rc |= idx; } return rc; //ON__UINT32 shift = 27; //ON__UINT32 rc = (ON__UINT32)subdivision_count; //rc <<= shift; // //const unsigned short count // = (subdivision_count <= ON_SubDComponentRegion::region_index_capacity) // ? subdivision_count // : ON_SubDComponentRegion::region_index_capacity; //for (unsigned short i = 0; i < count; i++) //{ // shift -= 3; // ON__UINT32 three_bits = (ON__UINT32)(region_index[i] % 0x07); // if (0 != three_bits) // rc |= (three_bits << shift); //} //return rc; } void ON_SubDComponentRegionIndex::FromCompressedRegionIndex( ON__UINT32 compressed_region_index, unsigned short* subdivision_count, unsigned short region_index[] ) { const ON__UINT32 count = (compressed_region_index >> 24); if (nullptr != subdivision_count) *subdivision_count = (unsigned short)count; if (nullptr != region_index) { region_index[0] = (unsigned short)((compressed_region_index & 0x00FF0000) >> 16); for (unsigned short i = 1; i < ON_SubDComponentRegionIndex::IndexCapacity; i++) { region_index[i] = (unsigned short)((compressed_region_index & 0x0000C000) >> 14); compressed_region_index <<= 2; } } //ON__UINT32 shift = 27; //ON__UINT32 count = (region32 >> shift); //if (nullptr != subdivision_count) // *subdivision_count = (unsigned short)count; //if (nullptr != region_index) //{ // for (unsigned short i = 0; i < ON_SubDComponentRegion::region_index_capacity; i++) // region_index[i] = 0; // unsigned int idx = region32 << (32-shift); // for (unsigned short i = 0; 0 != idx && i < ON_SubDComponentRegion::region_index_capacity; i++) // { // ON__UINT32 three_bits = (idx & 0xE0000000U); // three_bits >>= 29; // region_index[i] = (unsigned short)three_bits; // idx <<= 3; // } //} } static ON_ProgressStepCounter CreateFragmentProgressStepCounter( ON_SubDFaceIterator& fit, const ON_SubDDisplayParameters& limit_mesh_parameters ) { unsigned int progress_step_count = 0; if (nullptr != limit_mesh_parameters.m_progress_reporter) { for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) { if ( 4 == face->m_edge_count) progress_step_count++; else progress_step_count += face->m_edge_count; } } ON_ProgressStepCounter counter = ON_ProgressStepCounter::Create( limit_mesh_parameters.m_progress_reporter, progress_step_count, limit_mesh_parameters.m_progress_reporter_interval[0], limit_mesh_parameters.m_progress_reporter_interval[1], 100 ); return counter; } wchar_t* ON_SubDFaceRegion::ToString( wchar_t* s, size_t s_capacity ) const { if (s_capacity <= 0 || nullptr == s) return nullptr; wchar_t* s1 = s + s_capacity-1; *s1 = 0; s = m_face_region.ToString(s, s_capacity); if (nullptr != s && s+4 < s1) { for (unsigned int i = 0; i < 4 && nullptr != s && s + 4 < s1; i++) { *s++ = ON_wString::Space; *s++ = (0 == i) ? '(' : ','; if ( ON_SubDComponentPtr::Type::Edge == m_edge_region[i].m_level0_component.ComponentType() ) { s = m_edge_region[i].ToString(s, s1 - s); } else { *s++ = 'e'; *s++ = '?'; } } if (nullptr != s && s < s1) *s++ = ')'; } if (nullptr != s && s+4 < s1) { for (unsigned int i = 0; i < 4 && nullptr != s && s + 4 < s1; i++) { *s++ = ON_wString::Space; *s++ = (0 == i) ? '(' : ','; if (0 != m_vertex_id[i]) { *s++ = 'v'; s = Internal_AppendUnsigned(m_vertex_id[i], s, s1); } else { *s++ = '0'; } } if (nullptr != s && s < s1) *s++ = ')'; } if (nullptr != s && s <= s1) *s = 0; return s; } const ON_wString ON_SubDFaceRegion::ToString() const { wchar_t buffer[256]; if (nullptr != ToString(buffer, sizeof(buffer) / sizeof(buffer[0]))) return ON_wString(buffer); return ON_wString::EmptyString; } static unsigned int GetQuadLimitSurfaceMeshFragmentsHelper( ON_SubDFaceIterator& fit, const ON_SubDDisplayParameters& limit_mesh_parameters, ON__UINT_PTR fragment_callback_context, bool(*mesh_fragment_callback_function)(ON__UINT_PTR, const class ON_SubDLimitMeshFragment*) ) { ON_ProgressStepCounter counter = CreateFragmentProgressStepCounter(fit,limit_mesh_parameters); if (nullptr == fit.FirstFace() ) return ON_SUBD_RETURN_ERROR(0); if ( nullptr == mesh_fragment_callback_function ) return ON_SUBD_RETURN_ERROR(0); unsigned int display_density = limit_mesh_parameters.m_display_density; //const bool bUseMultipleThreads = limit_mesh_parameters.m_bUseMultipleThreads; ON_SubDManagedLimitMeshFragment fragment; ON_SubDManagedLimitMeshFragment sub_fragment; ON_SubDQuadFaceMesher qfm; ON_SubDQuadFaceMesher sub_qfm; ON_SubDFaceNeighborhood fnbd; qfm.m_output = ON_SubDQuadFaceMesher::Output::mesh; ON_SubDLimitMeshFragment* callback_fragment = nullptr; const ON_SubDFace** quad_faces = nullptr; unsigned int quad_face_count = 0; unsigned int fragment_count = 0; if (0 == display_density) { // make sure all faces are quads. If not, increase density to 1 for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) { if (4 == face->m_edge_count) continue; display_density = 1; break; } } Internal_SubQuadTransientComponents face_transient_components(fit); // TODO // // Support multiple threads by adding more fragment, sub_fragment qfm, sub_qfm and fnbd // resources and managing them. // const unsigned int subquad_display_density = (display_density > 1) ? (display_density - 1) : 0; const unsigned short unset_face_edge_index = 0xFFFFU; for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) { if (face->m_edge_count < 3) continue; face_transient_components.Initialize(face); if (4 == face->m_edge_count) { // face is a quad quad_faces = &face; quad_face_count = 1; if (callback_fragment != &fragment) { if (0 == fragment.m_P_capacity) { fragment.ReserveCapacity(ON_SubD::FacetType::Quad, display_density); fragment.m_P_count = (unsigned short)ON_SubDLimitMeshFragment::PointCountFromDisplayDensity(ON_SubD::FacetType::Quad,display_density); if ( fragment.m_P_count > fragment.m_P_capacity) return ON_SUBD_RETURN_ERROR(0); fragment.m_face_vertex_index[0] = 0; fragment.m_face_vertex_index[1] = 1; fragment.m_face_vertex_index[2] = 2; fragment.m_face_vertex_index[3] = 3; } callback_fragment = &fragment; qfm.SetDestinationToMeshFragment(display_density, fragment); } } else { // face is not a quad. It will be subdivided into quads and each // of those quads is meshed a a level of display_density-1. if (false == fnbd.Subdivide(ON_SubD::SubDType::QuadCatmullClark, face)) continue; quad_faces = fnbd.m_center_vertex1->m_faces; quad_face_count = fnbd.m_center_vertex1->m_face_count; if (quad_face_count < 2) continue; if (callback_fragment != &sub_fragment) { if (0 == sub_fragment.m_P_capacity) { sub_fragment.ReserveCapacity(ON_SubD::FacetType::Quad, subquad_display_density); sub_fragment.m_P_count = (unsigned short)ON_SubDLimitMeshFragment::PointCountFromDisplayDensity(ON_SubD::FacetType::Quad,subquad_display_density); if ( sub_fragment.m_P_count > sub_fragment.m_P_capacity) return ON_SUBD_RETURN_ERROR(0); sub_fragment.m_face_vertex_index[0] = unset_face_edge_index; sub_fragment.m_face_vertex_index[1] = unset_face_edge_index; sub_fragment.m_face_vertex_index[2] = unset_face_edge_index; sub_fragment.m_face_vertex_index[3] = unset_face_edge_index; } callback_fragment = &sub_fragment; qfm.SetDestinationToMeshFragment(subquad_display_density, sub_fragment); } } callback_fragment->m_face = face; callback_fragment->m_face_fragment_count = (unsigned short)quad_face_count; for (unsigned int qi = 0; qi < quad_face_count; qi++) { face_transient_components.NextSubQuad(); ON_SubDFaceRegion face_region; Internal_SetLevel0FaceAndEdgeRegion( face, qi, face_transient_components, // qi and subdivsion_edge_region[] are used only when "f" is a level 1 sud quad of "face" face_region ); const ON_SubDFace* f = quad_faces[qi]; if (unset_face_edge_index == callback_fragment->m_face_vertex_index[0]) callback_fragment->m_face_vertex_index[2] = (unsigned short)((qi+1)%quad_face_count); callback_fragment->m_face_fragment_index = (unsigned short)qi; if (false == qfm.GetLimitMesh(face_region, f)) continue; fragment_count++; callback_fragment->m_bbox = ON_PointListBoundingBox(3,0,callback_fragment->m_P_count,(int)callback_fragment->m_P_stride,callback_fragment->m_P); if (false == mesh_fragment_callback_function(fragment_callback_context, callback_fragment)) return true; if (0 == (fragment_count % 16)) { if (ON_Terminator::TerminationRequested(limit_mesh_parameters.m_terminator)) return 0; // not an error } counter.IncrementStep(); } } counter.Finished(); return fragment_count; } static unsigned int GetQuadLimitSurfacePatchFragmentsHelper( ON_SubDFaceIterator& fit, const ON_SubDDisplayParameters& limit_mesh_parameters, ON__UINT_PTR fragment_callback_context, bool(*begin_face_callback_function)(ON__UINT_PTR ,const ON_SubDFaceRegion&),//, const class ON_SubDFace*, const class ON_SubDFace*, unsigned int), bool(*patch_fragment_callback_function)(ON__UINT_PTR, const class ON_SubDLimitNurbsFragment*) ) { ON_ProgressStepCounter counter = CreateFragmentProgressStepCounter(fit,limit_mesh_parameters); if (nullptr == fit.FirstFace() ) return ON_SUBD_RETURN_ERROR(0); if ( nullptr == patch_fragment_callback_function ) return ON_SUBD_RETURN_ERROR(0); unsigned int display_density = limit_mesh_parameters.m_display_density; //const bool bUseMultipleThreads = limit_mesh_parameters.m_bUseMultipleThreads; ON_SubDQuadFacePatcher patcher; ON_SubDQuadFaceMesher qfm; ON_SubDQuadFaceMesher sub_qfm; ON_SubDFaceNeighborhood fnbd; Internal_SubQuadTransientComponents face_transient_components(fit); const ON_SubDFace** quad_faces = nullptr; unsigned int quad_face_count = 0; unsigned int fragment_count = 0; if (0 == display_density) { // make sure all faces are quads. If not, increase density to 1 for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) { if (4 == face->m_edge_count) { display_density = 1; break; } } } patcher.m_fragment_callback_context = fragment_callback_context; patcher.m_fragment_callback_function = patch_fragment_callback_function; const unsigned int subquad_display_density = (display_density > 1) ? (display_density - 1) : 0; for (const ON_SubDFace* face = fit.FirstFace(); nullptr != face; face = fit.NextFace()) { if (face->m_edge_count < 3) continue; face_transient_components.Initialize(face); if (4 == face->m_edge_count) { // face is a quad quad_faces = &face; quad_face_count = 1; patcher.m_display_density = display_density; } else { // face is not a quad. It will be subdivided into quads and each // of those quads is meshed a a level of display_density-1. if (false == fnbd.Subdivide(ON_SubD::SubDType::QuadCatmullClark, face)) continue; quad_faces = fnbd.m_center_vertex1->m_faces; quad_face_count = fnbd.m_center_vertex1->m_face_count; patcher.m_display_density = subquad_display_density; if (quad_face_count < 2) continue; } patcher.m_patch_fragment = ON_SubDLimitNurbsFragment::Unset; qfm.SetDestinationToPatchFragment(patcher); for (unsigned int qi = 0; qi < quad_face_count; qi++) { face_transient_components.NextSubQuad(); // Internal_SetLevel0FaceAndEdgeRegion() sets face_region and patcher.m_patch_fragment.m_edge_region[] // In the case when face is not a quad and "f" is the level 1 subd quad, // the regions (face_region and patcher.m_patch_fragment.m_edge_region[]) // are "Pushed()" so they indicate what portion of "face" and it's original edges are being used. ON_SubDFaceRegion face_region; Internal_SetLevel0FaceAndEdgeRegion( face, qi, face_transient_components, // qi and subdivsion_edge_region[] are used only when "f" is a level 1 sud quad of "face" face_region ); const ON_SubDFace* f = quad_faces[qi]; if (nullptr != begin_face_callback_function) { // SubD to NURBS case: // fragment_callback_context = pointer to Internal_SubDNurbsPatchGetter class // begin_face_callback_function = Internal_SubDNurbsPatchGetter::BeginFaceCallback() // Internal_SubDNurbsPatchGetter::BeginFaceCallback() flushes accumulated surface content from previous SUbD face. // Then it initializes fragment_callback_context for current "face" and, it it exits, sub-quad "f" begin_face_callback_function(fragment_callback_context, face_region);//, face, (f != face) ? f : nullptr, qi); } patcher.m_patch_fragment.m_face_region = face_region; if (false == qfm.GetLimitPatches(face_region, f)) continue; fragment_count++; if (0 == (fragment_count % 16)) { if (ON_Terminator::TerminationRequested(limit_mesh_parameters.m_terminator)) return 0; // not an error } counter.IncrementStep(); } } counter.Finished(); return fragment_count; } class GetLimitSurfaceMesh_context_FragmentMark { public: class GetLimitSurfaceMesh_context* m_context = nullptr; size_t m_point_count0 = 0; size_t m_quad_count0 = 0; size_t m_fragment_count0 = 0; size_t m_point_count1 = 0; size_t m_quad_count1 = 0; size_t m_fragment_count1 = 0; unsigned int m_mark_index = 0; unsigned int m_face_id = 0; unsigned int m_zero_face_id = 0; unsigned int m_parent_face_id = 0; unsigned int m_face_fragment_count = 0; unsigned int m_face_fragment_index = 0; ON__UINT_PTR m_fragment_group_id = 0; // points to memory in the m_context that is under construction. ON_SubDLimitMeshFragment m_fragment = ON_SubDLimitMeshFragment::Empty; static int CompareFaceIdAndFragmentIndex( const GetLimitSurfaceMesh_context_FragmentMark* a, const GetLimitSurfaceMesh_context_FragmentMark* b ); }; int GetLimitSurfaceMesh_context_FragmentMark::CompareFaceIdAndFragmentIndex( const GetLimitSurfaceMesh_context_FragmentMark* a, const GetLimitSurfaceMesh_context_FragmentMark* b ) { if ( a == b ) return 0; if ( nullptr == a ) return -1; if ( nullptr == b ) return 1; if ( a->m_face_id < b->m_face_id ) return -1; if ( a->m_face_id > b->m_face_id ) return 1; if ( a->m_face_fragment_index < b->m_face_fragment_index ) return -1; if ( a->m_face_fragment_index > b->m_face_fragment_index ) return 1; return 0; } class GetLimitSurfaceMesh_context { public: GetLimitSurfaceMesh_context() = default; #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) bool m_bUseMultipleThreads = false; // If true, callback uses the lock ON_SleepLock m_lock; #endif size_t m_point_capacity = 0; double* m_points = nullptr; size_t m_point_stride = 0; size_t m_normal_capacity = 0; double* m_normals = nullptr; size_t m_normal_stride = 0; size_t m_quad_capacity = 0; unsigned int* m_quads = nullptr; size_t m_quad_stride = 0; // >= 4 ON__UINT_PTR* m_quad_group_ids = nullptr; size_t m_quad_group_id_stride = 0; // counts are updated as meshing progresses size_t m_point_count = 0; size_t m_quad_count = 0; size_t m_fragment_count = 0; ON_SimpleArray< GetLimitSurfaceMesh_context_FragmentMark > m_marks; }; static bool CoincidentPointTest( unsigned int i, unsigned int j, const double* points, const size_t point_stride, const double* normals, const size_t normal_stride ) { if ( i == j) return true; const double* a = points + i*point_stride; const double* b = points + j*point_stride; double d = fabs(a[0]-b[0]) + fabs(a[1]-b[1]) + fabs(a[2]-b[2]); if (!(d <= 1e-8)) return ON_SUBD_RETURN_ERROR(false); a = normals + i*normal_stride; b = normals + j*normal_stride; d = fabs(a[0]-b[0]) + fabs(a[1]-b[1]) + fabs(a[2]-b[2]); if (!(d <= 0.01)) return ON_SUBD_RETURN_ERROR(false); return true; } static bool GetLimitSurfaceMesh_callback(ON__UINT_PTR void_context, const class ON_SubDLimitMeshFragment* fragment) { GetLimitSurfaceMesh_context* context = (GetLimitSurfaceMesh_context*)void_context; unsigned int mark_count = 0; ON_SimpleArray< GetLimitSurfaceMesh_context_FragmentMark > face_fragment_marks; // When using multiple threads, // get the lock, // quickly reserve the section of the desitinataion arrays that will be used by this fragment, // and then return the lock. // SInce the space is now reserved, the actual copying can be done after returning the lock. GetLimitSurfaceMesh_context_FragmentMark mark; mark.m_context = context; mark.m_fragment = *fragment; if (nullptr != fragment->m_face) { mark.m_face_id = fragment->m_face->m_id; mark.m_zero_face_id = fragment->m_face->m_zero_face_id; mark.m_parent_face_id = fragment->m_face->m_parent_face_id; } mark.m_face_fragment_count = fragment->m_face_fragment_count; mark.m_face_fragment_index = fragment->m_face_fragment_index; #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) bool bReleaseLock = false; if (context->m_bUseMultipleThreads) { if (false == context->m_lock.GetLock(0, ON_SleepLock::OneSecond)) { // return tru to keep going, but something is really hogging the lock return ON_SUBD_RETURN_ERROR(true); } } #endif mark.m_point_count0 = context->m_point_count; mark.m_quad_count0 = context->m_quad_count; mark.m_point_count1 = mark.m_point_count0 + fragment->m_P_count; mark.m_quad_count1 = mark.m_quad_count0 + fragment->m_grid.m_F_count; if (mark.m_point_count1 > context->m_point_capacity || mark.m_quad_count1 > context->m_quad_capacity) { #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) if (bReleaseLock) context->m_lock.ReturnLock(); #endif return ON_SUBD_RETURN_ERROR(false); } mark.m_mark_index = context->m_marks.UnsignedCount(); mark.m_fragment_group_id = (ON__UINT_PTR)(context->m_fragment_count+1); if (mark.m_face_fragment_count <= 1 || 0 == mark.m_face_id || ON_UNSET_UINT_INDEX == mark.m_face_id) { mark_count = 1; } else if ( context->m_marks.UnsignedCount() + 1 >= mark.m_face_fragment_count ) { GetLimitSurfaceMesh_context_FragmentMark* context_fragment_marks = context->m_marks.Array(); const unsigned int context_mark_count0 = context->m_marks.UnsignedCount(); // See if we have all the subfragments for this face for (unsigned int i = 0; i < context_mark_count0; i++) { if ( context_fragment_marks[i].m_face_id == mark.m_face_id ) mark_count++; } if (mark_count + 1 >= mark.m_face_fragment_count) { // move all the marks for this face from context->m_marks[] (possibly used by multiple threads) // to the local context_fragment_marks[] array for processing below. unsigned int context_mark_count1 = 0; face_fragment_marks.Reserve(mark.m_face_fragment_count); if (context_mark_count0 + 1 >= mark.m_face_fragment_count) { face_fragment_marks.Append((int)context_mark_count0, context_fragment_marks); } else { for (unsigned int i = 0; i < context_mark_count0; i++) { if (context_fragment_marks[i].m_face_id == mark.m_face_id) face_fragment_marks.Append(context_fragment_marks[i]); else { if (i < context_mark_count1) context_fragment_marks[context_mark_count1] = context_fragment_marks[i]; context_mark_count1++; } } } context->m_marks.SetCount(context_mark_count1); face_fragment_marks.Append(mark); mark_count++; } else { // This face will have more subfragments delivered in the future. mark_count = 0; context->m_marks.AppendNew() = mark; } } else { // This face will have more subfragments delivered in the future. context->m_marks.AppendNew() = mark; } const size_t P_stride = context->m_point_stride; const size_t N_stride = context->m_normal_stride; const size_t F_stride = context->m_quad_stride; const size_t GID_stride = context->m_quad_group_id_stride; double* P = (nullptr != context->m_points) ? (context->m_points + mark.m_point_count0*P_stride) : nullptr; double* N = (nullptr != context->m_normals) ? (context->m_normals + mark.m_point_count0*N_stride) : nullptr; unsigned int* F = (nullptr != context->m_quads) ? (context->m_quads + mark.m_quad_count0*F_stride) : nullptr; ON__UINT_PTR* GID = (nullptr != context->m_quad_group_ids) ? (context->m_quad_group_ids + mark.m_quad_count0*GID_stride) : nullptr; context->m_point_count = mark.m_point_count1; context->m_quad_count = mark.m_quad_count1; context->m_fragment_count++; #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) if (bReleaseLock) context->m_lock.ReturnLock(); #endif // Copy the mesh from the fragment to the destination arrays // // All the code below must be thread safe. // // Basically, space context->m_point_stride[] and the other arrays // is reserved above and time consuming calculations take place below so // any other threads working to create this mesh can continue. if ( nullptr != P ) { const double* srcP1 = fragment->m_P + fragment->m_P_count*fragment->m_P_stride; for (const double* srcP = fragment->m_P; srcP < srcP1; srcP += fragment->m_P_stride) { P[0] = srcP[0]; P[1] = srcP[1]; P[2] = srcP[2]; P += P_stride; } } if ( nullptr != N ) { const double* srcN1 = fragment->m_N + fragment->m_P_count*fragment->m_N_stride; for (const double* srcN = fragment->m_N; srcN < srcN1; srcN += fragment->m_N_stride) { N[0] = srcN[0]; N[1] = srcN[1]; N[2] = srcN[2]; N += N_stride; } } if ( nullptr != F ) { const ON_SubDLimitMeshFragmentGrid& quads = fragment->m_grid; const unsigned int fvi0 = (unsigned int)mark.m_point_count0; const unsigned int* srcF1 = quads.m_F + quads.m_F_count*quads.m_F_stride; for (const unsigned int* srcF = quads.m_F; srcF < srcF1; srcF += quads.m_F_stride) { F[0] = srcF[0] + fvi0; F[1] = srcF[1] + fvi0; F[2] = srcF[2] + fvi0; F[3] = srcF[3] + fvi0; F += F_stride; } } if ( nullptr != GID ) { const ON__UINT_PTR* group_id1 = GID + fragment->m_grid.m_F_count*GID_stride; while(GID < group_id1) { *GID = mark.m_fragment_group_id; GID += GID_stride; } } while (mark_count >= 2 && face_fragment_marks.UnsignedCount() == mark_count ) { // combine subfragments into a single fragment. face_fragment_marks.QuickSort( GetLimitSurfaceMesh_context_FragmentMark::CompareFaceIdAndFragmentIndex ); if (face_fragment_marks[0].m_face_id != face_fragment_marks[mark_count - 1].m_face_id ) break; if (0 != face_fragment_marks[0].m_face_fragment_index) break; if (mark_count - 1 != face_fragment_marks[mark_count - 1].m_face_fragment_index) break; mark = face_fragment_marks[0]; unsigned int* quads = mark.m_context->m_quads; const size_t quad_stride = mark.m_context->m_quad_stride; const double* points = mark.m_context->m_points; const size_t point_stride = mark.m_context->m_point_stride; const double* normals = mark.m_context->m_normals; const size_t normal_stride = mark.m_context->m_normal_stride; ON__UINT_PTR* group_ids = mark.m_context->m_quad_group_ids; const size_t group_id_stride = mark.m_context->m_quad_group_id_stride; if ( nullptr == quads || nullptr == points || nullptr == normals) break; if ( quad_stride < 4 || point_stride < 3 || normal_stride < 3 ) break; const unsigned int grid_F_count = mark.m_fragment.m_grid.m_F_count; const unsigned int grid_side_count = mark.m_fragment.m_grid.SideSegmentCount(); const size_t magic_stride = grid_side_count*quad_stride; const ON__UINT_PTR group_id = (nullptr != group_ids && group_id_stride > 0) ? group_ids[mark.m_quad_count0*group_id_stride] : 0; const unsigned int apex_point_index = (unsigned int)mark.m_point_count0; const ON_3dPoint apex_point(points+apex_point_index*point_stride); const ON_3dVector apex_normal(normals+apex_point_index*normal_stride); unsigned int* subfragment_quads = quads + face_fragment_marks[mark_count-1].m_quad_count0*quad_stride; unsigned int i; for ( i = 0; i < mark_count; i++ ) { mark = face_fragment_marks[i]; if ( grid_F_count != mark.m_fragment.m_grid.m_F_count) break; unsigned int* prev_subfragment_quads = subfragment_quads; subfragment_quads = quads + mark.m_quad_count0*quad_stride; if (false == CoincidentPointTest(apex_point_index,subfragment_quads[0],points,point_stride,normals,normal_stride)) break; subfragment_quads[0] = apex_point_index; unsigned int n; for (n = 0; n < grid_side_count; n++) { if (false == CoincidentPointTest(subfragment_quads[0],prev_subfragment_quads[0],points,point_stride,normals,normal_stride)) break; prev_subfragment_quads[0] = subfragment_quads[0]; if (false == CoincidentPointTest(subfragment_quads[1],prev_subfragment_quads[3],points,point_stride,normals,normal_stride)) break; prev_subfragment_quads[3] = subfragment_quads[1]; subfragment_quads += quad_stride; prev_subfragment_quads += magic_stride; } if (n < grid_side_count) break; subfragment_quads -= magic_stride; if (0 != group_id) { ON__UINT_PTR* p0 = group_ids + mark.m_quad_count0*group_id_stride; if (group_id != p0[0]) { for ( ON__UINT_PTR* p1 = p0 + grid_F_count; p0 < p1; p0 += group_id_stride) *p0 = group_id; } } } if ( mark_count != i ) break; break; } return true; } static int CompareQuadGroupId(const void* a, const void* b) { ON__UINT_PTR ai = *(const ON__UINT_PTR*)a; ON__UINT_PTR bi = *(const ON__UINT_PTR*)b; if (ai < bi) return -1; if (ai > bi) return 1; return 0; } ON_Mesh* ON_SubD::GetLimitSurfaceMesh( const class ON_SubDDisplayParameters& limit_mesh_parameters, ON_Mesh* destination_mesh ) const { ON_SubDDisplayParameters local_limit_mesh_parameters = limit_mesh_parameters; ON_ProgressReporter::ReportProgress( local_limit_mesh_parameters.m_progress_reporter, 0.0 ); if (destination_mesh) destination_mesh->Destroy(); const ON_SubDLevel& active_level = ActiveLevel(); if (active_level.IsEmpty()) return ON_SUBD_RETURN_ERROR(nullptr); if (ON_SubD::SubDType::Unset == active_level.m_subdivision_type ) const_cast(this)->SetSubDType(ON_SubD::SubDType::QuadCatmullClark); const unsigned int fragment_count = LimitSurfaceMeshFragmentCount(); if ( fragment_count <= 0 ) return ON_SUBD_RETURN_ERROR(nullptr); const ON_SubD::SubDType subd_type = active_level.m_subdivision_type; if (ON_SubD::SubDType::QuadCatmullClark != subd_type) { // TODO - support tri subd after quad stuff is finished. return ON_SUBD_RETURN_ERROR(nullptr); } if (0 == local_limit_mesh_parameters.m_display_density && fragment_count > FaceCount()) local_limit_mesh_parameters.m_display_density = 1; ////////////////////////////////////////////////////////////////// // // Set: // vertex_count = number of points needed to calculate the mesh // face_count = number of quad faces needed to calculate the mesh // unsigned int vertex_count = 0; unsigned int face_count = 0; unsigned int tri_count = 0; // tri count unsigned int quad_count = 0; // quad count unsigned int ngon_edge_sum = 0; // sum of edge counts for faces with m_edge_count >= 5. ON_SubDFaceIterator fit(*this); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { if (3 == f->m_edge_count) tri_count++; if (4 == f->m_edge_count) quad_count++; else ngon_edge_sum += f->m_edge_count; } if (ON_SubD::SubDType::QuadCatmullClark == subd_type) { const unsigned int m0 = 1 << local_limit_mesh_parameters.m_display_density; const unsigned int m1 = (m0 > 1) ? m0 / 2 : 1; if (ngon_edge_sum > 0 || tri_count > 0) { ngon_edge_sum += 4 * quad_count + 3 * tri_count; quad_count = 0; tri_count = 0; } vertex_count = quad_count*(m0 + 1)*(m0 + 1) + ngon_edge_sum*(m1 + 1)*(m1 + 1); face_count = quad_count*m0*m0 + ngon_edge_sum*m1*m1; } else if (ON_SubD::SubDType::TriLoopWarren == subd_type) { // TODO ADD TRI SUPPORT return ON_SUBD_RETURN_ERROR(nullptr); } if (vertex_count < 4 || face_count < 1) return ON_SUBD_RETURN_ERROR(nullptr); std::unique_ptr< ON_Mesh > up; ON_Mesh* mesh = nullptr; if (nullptr != destination_mesh) mesh = destination_mesh; else { up = std::make_unique< ON_Mesh >(); mesh = up.get(); } if ( nullptr == mesh) return ON_SUBD_RETURN_ERROR(nullptr); // mesh vertices // mesh face group ids GetLimitSurfaceMesh_context context; ON_3dPointArray& D = mesh->DoublePrecisionVertices(); context.m_point_capacity = vertex_count; context.m_points = (double*)D.Reserve(vertex_count); context.m_point_stride = 3; ON_SimpleArray< ON_3dVector > mesh_N; context.m_normal_capacity = vertex_count; context.m_normals = (double*)mesh_N.Reserve(vertex_count); context.m_normal_stride = 3; context.m_quad_capacity = face_count; context.m_quads = (unsigned int*)mesh->m_F.Reserve(face_count); context.m_quad_stride = 4; ON_SimpleArray< ON__UINT_PTR > quad_group_ids_buffer; ON__UINT_PTR* quad_group_ids = quad_group_ids_buffer.Reserve(face_count); context.m_quad_group_ids = quad_group_ids; context.m_quad_group_id_stride = 1; #if defined(OPENNURBS_SLEEPLOCK_AVAILABLE) context.m_bUseMultipleThreads = local_limit_mesh_parameters.m_bUseMultipleThreads; #endif if (ON_SubD::SubDType::QuadCatmullClark == subd_type) { //ON_Interval progress_limits(0.1,0.9); const ON_Interval progress_limits = local_limit_mesh_parameters.m_progress_reporter_interval; if (progress_limits.IsIncreasing()) { local_limit_mesh_parameters.m_progress_reporter_interval.Set( progress_limits.ParameterAt(0.0), progress_limits.ParameterAt(0.5) ); } unsigned int quad_fragment_count = GetQuadLimitSurfaceMeshFragmentsHelper( fit, local_limit_mesh_parameters, (ON__UINT_PTR)&context, GetLimitSurfaceMesh_callback ); local_limit_mesh_parameters.m_progress_reporter_interval = progress_limits; if ( 0 == quad_fragment_count ) return ON_SUBD_RETURN_ERROR(nullptr); } else if (ON_SubD::SubDType::TriLoopWarren == subd_type) { // todo - add tri support return ON_SUBD_RETURN_ERROR(nullptr); } else { return ON_SUBD_RETURN_ERROR(nullptr); } const unsigned int mesh_point_count = (unsigned int)context.m_point_count; const unsigned int mesh_face_count = (unsigned int)context.m_quad_count; if (mesh_point_count < 3 || mesh_face_count < 1) return ON_SUBD_RETURN_ERROR(nullptr); // Set face count mesh->m_F.SetCount(mesh_face_count); // Set vertex counts D.SetCount(mesh_point_count); quad_group_ids_buffer.SetCount(mesh_face_count); mesh->UpdateSinglePrecisionVertices(); // copy ON_3dVector vertex normals in mesh_N[] to ON_3fVector normals in mesh->m_N[] mesh_N.SetCount(mesh_point_count); const ON_3dVector* dN = mesh_N.Array(); const ON_3dVector* dN1 = dN + mesh_point_count; ON_3fVector* fN = mesh->m_N.Reserve(mesh_point_count); mesh->m_N.SetCount(mesh_point_count); while (dN < dN1) *fN++ = *dN++; // set bounding boxes mesh->BoundingBox(); // group all mesh faces that came from a subd control net face into an ngon. for (;;) { const ON_3dPointListRef mesh_vertex_list(mesh); const ON_MeshFaceList mesh_face_list(mesh); ON_MeshVertexFaceMap vf_map; if (!vf_map.SetFromFaceList(mesh_vertex_list.PointCount(), mesh_face_list, false)) break; const unsigned int *const* vertex_face_map = vf_map.VertexFaceMap(); if (nullptr == vertex_face_map) break; ON_SimpleArray< unsigned int > mesh_face_index_buffer; mesh_face_index_buffer.Reserve(mesh_face_count); mesh_face_index_buffer.SetCount(mesh_face_count); unsigned int* mesh_face_index = mesh_face_index_buffer.Array(); // mesh_face_index[] = permutation of (0,1,...,quad_count-1) // such that the mesh faces that came from the same level zero subd face // are grouped together. ON_Sort(ON::sort_algorithm::quick_sort, mesh_face_index, quad_group_ids, mesh_face_count, sizeof(quad_group_ids[0]), CompareQuadGroupId); ON_SimpleArray< unsigned int> ngon_fi(256); ON_SimpleArray< unsigned int> ngon_vi; ON_ProgressStepCounter counter = ON_ProgressStepCounter::Create( local_limit_mesh_parameters.m_progress_reporter, mesh_face_count, 0.5, 1.0, 50 ); for (unsigned int i = 0; i < mesh_face_count; /*empty increment*/) { // get list of faces in the mesh ngon ngon_fi.SetCount(0); ngon_fi.Append(mesh_face_index[i]); const ON__UINT_PTR ngon_group_id = quad_group_ids[mesh_face_index[i]]; for (i++; i < mesh_face_count && ngon_group_id == quad_group_ids[mesh_face_index[i]]; i++) { ngon_fi.Append(mesh_face_index[i]); } counter.IncrementStep(); if (ngon_fi.Count() < 2) continue; // create ngon ngon_vi.SetCount(0); if (ON_MeshNgon::FindNgonOuterBoundary(mesh_vertex_list, mesh_face_list, vertex_face_map, ngon_fi.UnsignedCount(), ngon_fi.Array(), ngon_vi) >= 3) { mesh->AddNgon(ngon_vi.UnsignedCount(), ngon_vi.Array(), ngon_fi.UnsignedCount(), ngon_fi.Array()); } } break; } // success ON_ProgressReporter::ReportProgress(local_limit_mesh_parameters.m_progress_reporter,1.0); up.release(); return mesh; } class VertexToDuplicate { public: const ON_SubDVertex* m_vertex = nullptr; const ON_SubDFace* m_face = nullptr; unsigned int m_mesh_V_index = 0; unsigned int m_mesh_F_index = 0; static int CompareVertexId(const class VertexToDuplicate* a, const class VertexToDuplicate*); static int CompareVertexAndFaceIds(const class VertexToDuplicate* a, const class VertexToDuplicate*); static bool NeedsDuplicated( const ON_SubDVertex* vertex ); }; int VertexToDuplicate::CompareVertexId(const class VertexToDuplicate* a, const class VertexToDuplicate* b) { if ( a == b ) return 0; if ( nullptr == a ) return -1; if ( nullptr == b ) return 1; unsigned int a_id = a->m_vertex ? a->m_vertex->m_id : 0; unsigned int b_id = b->m_vertex ? b->m_vertex->m_id : 0; if ( a_id < b_id ) return -1; if ( a_id > b_id ) return 1; return 0; } int VertexToDuplicate::CompareVertexAndFaceIds(const class VertexToDuplicate* a, const class VertexToDuplicate* b) { if ( a == b ) return 0; int rc = VertexToDuplicate::CompareVertexId(a,b); if (0 != rc) return rc; if (nullptr == a) return -1; if (nullptr == b) return 1; unsigned int a_id = a->m_face ? a->m_face->m_id : 0; unsigned int b_id = b->m_face ? b->m_face->m_id : 0; if (a_id < b_id) return -1; if (a_id > b_id) return 1; return 0; } bool VertexToDuplicate::NeedsDuplicated( const ON_SubDVertex* vertex ) { if ( nullptr == vertex || vertex->m_face_count <= 0 || vertex->m_edge_count < 2 || nullptr == vertex->m_edges ) return false; if (vertex->IsSmooth()) return false; const unsigned int edge_count = vertex->m_edge_count; for (unsigned int vei = 0; vei < edge_count; vei++) { const ON_SubDEdge* edge = vertex->Edge(vei); if ( nullptr != edge && false == edge->IsSmooth(true) && edge->m_face_count > 1 ) return true; } return false; } static bool ChangeMeshFaceIndex( unsigned int mesh_V_index0, unsigned int mesh_F_count, ON_Mesh* mesh, VertexToDuplicate& dup, ON_SimpleArray& dups_sub_array ) { int k = dups_sub_array.BinarySearch(&dup,VertexToDuplicate::CompareVertexAndFaceIds); if (k < 0) { // error. terminate creation of dups. ON_SubDIncrementErrorCount(); return false; } VertexToDuplicate* dupk = dups_sub_array.Array() + k; if (mesh_V_index0 != dup.m_mesh_V_index) { if (mesh_V_index0 == dupk->m_mesh_V_index && dupk->m_mesh_F_index < mesh_F_count) { unsigned int* fvi = (unsigned int*)(mesh->m_F[dupk->m_mesh_F_index].vi); if (fvi[0] == mesh_V_index0) fvi[0] = dup.m_mesh_V_index; if (fvi[1] == mesh_V_index0) fvi[1] = dup.m_mesh_V_index; if (fvi[2] == mesh_V_index0) fvi[2] = dup.m_mesh_V_index; if (fvi[3] == mesh_V_index0) fvi[3] = dup.m_mesh_V_index; } } dupk->m_mesh_V_index = ON_UNSET_UINT_INDEX; dupk->m_mesh_F_index = ON_UNSET_UINT_INDEX; return true; } static bool DuplicateVerticesAtCreases( ON_Mesh* mesh, ON_3dPointArray& D, ON_SimpleArray& dups_array ) { const unsigned int mesh_F_count = mesh->m_F.UnsignedCount(); const unsigned int mesh_D_count0 = D.UnsignedCount(); const unsigned int dups_count = dups_array.UnsignedCount(); if (dups_count <= 1) return true; dups_array.QuickSort(VertexToDuplicate::CompareVertexAndFaceIds); ON_SimpleArray dups_sub_array; // for searching VertexToDuplicate* dups = dups_array; VertexToDuplicate dup; unsigned int i1 = 0; for (unsigned int i0 = i1; i0 < dups_count; i0 = i1) { dup = dups[i0]; if (nullptr == dup.m_vertex) { ON_SubDIncrementErrorCount(); return false; } for (i1 = i0 + 1; i1 < dups_count; i1++) { int rc = VertexToDuplicate::CompareVertexId(&dup,dups+i1); if (rc < 0) break; if ( 0 != rc || dup.m_vertex != dups[i1].m_vertex || dup.m_mesh_V_index != dups[i1].m_mesh_V_index || dup.m_mesh_V_index >= mesh_D_count0 ) { ON_SubDIncrementErrorCount(); return false; } } if ( i1 == i0+1) continue; const unsigned int mesh_V_index0 = dup.m_mesh_V_index; const ON_3dPoint P = D[mesh_V_index0]; dups_sub_array.SetArray(dups+i0,i1-i0,0); ON_SubDSectorIterator sit; unsigned int sector_count = 0; bool bDupError = false; for (unsigned int i = i0; i < i1 && false == bDupError; i++) { if (dups[i].m_mesh_V_index >= mesh_D_count0 || dups[i].m_mesh_F_index >= mesh_F_count) { if (sector_count > 0 && ON_UNSET_UINT_INDEX == dups[i].m_mesh_V_index && ON_UNSET_UINT_INDEX == dups[i].m_mesh_F_index ) { // this dup[i] was part of a previously processed sector. continue; } // error. terminate creation of dups. ON_SubDIncrementErrorCount(); bDupError = true; break; } if (nullptr == sit.Initialize(dups[i].m_face, 0, dup.m_vertex)) { // error. terminate creation of dups. ON_SubDIncrementErrorCount(); bDupError = true; break; } if ( nullptr == sit.IncrementToCrease(-1) ) { // error. terminate creation of dups. ON_SubDIncrementErrorCount(); bDupError = true; break; } if (dup.m_vertex->IsDart()) { const ON_SubDEdge* edge = sit.CurrentEdge(0); if (nullptr == edge || false == edge->IsCrease(false) || 2 != edge->m_face_count) { ON_SubDIncrementErrorCount(); bDupError = true; break; } for (unsigned int efi = 0; efi < 2; efi++) { dup.m_face = edge->Face(efi); dup.m_mesh_V_index = D.UnsignedCount(); D.Append(P); if (false == ChangeMeshFaceIndex(mesh_V_index0, mesh_F_count, mesh, dup, dups_sub_array)) { ON_SubDIncrementErrorCount(); bDupError = true; break; } } sit.NextFace(true); } sector_count++; if (sector_count > 1) { dup.m_mesh_V_index = D.UnsignedCount(); D.Append(P); } else { dup.m_mesh_V_index = mesh_V_index0; } for (dup.m_face = sit.CurrentFace(); nullptr != dup.m_face && false == bDupError; dup.m_face = sit.NextFace(true)) { if (false == ChangeMeshFaceIndex(mesh_V_index0, mesh_F_count, mesh, dup, dups_sub_array)) { ON_SubDIncrementErrorCount(); bDupError = true; break; } } if (bDupError) break; } dups_sub_array.SetCapacity(0); if (bDupError) return false; } return true; } ON_Mesh* ON_SubD::GetControlNetMesh( ON_Mesh* destination_mesh ) const { if (destination_mesh) destination_mesh->Destroy(); const ON_SubDLevel& level = ActiveLevel(); if (level.IsEmpty()) return ON_SUBD_RETURN_ERROR(nullptr); VertexToDuplicate dup; ON_SimpleArray dups_array; const ON_SubDimple* subdimple = SubDimple(); if ( nullptr == subdimple) return nullptr; const unsigned int subd_vertex_count = level.m_vertex_count; unsigned int mesh_ngon_count = 0; unsigned int mesh_face_count = 0; unsigned int max_ngon_Vcount = 0; for (const ON_SubDFace* face = level.m_face[0]; nullptr != face; face = face->m_next_face) { if ( face->m_edge_count < 2 ) continue; if (face->m_edge_count <= 4) { mesh_face_count++; continue; } mesh_ngon_count++; mesh_face_count += face->m_edge_count; if ( max_ngon_Vcount < face->m_edge_count ) max_ngon_Vcount = face->m_edge_count; } if (subd_vertex_count < 4 || mesh_face_count < 1 ) return ON_SUBD_RETURN_ERROR(nullptr); std::unique_ptr< ON_Mesh > up; ON_Mesh* mesh = nullptr; if (nullptr != destination_mesh) mesh = destination_mesh; else { up = std::make_unique< ON_Mesh >(); mesh = up.get(); } ON_3dPointArray& D = mesh->DoublePrecisionVertices(); D.Reserve(subd_vertex_count+mesh_ngon_count); D.SetCount(0); mesh->m_F.Reserve(mesh_face_count); mesh->m_F.SetCount(0); ON_SimpleArray< ON_2udex > ngon_spans(mesh_ngon_count); bool rc = false; for (;;) { unsigned int archive_id_partition[4] = {}; level.SetArchiveId(archive_id_partition); if (archive_id_partition[1] - archive_id_partition[0] != subd_vertex_count) break; for (const ON_SubDVertex* vertex = level.m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) { unsigned int vi = vertex->ArchiveId(); if (vi < 1 || vi > subd_vertex_count) break; if (D.UnsignedCount()+1 != vi) break; D.AppendNew() = vertex->m_P; } if (D.UnsignedCount() != subd_vertex_count) break; ngon_spans.Reserve(mesh_ngon_count); unsigned int max_ngon_face_count = 0; mesh_face_count = 0; for (const ON_SubDFace* face = level.m_face[0]; nullptr != face; face = face->m_next_face) { ON_MeshFace meshf = {}; if (face->m_edge_count <= 4) { if (face->m_edge_count < 3) continue; for (unsigned short fvi = 0; fvi < face->m_edge_count; fvi++) { const ON_SubDVertex* vertex = face->Vertex(fvi); meshf.vi[fvi] = (int)((nullptr != vertex) ? vertex->ArchiveId() : 0U); if (meshf.vi[fvi] < 1 || meshf.vi[fvi] > (int)subd_vertex_count) { meshf.vi[0] = -1; break; } meshf.vi[fvi]--; if (VertexToDuplicate::NeedsDuplicated(vertex)) { dup.m_vertex = vertex; dup.m_face = face; dup.m_mesh_F_index = mesh->m_F.UnsignedCount(); dup.m_mesh_V_index = meshf.vi[fvi]; dups_array.Append(dup); } } if (-1 == meshf.vi[0] ) continue; if ( 3 == face->m_edge_count) meshf.vi[3] = meshf.vi[2]; mesh->m_F.Append(meshf); continue; } else { ON_3dPoint center_point; if (false == face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, true, center_point)) continue; ON_2udex ngon_span = { mesh->m_F.UnsignedCount(), 0 }; const unsigned int dup_count0 = dups_array.UnsignedCount(); const unsigned int Dcount0 = D.UnsignedCount(); const unsigned int Fcount0 = mesh->m_F.UnsignedCount(); meshf.vi[2] = (int)Dcount0; meshf.vi[3] = meshf.vi[2]; const ON_SubDVertex* vertex = face->Vertex(0); meshf.vi[1] = (nullptr != vertex) ? vertex->ArchiveId() : 0; if (meshf.vi[1] < 1 || meshf.vi[1] >= (int)subd_vertex_count) continue; meshf.vi[1]--; if (VertexToDuplicate::NeedsDuplicated(vertex)) { dup.m_vertex = vertex; dup.m_face = face; dup.m_mesh_F_index = mesh->m_F.UnsignedCount(); dup.m_mesh_V_index = meshf.vi[1]; dups_array.Append(dup); } D.Append(center_point); for (unsigned short fvi = 1; fvi <= face->m_edge_count; fvi++) { meshf.vi[0] = meshf.vi[1]; vertex = face->Vertex(fvi % face->m_edge_count); meshf.vi[1] = (int)((nullptr != vertex) ? vertex->ArchiveId() : 0U); if (meshf.vi[1] < 1 || meshf.vi[1] > (int)subd_vertex_count) { meshf.vi[0] = -1; break; } meshf.vi[1]--; if (VertexToDuplicate::NeedsDuplicated(vertex)) { dup.m_vertex = vertex; dup.m_face = face; dup.m_mesh_F_index = mesh->m_F.UnsignedCount(); dup.m_mesh_V_index = meshf.vi[1]; dups_array.Append(dup); } mesh->m_F.Append(meshf); } ngon_span.j = mesh->m_F.UnsignedCount(); unsigned int ngon_face_count = ngon_span.j - ngon_span.i; if (-1 == meshf.vi[0] || ngon_face_count < 3) { D.SetCount(Dcount0); mesh->m_F.SetCount(Fcount0); dups_array.SetCount(dup_count0); continue; } ngon_span.j = mesh->m_F.UnsignedCount(); if (ngon_face_count >= 3) { ngon_spans.Append(ngon_span); if ( ngon_face_count > max_ngon_face_count) max_ngon_face_count = ngon_face_count; } } } if (mesh->m_F.UnsignedCount() <= 0) break; rc = true; break; } level.ClearArchiveId(); if (false == rc ) return ON_SUBD_RETURN_ERROR(nullptr); if (D.UnsignedCount() < 3 || mesh->m_F.UnsignedCount() < 1) return ON_SUBD_RETURN_ERROR(nullptr); DuplicateVerticesAtCreases(mesh,D,dups_array); mesh->UpdateSinglePrecisionVertices(); mesh->ComputeFaceNormals(); mesh->ComputeVertexNormals(); mesh->BoundingBox(); // group all mesh faces that came from the same level zero subd face into an ngon. if (ngon_spans.UnsignedCount() > 0 && max_ngon_Vcount >= 3) { ON_SimpleArray< unsigned int> ngon_buffer; unsigned int* ngon_fi = ngon_buffer.Reserve(2*max_ngon_Vcount); unsigned int* ngon_vi = ngon_fi + max_ngon_Vcount; for (unsigned int ngon_dex = 0; ngon_dex < ngon_spans.UnsignedCount(); ngon_dex++ ) { ON_2udex ngon_span = ngon_spans[ngon_dex]; unsigned int Fcount = ngon_span.j-ngon_span.i; if ( Fcount < 3) continue; ngon_fi[0] = ngon_span.i; ngon_fi[0] = (unsigned int)mesh->m_F[ngon_fi[0]].vi[0]; unsigned int ngon_Vcount = 0; for (unsigned int i = ngon_span.i; i < ngon_span.j; i++) { ngon_fi[ngon_Vcount] = i; ngon_vi[ngon_Vcount] = (unsigned int)(mesh->m_F[i].vi[0]); ngon_Vcount++; } mesh->AddNgon(ngon_Vcount, ngon_vi, ngon_Vcount, ngon_fi ); } } up.release(); return mesh; } double* ON_SubDQuadFaceMesher::Internal_Buffer(size_t buffer_capacity) { if (buffer_capacity < m__buffer_capacity) return m__buffer; if (nullptr != m__buffer) { delete[] m__buffer; m__buffer = nullptr; } m__buffer_capacity = 0; m__buffer = new(std::nothrow) double[buffer_capacity]; if (nullptr != m__buffer) m__buffer_capacity = buffer_capacity; return m__buffer; } void ON_SubDQuadFaceMesher::SetDestinationInitialize( ON_SubDQuadFaceMesher::Output output ) { // Reuse workspaces ReturnAllFixedSizeHeaps(); m_output = output; m_display_density = 0; m_count = 0; m_capacity = 0; m_point_stride0 = 0; m_point_stride1 = 0; m_points = nullptr; m_normal_stride0 = 0; m_normal_stride1 = 0; m_normals = nullptr; m_patcher = nullptr; } bool ON_SubDQuadFaceMesher::SetDestinationToLocalMeshBuffer( unsigned int mesh_density ) { const unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); const size_t point_count = (count > 0) ? ((count + 1)*(count + 1)) : 0; // initialize this to make another mesh. SetDestinationInitialize(ON_SubDQuadFaceMesher::Output::mesh); double* buffer = Internal_Buffer(6 * point_count); if (point_count > 0 && nullptr == buffer ) { return ON_SUBD_RETURN_ERROR(false); } if (0 == count && mesh_density > 0) return ON_SUBD_RETURN_ERROR(false); m_points = buffer; m_normals = buffer + 3*point_count; m_point_stride0 = 3; m_point_stride1 = (count+1)*m_point_stride0; m_normal_stride0 = m_point_stride0; m_normal_stride1 = m_point_stride1; m_display_density = mesh_density; m_count = 0; m_capacity = count; return (count == m_count); } bool ON_SubDQuadFaceMesher::SetDestinationToMeshFragment( unsigned int mesh_density, class ON_SubDLimitMeshFragment& fragment ) { // initialize this SetDestinationInitialize(ON_SubDQuadFaceMesher::Output::mesh); if ( nullptr == fragment.m_P || nullptr == fragment.m_N) return ON_SUBD_RETURN_ERROR(false); const unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); const unsigned int point_count = (count + 1)*(count + 1); if ( point_count > fragment.m_P_capacity ) return ON_SUBD_RETURN_ERROR(false); const unsigned int quad_count = count*count; if (fragment.m_grid.m_F_count != quad_count || 0 != fragment.m_grid.m_F_level_of_detail || nullptr == fragment.m_grid.m_F || fragment.m_grid.m_F_stride != 4) { fragment.m_grid = ON_SubDLimitMeshFragmentGrid::Quads(count,0); if ( nullptr == fragment.m_grid.m_F) return ON_SUBD_RETURN_ERROR(false); } m_point_stride0 = fragment.m_P_stride; m_point_stride1 = (count+1)*m_point_stride0; m_points = fragment.m_P; m_normal_stride0 = fragment.m_N_stride; m_normal_stride1 = (count+1)*m_normal_stride0; m_normals = fragment.m_N; m_display_density = mesh_density; m_count = 0; m_capacity = count; return true; } bool ON_SubDQuadFaceMesher::SetDestinationToPatchFragment( class ON_SubDQuadFacePatcher& patcher ) { // initialize this SetDestinationInitialize(ON_SubDQuadFaceMesher::Output::patches); m_patcher = &patcher; m_display_density = patcher.m_display_density; return true; } bool ON_SubDQuadFaceMesher::UnsetMeshPoints() { if (ON_SubDQuadFaceMesher::Output::mesh != m_output) return ON_SUBD_RETURN_ERROR(false); if (nullptr == m_points || 0 == m_count) return ON_SUBD_RETURN_ERROR(false); double* p1end = m_points + (m_count + 1)*m_point_stride1; for (double* p1 = m_points; p1 < p1end; p1 += m_point_stride1) { double* pend = p1 + (m_count + 1)*m_point_stride0; for (double* p = p1; p < pend; p += m_point_stride0) *p = ON_UNSET_VALUE; } return true; } void ON_SubDQuadFaceMesher::Get6xCubicBasis( double t, double b6[4] ) { const double knot[6] = {-2.0,-1.0,0.0,1.0,2.0,3.0}; double N[16]; ON_EvaluateNurbsBasis( 4, knot, t, N ); b6[0] = 6.0*N[0]; b6[1] = 6.0*N[1]; b6[2] = 6.0*N[2]; b6[3] = 6.0*N[3]; // TODO - hard code polynomial formula //const double tt = t*t; //const double s = 1.0 - t; //const double ss = s*s; //b6[0] = (t*tt); //b6[1] = (1.0 + 3.0*(s*tt)); //b6[2] = (1.0 + 3.0*(t*ss)); //b6[3] = (s*ss); } void ON_SubDQuadFaceMesher::GetCubicBasisDerivative( double t, double d[4] ) { const double knot[6] = {-2.0,-1.0,0.0,1.0,2.0,3.0}; double N[16]; ON_EvaluateNurbsBasis( 4, knot, t, N ); ON_EvaluateNurbsBasisDerivatives( 4, knot, 1, N ); d[0] = N[4]; d[1] = N[5]; d[2] = N[6]; d[3] = N[7]; // TODO - hard code polynomial formula //const double tt = t*t; //const double s = 1.0 - t; //const double ss = s*s; //d[0] = tt; //d[1] = (0.5 + t) - 1.5*tt; //d[2] = -(0.5 + s) + 1.5*ss; //d[3] = -ss; } bool ON_SubDQuadFaceMesher::Internal_EvaluateSurfaceNormalBackup1( double s, double t, unsigned int count, unsigned int i, unsigned int j, double* N ) const { const double knot[6] = {-2.0,-1.0,0.0,1.0,2.0,3.0}; ON_3dVector v[6]; // P, Du, Dv, Duu, Duv, Dvv bool bHaveNormal = ON_EvaluateNurbsSurfaceSpan( 3, // dim 0, // is_rat 4, 4, // order, order knot, // knot0[] array of (2*order0-2) doubles knot, // knot1[] array of (2*order1-2) doubles 12,3, // cv_stride0, cv_stride1 &m_srf_cv[0][0][0],// cv at "lower left" of bispan 2, // der_count, // number of derivatives to compute (>=0) s,t, // evaluation parameters 3, // v_stride &v[0][0] // P, Du, Dv, Duu, Duv, Dvv returned here ); if (bHaveNormal) { // P[], Du, Dv and V[0], V[1], V[2] should be nearly identical int limit_dir = 0; if (0 == i) { limit_dir = (j < count) ? 1 : 2; } else if (count == i) { limit_dir = (j < count) ? 4 : 3; } if (0 == j) { limit_dir = 1; } else if (count == j) { limit_dir = 4; } bHaveNormal = ON_EvNormal(limit_dir, v[1], v[2], v[3], v[4], v[5], *((ON_3dVector*)N)); } return bHaveNormal; } bool ON_SubDQuadFaceMesher::Internal_EvaluateSurfaceNormalBackup2( const double* P00, unsigned int count, unsigned int i, unsigned int j, double* N ) const { if (false == ((const ON_3dPoint*)(P00 + ((i * m_point_stride0) + (j * m_point_stride1))))->IsValid() ) return false; ON_2dex corners_dex[4]; ON_3dPoint corners[4]; if ( i <= 0 || i >= count || j <= 0 || j >= count ) { corners_dex[0].i = (i > 0) ? (i - 1) : 0; corners_dex[0].j = (j > 0) ? (j - 1) : 0; corners_dex[2].i = (i < count) ? (i + 1) : count; corners_dex[2].j = (j < count) ? (j + 1) : count; corners_dex[1].i = corners_dex[2].i; corners_dex[1].j = corners_dex[0].j; corners_dex[3].i = corners_dex[0].i; corners_dex[3].j = corners_dex[2].j; } else { corners_dex[0].i = (i - 1); corners_dex[0].j = j; corners_dex[1].i = i; corners_dex[1].j = (j - 1); corners_dex[2].i = (i + 1); corners_dex[2].j = j; corners_dex[3].i = i; corners_dex[3].j = (j + 1); } for (int k = 0; k < 4; k++) { corners[k] = P00 + ((corners_dex[k].i * m_point_stride0) + (corners_dex[k].j * m_point_stride1)); if (false == corners[k].IsValid()) return false; } const ON_3dVector A (corners[2] - corners[0]); const ON_3dVector B (corners[3] - corners[1]); *((ON_3dVector*)N) = ON_CrossProduct(A, B); bool bHaveNormal = ((ON_3dVector*)N)->Unitize(); return bHaveNormal; } bool ON_SubDQuadFaceMesher::EvaluateSurface( unsigned int count, unsigned int point_i0, unsigned int point_j0 ) const { double iso_cv[4][3]; double b[4], Du[3], Dv[3], s, t; unsigned int i, j; if ( nullptr == m_points || m_point_stride0 < 3 || m_point_stride1 < 3) return ON_SUBD_RETURN_ERROR(false); // verify that count is a power of 2 for ( i = count; i > 1; i /= 2) { if (0 != (i%2)) return ON_SUBD_RETURN_ERROR(false); // count != 2^n } if (1 != i) return ON_SUBD_RETURN_ERROR(false); // count != 2^n double* P00 = m_points + point_i0*m_point_stride0 + point_j0*m_point_stride1; const size_t normal_stride[2] = {(nullptr != m_normals)?m_normal_stride0:0,(nullptr != m_normals)?m_normal_stride1:0}; double* N00 = m_normals + point_i0*normal_stride[0] + point_j0*normal_stride[1]; const double delta_t = 1.0 / ((double)count); if (nullptr != N00) { for (i = 0; i <= count; i++) { // Set iso_cv[][] = cvs for isocurve(s) = srf(i*delta_t,s) s = (i < count) ? (i*delta_t) : 1.0; ON_SubDQuadFaceMesher::Get6xCubicBasis(s, b); const double* srf = &m_srf_cv[0][0][0]; iso_cv[0][0] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[0][1] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[0][2] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[1][0] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[1][1] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[1][2] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[2][0] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[2][1] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[2][2] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[3][0] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[3][1] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; srf++; iso_cv[3][2] = ((b[0] * srf[0] + b[3] * srf[36]) + (b[1] * srf[12] + b[2] * srf[24])) / 6.0; double* P = P00 + (i * m_point_stride0); double* N = N00 + (i * normal_stride[0]); for (j = 0; j <= count; j++) { if (ON_UNSET_VALUE == P[0]) { // evaluate Dv(i*delta_t,j*delta_t) and save it in N[0,1,2] t = (j < count) ? (j*delta_t) : 1.0; ON_SubDQuadFaceMesher::GetCubicBasisDerivative(t, b); N[0] = ((b[0] * iso_cv[0][0] + b[3] * iso_cv[3][0]) + (b[1] * iso_cv[1][0] + b[2] * iso_cv[2][0])); N[1] = ((b[0] * iso_cv[0][1] + b[3] * iso_cv[3][1]) + (b[1] * iso_cv[1][1] + b[2] * iso_cv[2][1])); N[2] = ((b[0] * iso_cv[0][2] + b[3] * iso_cv[3][2]) + (b[1] * iso_cv[1][2] + b[2] * iso_cv[2][2])); } P += m_point_stride1; N += normal_stride[1]; } } } bool bUseSurfaceNormalBackup2 = false; for (j = 0; j <= count; j++) { // Set iso_cv[][] = cvs for isocurve(s) = srf(s,j*delta_t) t = (j < count) ? (j*delta_t) : 1.0; ON_SubDQuadFaceMesher::Get6xCubicBasis(t, b); const double* srf = &m_srf_cv[0][0][0]; // The ((first + fourth) + (second + third)) insures the values of the // mesh points and normals will be exactly symmetric when the control points are exactly // symmetric. iso_cv[0][0] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf++; iso_cv[0][1] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf++; iso_cv[0][2] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf = &m_srf_cv[1][0][0]; iso_cv[1][0] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf++; iso_cv[1][1] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf++; iso_cv[1][2] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf = &m_srf_cv[2][0][0]; iso_cv[2][0] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf++; iso_cv[2][1] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf++; iso_cv[2][2] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf = &m_srf_cv[3][0][0]; iso_cv[3][0] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf++; iso_cv[3][1] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; srf++; iso_cv[3][2] = ((b[0] * srf[0] + b[3] * srf[9]) + (b[1] * srf[3] + b[2] * srf[6])) / 6.0; double* P = P00 + (j * m_point_stride1); double* N = N00 + (j * normal_stride[1]); for (i = 0; i <= count; i++) { if (ON_UNSET_VALUE == P[0]) { s = (i < count) ? (i*delta_t) : 1.0; ON_SubDQuadFaceMesher::Get6xCubicBasis(s, b); // P = srf(i*delta_t,j*delta_t) P[0] = ((b[0] * iso_cv[0][0] + b[3] * iso_cv[3][0]) + (b[1] * iso_cv[1][0] + b[2] * iso_cv[2][0])) / 6.0; P[1] = ((b[0] * iso_cv[0][1] + b[3] * iso_cv[3][1]) + (b[1] * iso_cv[1][1] + b[2] * iso_cv[2][1])) / 6.0; P[2] = ((b[0] * iso_cv[0][2] + b[3] * iso_cv[3][2]) + (b[1] * iso_cv[1][2] + b[2] * iso_cv[2][2])) / 6.0; if (nullptr != N) { // Above, I saved Dv in N[]; Dv[0] = N[0]; Dv[1] = N[1]; Dv[2] = N[2]; ON_SubDQuadFaceMesher::GetCubicBasisDerivative(s, b); // evaluate = Du(i*delta_t,j*delta_t) Du[0] = ((b[0] * iso_cv[0][0] + b[3] * iso_cv[3][0]) + (b[1] * iso_cv[1][0] + b[2] * iso_cv[2][0])); Du[1] = ((b[0] * iso_cv[0][1] + b[3] * iso_cv[3][1]) + (b[1] * iso_cv[1][1] + b[2] * iso_cv[2][1])); Du[2] = ((b[0] * iso_cv[0][2] + b[3] * iso_cv[3][2]) + (b[1] * iso_cv[1][2] + b[2] * iso_cv[2][2])); // surface normal = Du X Dv *((ON_3dVector*)N) = ON_CrossProduct(Du, Dv); bool bHaveNormal = ((ON_3dVector*)N)->Unitize(); if (false == bHaveNormal) { bHaveNormal = Internal_EvaluateSurfaceNormalBackup1(s,t,count,i,j,N); if (false == bHaveNormal) { bUseSurfaceNormalBackup2 = true; N[0] = 0.0; N[1] = 0.0; N[2] = 0.0; } } } } P += m_point_stride0; N += normal_stride[0]; } } if (bUseSurfaceNormalBackup2) { for (j = 0; j <= count; j++) { double* N = N00 + (j * normal_stride[1]); for (i = 0; i <= count; i++) { if (0.0 == N[0] && 0.0 == N[1] && 0.0 == N[2] ) { if ( false == Internal_EvaluateSurfaceNormalBackup2(P00,count,i,j,N) ) { bUseSurfaceNormalBackup2 = true; ON_ERROR("Unable to get surface normal."); N[0] = 0.0; N[1] = 0.0; N[2] = 0.0; } } N += normal_stride[0]; } } } return true; } bool ON_SubDQuadFacePatcher::Send4x4Patch( unsigned int display_density, const class ON_SubDFaceRegion& face_region, ON_SubDLimitNurbsFragment::BispanType bispan_type ) { m_patch_fragment.m_type = ON_SubDLimitNurbsFragment::Type::BicubicSingle; m_patch_fragment.m_bispan_type[0] = bispan_type; m_patch_fragment.m_bispan_type[1] = ON_SubDLimitNurbsFragment::BispanType::None; m_patch_fragment.m_bispan_type[2] = ON_SubDLimitNurbsFragment::BispanType::None; m_patch_fragment.m_bispan_type[3] = ON_SubDLimitNurbsFragment::BispanType::None; m_patch_fragment.m_face_region = face_region; const bool rc = m_fragment_callback_function(m_fragment_callback_context,&m_patch_fragment); // erase any region modifications made by the callback m_patch_fragment.m_face_region = face_region; return rc; } bool ON_SubDQuadFacePatcher::Send5x5Patch( unsigned int display_density, const class ON_SubDFaceRegion& face_region, const ON_SubDLimitNurbsFragment::BispanType bispan_type[4] ) { m_patch_fragment.m_type = ON_SubDLimitNurbsFragment::Type::BicubicQuadrant; m_patch_fragment.m_bispan_type[0] = bispan_type[0]; m_patch_fragment.m_bispan_type[1] = bispan_type[1]; m_patch_fragment.m_bispan_type[2] = bispan_type[2]; m_patch_fragment.m_bispan_type[3] = bispan_type[3]; m_patch_fragment.m_face_region = face_region; const bool rc = m_fragment_callback_function(m_fragment_callback_context,&m_patch_fragment); // erase any region modifications made by the callback m_patch_fragment.m_face_region = face_region; return rc; } bool ON_SubDQuadFaceMesher::GetLimitSubMeshOrPatch( const ON_SubDFaceRegion& face_region, ON_SubDQuadNeighborhood* qft, unsigned int display_density, unsigned int point_i0, unsigned int point_j0 ) { // count = power of 2 = 2^display_density unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(display_density); if ( 0 == count ) return ON_SUBD_RETURN_ERROR(false); if (nullptr == qft || false == qft->IsSet()) return ON_SUBD_RETURN_ERROR(false); if ( ON_SubDQuadFaceMesher::Output::mesh == m_output && count > m_capacity) return ON_SUBD_RETURN_ERROR(false); if (qft->m_bIsCubicPatch) { switch (m_output) { case ON_SubDQuadFaceMesher::Output::mesh: if (false == qft->GetLimitSurfaceCV(&m_srf_cv[0][0][0], 4U)) return ON_SUBD_RETURN_ERROR(false); return EvaluateSurface(count, point_i0, point_j0); break; case ON_SubDQuadFaceMesher::Output::patches: if (false == qft->GetLimitSurfaceCV(&m_patcher->m_patch_fragment.m_patch_cv[0][0][0], 5U)) return ON_SUBD_RETURN_ERROR(false); //Internal_UpdateFaceRegionCorners(qft, face_region); m_patcher->Send4x4Patch(display_density,face_region,ON_SubDLimitNurbsFragment::BispanType::Exact); return true; // even if callback returns false break; } return ON_SUBD_RETURN_ERROR(false); } if (count > 1 && display_density > 0) { count /= 2; bool bSubDivide[4] = {}; unsigned int subdivide_count = 0; for (unsigned int q0fvi = 0; q0fvi < 4; q0fvi++) { if ( qft->m_bExactQuadrantPatch[q0fvi] ) { if (ON_SubDQuadFaceMesher::Output::mesh == m_output) { // Use the exact patch to calcuate exact mesh vertex and normal values if (qft->GetLimitSubSurfaceSinglePatchCV(q0fvi, m_srf_cv)) { unsigned int submesh_point_i0 = point_i0 + ((1 == q0fvi || 2 == q0fvi) ? count : 0); unsigned int submesh_point_j0 = point_j0 + ((2 == q0fvi || 3 == q0fvi) ? count : 0); if (false == EvaluateSurface(count, submesh_point_i0, submesh_point_j0)) return ON_SUBD_RETURN_ERROR(false); continue; } } } else { // local subdivision is required bSubDivide[q0fvi] = true; subdivide_count++; } } if (ON_SubDQuadFaceMesher::Output::patches == m_output && subdivide_count < 4 ) { // Get the bicubic NURBS control points for the patches that are exact. // (subdivide_count < 4) means there is at least one. // These are delivered as a collection to enable merging them into as // large a face/patch/... as possible. ON_SubDLimitNurbsFragment::BispanType pt[4] = { ON_SubDLimitNurbsFragment::BispanType::None, ON_SubDLimitNurbsFragment::BispanType::None, ON_SubDLimitNurbsFragment::BispanType::None, ON_SubDLimitNurbsFragment::BispanType::None }; // Harvest any exact patches that are available at this subdivision level const bool bEnableApproximatePatch = false; unsigned int quadrant_count = qft->GetLimitSubSurfaceMultiPatchCV( bEnableApproximatePatch, m_patcher->m_patch_fragment.m_patch_cv, pt ); if ( 4 != subdivide_count + quadrant_count ) return ON_SUBD_RETURN_ERROR(false); m_patcher->m_patch_fragment.m_type = ON_SubDLimitNurbsFragment::Type::BicubicQuadrant; //Internal_UpdateFaceRegionCorners(qft, face_region); bool bCallbackResult = m_patcher->Send5x5Patch( display_density, face_region, pt ); if ( false == bCallbackResult) return true; } for (unsigned int q0fvi = 0; q0fvi < 4; q0fvi++) { if ( false == bSubDivide[q0fvi]) continue; // local subdivision is required ON_SubD_FixedSizeHeap* fsh = CheckOutLocalFixedSizeHeap(); if ( nullptr == fsh ) return ON_SUBD_RETURN_ERROR(false); ON_SubDQuadNeighborhood qft1; if (false == qft->Subdivide( q0fvi, *fsh, &qft1 )) { ReturnLocalFixedSizeHeap(fsh); return ON_SUBD_RETURN_ERROR(false); } //if (ON_SubDQuadFaceMesher::Output::patches == m_output) // Internal_UpdateFaceRegionCorners(qft, face_region); subdivide_count--; if (0 == subdivide_count) { // If there is an exraordiary vertex and qft is using one of the // m_fsh[] on this class, then the recursion will need the // m_fsh the no longer needed qft is using. const bool bRetainFixedSizeHeap = nullptr == qft->m_fsh || false == ReturnLocalFixedSizeHeap(qft->m_fsh); ON_SubDQuadNeighborhood::Clear(qft, bRetainFixedSizeHeap); } unsigned int submesh_point_i0 = point_i0 + ((1==q0fvi || 2==q0fvi) ? count : 0); unsigned int submesh_point_j0 = point_j0 + ((2==q0fvi || 3==q0fvi) ? count : 0); ON_SubDFaceRegion face_corner_region(face_region); face_corner_region.Push(q0fvi); const bool rc = GetLimitSubMeshOrPatch(face_corner_region, &qft1, display_density-1, submesh_point_i0, submesh_point_j0 ); ReturnLocalFixedSizeHeap(fsh); if ( false == rc ) return ON_SUBD_RETURN_ERROR(false); } return true; } if (1 == count && 0 == display_density) { // No more subdivison steps are permitted if (ON_SubDQuadFaceMesher::Output::patches == m_output) { ON_SubDLimitNurbsFragment::BispanType pt[4] = { ON_SubDLimitNurbsFragment::BispanType::None, ON_SubDLimitNurbsFragment::BispanType::None, ON_SubDLimitNurbsFragment::BispanType::None, ON_SubDLimitNurbsFragment::BispanType::None }; ON_SubDComponentRegionBreakpoint(&face_region.m_face_region); // Harvest whatever patches are available and allow approximate patches to be returned // if an appropriate number of subdivisions have been performed const bool bEnableApproximatePatch = qft->m_extraordinary_corner_vertex_count <= 1 && face_region.m_face_region.SubdivisionCount() >= 2; while( bEnableApproximatePatch && 1 == qft->m_extraordinary_corner_vertex_count && 1 == qft->m_exact_quadrant_patch_count && nullptr != qft->m_center_edges[0] && nullptr != qft->m_center_edges[1] && nullptr != qft->m_center_edges[2] && nullptr != qft->m_center_edges[3] ) { bool bIsDart = false; unsigned int extraordinary_vertex_index = qft->ExtraordinaryCenterVertexIndex(ON_SubD::VertexTag::Crease, 4); if (extraordinary_vertex_index > 3 && ON_SubDQuadFaceMesher::Output::patches == m_output) { // Test for Dart fixes RH-31322 extraordinary_vertex_index = qft->ExtraordinaryCenterVertexIndex(ON_SubD::VertexTag::Dart, 4); if ( extraordinary_vertex_index > 3) break; bIsDart = true; } const ON_SubDVertex* extraordinary_vertex = qft->CenterVertex(extraordinary_vertex_index); if (nullptr == extraordinary_vertex) break; ON_SubD_FixedSizeHeap* fsh = CheckOutLocalFixedSizeHeap(); if (nullptr == fsh) break; ON_SubDQuadNeighborhood qft1; // claculate limit points on edges needed to get approximate NURBS patches near the singular point. if (qft->Subdivide(extraordinary_vertex_index, *fsh, &qft1)) { ON_2dex qft1_vdex[2] = {ON_2dex::Unset,ON_2dex::Unset}; unsigned int center_edge_index[2] = { ON_UNSET_UINT_INDEX,ON_UNSET_UINT_INDEX }; switch (extraordinary_vertex_index) { case 0: center_edge_index[0] = 3; qft1_vdex[0] = ON_2dex(1, 2); center_edge_index[1] = 0; qft1_vdex[1] = ON_2dex(2, 1); break; case 1: center_edge_index[0] = 0; qft1_vdex[0] = ON_2dex(1, 1); center_edge_index[1] = 1; qft1_vdex[1] = ON_2dex(2, 2); break; case 2: center_edge_index[0] = 1; qft1_vdex[0] = ON_2dex(2, 1); center_edge_index[1] = 2; qft1_vdex[1] = ON_2dex(1, 2); break; case 3: center_edge_index[0] = 2; qft1_vdex[0] = ON_2dex(2, 2); center_edge_index[1] = 3; qft1_vdex[1] = ON_2dex(1, 1); break; default: center_edge_index[0] = ON_UNSET_UINT_INDEX; qft1_vdex[0] = ON_2dex::Unset; center_edge_index[1] = ON_UNSET_UINT_INDEX; qft1_vdex[1] = ON_2dex::Unset; break; } for (int n = 0; n < 2; n++) { if (center_edge_index[n] >= 4) continue; if (qft->m_bCenterEdgeLimitPoint[center_edge_index[n]]) continue; const ON_SubDVertex* v = qft1.m_vertex_grid[qft1_vdex[n].i][qft1_vdex[n].j]; if (nullptr == v) continue; qft->m_bCenterEdgeLimitPoint[center_edge_index[n]] = v->GetLimitPoint(ON_SubD::SubDType::QuadCatmullClark, qft1.m_face_grid[1][1], true, qft->m_center_edge_limit_point[center_edge_index[n]]); } } const ON_2dex srf_cv1_side_midpoint_dex[4] = { ON_2dex(2,0), ON_2dex(4,2), ON_2dex(2,4), ON_2dex(0,2) }; const ON_2dex srf_cv1_ccw_dex[4] = { ON_2dex(1,0), ON_2dex(0,1), ON_2dex(-1,0), ON_2dex(0,-1) }; if (ON_SubD::EdgeTag::Crease == qft->m_center_edges[extraordinary_vertex_index]->m_edge_tag) { const ON_2dex delta(srf_cv1_ccw_dex[extraordinary_vertex_index]); const ON_2dex dex0 = srf_cv1_side_midpoint_dex[extraordinary_vertex_index]; const ON_2dex dex1(dex0.i + delta.i, dex0.j + delta.j); const ON_2dex dex2(dex1.i + delta.i, dex1.j + delta.j); double *P[3] = { &qft->m_srf_cv1[dex0.i][dex0.j][0], &qft->m_srf_cv1[dex1.i][dex1.j][0], &qft->m_srf_cv1[dex2.i][dex2.j][0] }; while (ON_UNSET_VALUE == P[0][0] && ON_UNSET_VALUE == P[1][0] && ON_UNSET_VALUE == P[2][0]) { // calculate 3 additional subd points needed to get patch 3 ON_SubDQuadNeighborhood::Clear(&qft1, false); if (false == qft->Subdivide((extraordinary_vertex_index + 1) % 4, *fsh, &qft1)) break; if (1 != qft1.m_boundary_crease_count) break; for (int n = 0; n < 4; n++) { if ( qft1.m_bBoundaryCrease[n] && nullptr != qft1.m_center_edges[n] && ON_SubD::EdgeTag::Crease == qft1.m_center_edges[n]->m_edge_tag ) { ON_2dex crease_dex[3]; crease_dex[0] = ON_SubDQuadNeighborhood::CenterVertexDex(n); crease_dex[1] = ON_SubDQuadNeighborhood::CenterVertexDex((n+1)%4); ON_2dex d(crease_dex[1].i - crease_dex[0].i, crease_dex[1].j - crease_dex[0].j); crease_dex[2] = ON_2dex(crease_dex[1].i + d.i, crease_dex[1].j + d.j); int tmp = d.j; d.j = d.i; d.i = -tmp; const ON_2dex smooth_dex[3] = { ON_2dex(crease_dex[0].i + d.i, crease_dex[0].j + d.j), ON_2dex(crease_dex[1].i + d.i, crease_dex[1].j + d.j), ON_2dex(crease_dex[2].i + d.i, crease_dex[2].j + d.j) }; for (int k = 0; k < 3; k++) { const ON_SubDVertex* crease_vertex = qft1.m_vertex_grid[crease_dex[k].i][crease_dex[k].j]; if (nullptr == crease_vertex) continue; if ( ON_SubD::VertexTag::Crease != crease_vertex->m_vertex_tag) continue; const ON_SubDVertex* smooth_vertex = qft1.m_vertex_grid[smooth_dex[k].i][smooth_dex[k].j]; if (nullptr == smooth_vertex) continue; if ( ON_SubD::VertexTag::Smooth != smooth_vertex->m_vertex_tag) continue; P[k][0] = 2.0*crease_vertex->m_P[0] - smooth_vertex->m_P[0]; P[k][1] = 2.0*crease_vertex->m_P[1] - smooth_vertex->m_P[1]; P[k][2] = 2.0*crease_vertex->m_P[2] - smooth_vertex->m_P[2]; } break; } } break; } } if (ON_SubD::EdgeTag::Crease == qft->m_center_edges[(extraordinary_vertex_index+3)%4]->m_edge_tag) { const ON_2dex delta(srf_cv1_ccw_dex[(extraordinary_vertex_index+1)%4]); // +1 to go reverse direction const ON_2dex dex0(srf_cv1_side_midpoint_dex[(extraordinary_vertex_index + 3)%4]); const ON_2dex dex1(dex0.i + delta.i, dex0.j + delta.j); const ON_2dex dex2(dex1.i + delta.i, dex1.j + delta.j); double *P[3] = { &qft->m_srf_cv1[dex0.i][dex0.j][0], &qft->m_srf_cv1[dex1.i][dex1.j][0], &qft->m_srf_cv1[dex2.i][dex2.j][0] }; if (ON_UNSET_VALUE == P[0][0] && ON_UNSET_VALUE == P[1][0] && ON_UNSET_VALUE == P[2][0]) { // calculate 3 additional subd points needed to get patch 1 ON_SubDQuadNeighborhood::Clear(&qft1, false); if (false == qft->Subdivide((extraordinary_vertex_index + 3) % 4, *fsh, &qft1)) break; if (1 != qft1.m_boundary_crease_count) break; for (int n = 0; n < 4; n++) { if ( qft1.m_bBoundaryCrease[n] && nullptr != qft1.m_center_edges[n] && ON_SubD::EdgeTag::Crease == qft1.m_center_edges[n]->m_edge_tag ) { ON_2dex crease_dex[3]; crease_dex[1] = ON_SubDQuadNeighborhood::CenterVertexDex(n); crease_dex[0] = ON_SubDQuadNeighborhood::CenterVertexDex((n+1)%4); ON_2dex d(crease_dex[1].i - crease_dex[0].i, crease_dex[1].j - crease_dex[0].j); crease_dex[2] = ON_2dex(crease_dex[1].i + d.i, crease_dex[1].j + d.j); int tmp = d.i; d.i = d.j; d.j = -tmp; const ON_2dex smooth_dex[3] = { ON_2dex(crease_dex[0].i + d.i, crease_dex[0].j + d.j), ON_2dex(crease_dex[1].i + d.i, crease_dex[1].j + d.j), ON_2dex(crease_dex[2].i + d.i, crease_dex[2].j + d.j) }; for (int k = 0; k < 3; k++) { const ON_SubDVertex* crease_vertex = qft1.m_vertex_grid[crease_dex[k].i][crease_dex[k].j]; if (nullptr == crease_vertex) continue; if ( ON_SubD::VertexTag::Crease != crease_vertex->m_vertex_tag) continue; const ON_SubDVertex* smooth_vertex = qft1.m_vertex_grid[smooth_dex[k].i][smooth_dex[k].j]; if (nullptr == smooth_vertex) continue; if ( ON_SubD::VertexTag::Smooth != smooth_vertex->m_vertex_tag) continue; P[k][0] = 2.0*crease_vertex->m_P[0] - smooth_vertex->m_P[0]; P[k][1] = 2.0*crease_vertex->m_P[1] - smooth_vertex->m_P[1]; P[k][2] = 2.0*crease_vertex->m_P[2] - smooth_vertex->m_P[2]; } break; } } } } if (nullptr != fsh) ReturnLocalFixedSizeHeap(fsh); break; } unsigned int quadrant_count = qft->GetLimitSubSurfaceMultiPatchCV( bEnableApproximatePatch, m_patcher->m_patch_fragment.m_patch_cv, pt ); if (quadrant_count > 0) { m_patcher->m_patch_fragment.m_type = ON_SubDLimitNurbsFragment::Type::BicubicQuadrant; bool bCallbackResult = m_patcher->Send5x5Patch(display_density, face_region, pt); if (false == bCallbackResult) return true; } return true; } // Use limit point evalators to get exact locations and normals // for the unset corners of this mesh quad. // In a qft, all the vertices have single sectors double* P[2][2]; double* N[2][2]; ON_SubDSectorLimitPoint limit_point; for (unsigned int i = 0; i < 2; i++) { for (unsigned int j = 0; j < 2; j++) { P[i][j] = m_points + (point_i0 + i*count)*m_point_stride0 + (point_j0 + j*count)*m_point_stride1; if (ON_UNSET_VALUE == P[i][j][0]) { N[i][j] = m_normals + (point_i0 + i*count)*m_normal_stride0 + (point_j0 + j*count)*m_normal_stride1; if (false == qft->m_vertex_grid[1 + i][1 + j]->GetLimitPoint(ON_SubD::SubDType::QuadCatmullClark, qft->m_face_grid[1][1], true, limit_point )) return ON_SUBD_RETURN_ERROR(false); P[i][j][0] = limit_point.m_limitP[0]; P[i][j][1] = limit_point.m_limitP[1]; P[i][j][2] = limit_point.m_limitP[2]; N[i][j][0] = limit_point.m_limitN[0]; N[i][j][1] = limit_point.m_limitN[1]; N[i][j][2] = limit_point.m_limitN[2]; } } } return true; } return ON_SUBD_RETURN_ERROR(false); } bool ON_SubDQuadFaceMesher::GetLimitMesh( const ON_SubDFaceRegion& face_region, const ON_SubDFace* face ) { ReturnAllFixedSizeHeaps(); m_count = 0; if (ON_SubDQuadFaceMesher::Output::mesh != m_output) return ON_SUBD_RETURN_ERROR(false); if (nullptr == face || 4 != face->m_edge_count) return ON_SUBD_RETURN_ERROR(false); unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(m_display_density); if (0 == count) { return ON_SUBD_RETURN_ERROR(false); } if (count > m_capacity) { return ON_SUBD_RETURN_ERROR(false); } // Get neighborhood topology information ON_SubDQuadNeighborhood qft; if (false == qft.Set(face)) return ON_SUBD_RETURN_ERROR(false); // GetLimitSubMesh is recursive. m_count = count; UnsetMeshPoints(); return GetLimitSubMeshOrPatch(face_region,&qft,m_display_density,0,0); } bool ON_SubDQuadFaceMesher::GetLimitPatches( const ON_SubDFaceRegion& face_region, const ON_SubDFace* face ) { ReturnAllFixedSizeHeaps(); m_count = 0; if (ON_SubDQuadFaceMesher::Output::patches != m_output) return ON_SUBD_RETURN_ERROR(false); if (nullptr == face || 4 != face->m_edge_count) return ON_SUBD_RETURN_ERROR(false); unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(m_display_density); if (0 == count) { return ON_SUBD_RETURN_ERROR(false); } // Get neighborhood topology information ON_SubDQuadNeighborhood qft; if (false == qft.Set(face)) return ON_SUBD_RETURN_ERROR(false); // GetLimitSubMesh is recursive. return GetLimitSubMeshOrPatch(face_region,&qft,m_display_density,0,0); } static bool GetLimitSurfaceInStepsSetup( const ON_SubD& subd, ON_SubDDisplayParameters& limit_mesh_parameters ) { const unsigned int level_count = subd.LevelCount(); if (0 == level_count) return ON_SUBD_RETURN_ERROR(false); if (1 == level_count && ON_SubD::SubDType::Unset == subd.ActiveLevelSubDType()) const_cast< ON_SubD& >(subd).SetSubDType(ON_SubD::SubDType::QuadCatmullClark); const ON_SubD::SubDType subd_type = subd.ActiveLevelSubDType(); if (ON_SubD::SubDType::QuadCatmullClark != subd_type) { // TODO - support tri subd after quad stuff is finished. return ON_SUBD_RETURN_ERROR(false); } limit_mesh_parameters.m_progress_reporter_interval.Intersection(ON_Interval::ZeroToOne); if ( false == limit_mesh_parameters.m_progress_reporter_interval.IsIncreasing()) limit_mesh_parameters.m_progress_reporter_interval = ON_Interval::ZeroToOne; return true; } unsigned int ON_SubD::GetLimitSurfaceMeshInFragments( const class ON_SubDDisplayParameters& limit_mesh_parameters, ON__UINT_PTR fragment_callback_context, bool(*fragment_callback_function)(ON__UINT_PTR, const class ON_SubDLimitMeshFragment*) ) const { ON_SubDDisplayParameters local_limit_mesh_parameters = limit_mesh_parameters; if ( false == GetLimitSurfaceInStepsSetup(*this,local_limit_mesh_parameters) ) return ON_SUBD_RETURN_ERROR(0); ON_SubDFaceIterator fit(*this); unsigned int fragment_count = GetQuadLimitSurfaceMeshFragmentsHelper( fit, local_limit_mesh_parameters, fragment_callback_context, fragment_callback_function ); if ( fragment_count > 0 ) return fragment_count; return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubD::GetLimitSurfaceNurbsFragments( const class ON_SubDDisplayParameters& limit_mesh_parameters, ON__UINT_PTR fragment_callback_context, bool(*begin_face_callback_function)(ON__UINT_PTR ,const ON_SubDFaceRegion&),//, const class ON_SubDFace*, const class ON_SubDFace*, unsigned int), bool(*fragment_callback_function)(ON__UINT_PTR, const class ON_SubDLimitNurbsFragment*) ) const { ON_SubDDisplayParameters local_limit_mesh_parameters = limit_mesh_parameters; if ( false == GetLimitSurfaceInStepsSetup(*this,local_limit_mesh_parameters) ) return ON_SUBD_RETURN_ERROR(0); ON_SubDFaceIterator fit(*this); unsigned int fragment_count = GetQuadLimitSurfacePatchFragmentsHelper( fit, local_limit_mesh_parameters, fragment_callback_context, begin_face_callback_function, fragment_callback_function ); if ( fragment_count > 0 ) return fragment_count; return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDFaceIterator::LimitSurfaceMeshFragmentCount( ON_SubD::FacetType facet_type ) const { unsigned int fragment_count = 0; unsigned short ordinary_edge_count = (ON_SubD::FacetType::Tri == facet_type) ? 3U : 4U; // default is quads for (const ON_SubDFace* face = m_face_first; nullptr != face; face = face->m_next_face) { if ( ordinary_edge_count == face->m_edge_count ) fragment_count++; else fragment_count += face->m_edge_count; } return fragment_count; } unsigned int ON_SubD::LimitSurfaceMeshFragmentCount() const { return ActiveLevel().LimitSurfaceMeshFragmentCount(); } unsigned int ON_SubDLevel::LimitSurfaceMeshFragmentCount() const { unsigned int fragment_count = 0; ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(m_subdivision_type); unsigned short ordinary_edge_count = (ON_SubD::FacetType::Tri == facet_type) ? 3U : 4U; // default is quads for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) { if ( ordinary_edge_count == face->m_edge_count ) fragment_count++; else fragment_count += face->m_edge_count; } return fragment_count; } ON_SubDLimitMesh ON_SubD::UpdateLimitSurfaceMesh( unsigned int minimum_display_density ) const { ON_SubDDisplayParameters display_parameters; display_parameters.m_display_density = minimum_display_density; return ActiveLevel().UpdateLimitSurfaceMesh(*this,display_parameters); } ON_SubDLimitMesh ON_SubDLevel::UpdateLimitSurfaceMesh( const ON_SubD& subd, const class ON_SubDDisplayParameters& display_parameters ) const { if ( IsEmpty() ) return ON_SubDLimitMesh::Empty; if (m_limit_mesh.IsEmpty() || display_parameters.m_display_density > m_limit_mesh.DisplayParameters().m_display_density) { ON_SubDLimitMesh local_limit_mesh; if (nullptr != ON_SubDLimitMesh::Create(subd, display_parameters, &local_limit_mesh)) { ON_SubDLimitMesh::Swap(m_limit_mesh,local_limit_mesh); local_limit_mesh.Clear(); } } return m_limit_mesh; } class ON_SubDLimitMesh ON_SubD::LimitSurfaceMesh() const { return ActiveLevel().m_limit_mesh; } void ON_SubD::ClearLimitSurfaceMesh() const { const ON_SubDLevel* level = ActiveLevelConstPointer(); if ( nullptr != level ) level->m_limit_mesh = ON_SubDLimitMesh::Empty; } void ON_SubD::ClearEvaluationCache() const { const ON_SubDLevel* level = ActiveLevelConstPointer(); if (nullptr != level) { level->ClearEdgeFlags(); level->ClearBoundingBox(); level->ClearSubdivisonAndLimitPoints(); level->m_limit_mesh = ON_SubDLimitMesh::Empty; } } //////////////////////////////////////////////////////////////////////////// class ON_SUBD_CLASS ON_SubDLimitSurfaceFragment { public: ON_SubDLimitSurfaceFragment() = default; ~ON_SubDLimitSurfaceFragment() = default; ON_SubDLimitSurfaceFragment(const ON_SubDLimitSurfaceFragment&) = default; ON_SubDLimitSurfaceFragment& operator=(const ON_SubDLimitSurfaceFragment&) = default; public: static const ON_SubDLimitSurfaceFragment Empty; public: // m_face_region identifies what part of the SubD level0 face is or will be modeled by m_surface. ON_SubDFaceRegion m_face_region; // knot vector is uniform and not clamped. ON_NurbsSurface* m_surface = nullptr; ON_SubDLimitSurfaceFragment* Quadrant(unsigned int quadrant_index, bool bAllocateIfMissing); ON_SubDLimitSurfaceFragment* Parent(); static ON_SubDLimitSurfaceFragment* AllocateSurfaceFragment(); static void ReturnSurfaceFragment(ON_SubDLimitSurfaceFragment*); bool SetSurface(ON_NurbsSurface* surface); bool SetSurfaceFromQuadrants( ON_SubD::NurbsSurfaceType nurbs_surface_type ); bool SetQuadrantSurface(ON_NurbsSurface* quadrant_surface,unsigned int quadrant_index); private: // Parent fragment for this ON_SubDLimitSurfaceFragment* m_parent = nullptr; // The 4 quadrants of this region ON_SubDLimitSurfaceFragment* m_quadrants[4] = {}; static ON_FixedSizePool m_fsp; }; ON_FixedSizePool ON_SubDLimitSurfaceFragment::m_fsp; ON_SubDLimitSurfaceFragment* ON_SubDLimitSurfaceFragment::AllocateSurfaceFragment() { ON_MemoryAllocationTracking disable_tracking(false); if (0 == ON_SubDLimitSurfaceFragment::m_fsp.SizeofElement()) { ON_SubDLimitSurfaceFragment::m_fsp.Create(sizeof(ON_SubDLimitSurfaceFragment), 64, 64); } ON_SubDLimitSurfaceFragment* f = (ON_SubDLimitSurfaceFragment*)ON_SubDLimitSurfaceFragment::m_fsp.AllocateElement(); if (nullptr == f) { ON_SUBD_ERROR("Allocation failed"); } return f; } void ON_SubDLimitSurfaceFragment::ReturnSurfaceFragment(ON_SubDLimitSurfaceFragment* f ) { if (nullptr != f) ON_SubDLimitSurfaceFragment::m_fsp.ReturnElement(f); } bool ON_SubDLimitSurfaceFragment::SetSurface(ON_NurbsSurface* surface) { if (nullptr == surface) return false; if (nullptr != m_surface) { ON_SUBD_ERROR("Surface exists."); return false; } if ( nullptr != m_quadrants[0] || nullptr != m_quadrants[1] || nullptr != m_quadrants[2] || nullptr != m_quadrants[3] ) { ON_SUBD_ERROR("Setting surface when quadrants exist."); } m_surface = surface; return true; } bool ON_SubDLimitSurfaceFragment::SetQuadrantSurface(ON_NurbsSurface* quadrant_surface, unsigned int quadrant_index) { if (nullptr == quadrant_surface) return false; ON_SubDLimitSurfaceFragment* q = Quadrant(quadrant_index, true); if (nullptr == q) return false; return q->SetSurface(quadrant_surface); } static bool Internal_EqualKnots( double knot_tol, int dir, const ON_NurbsSurface* lhs, const ON_NurbsSurface* rhs ) { // all orders are 4 const int knot_count = lhs->KnotCount(dir); if (knot_count != rhs->KnotCount(dir)) return false; const double* lhs_knot = lhs->m_knot[dir]; const double* rhs_knot = rhs->m_knot[dir]; for (int i = 0; i < knot_count; i++) { if ( !(fabs(lhs_knot[i] - rhs_knot[i]) <= knot_tol) ) return false; } return true; } static bool Internal_OverlapingKnots( double knot_tol, int dir, const ON_NurbsSurface* lhs, const ON_NurbsSurface* rhs ) { // all orders are 4 const double* lhs_knot = lhs->m_knot[dir]; const double* rhs_knot = rhs->m_knot[dir]; if (!(rhs_knot[0] < rhs_knot[1] && rhs_knot[1] < rhs_knot[2] && rhs_knot[2] < rhs_knot[3])) return false; const unsigned int lhs_knot_count = lhs->KnotCount(dir); lhs_knot += (lhs_knot_count - 5); for (unsigned int i = 0; i < 5; i++) { if (!(fabs(lhs_knot[i] - rhs_knot[i]) <= knot_tol)) return false; } return true; } static ON_NurbsSurface* Internal_MergeC2Neighbors( ON_SubD::NurbsSurfaceType nurbs_surface_type, int dir, ON_NurbsSurface* lhs, ON_NurbsSurface* rhs ) { // Context: // lhs and rhs are cubic non-rational NURBS and are known to meed C2 at the shared edge. // // dir = 0: join East side of lhs to West side of rhs // dir = 1: join North side of lhs to South side of rhs // Remaining comments are for dir = 0: if ( dir < 0 || dir > 1 || nullptr == lhs || nullptr == rhs || 4 != lhs->m_order[0] || 4 != lhs->m_order[1] || 4 != rhs->m_order[0] || 4 != rhs->m_order[1] || 0 != lhs->m_is_rat || 0 != rhs->m_is_rat || 3 != lhs->m_dim || 3 != rhs->m_dim ) { ON_SUBD_ERROR("Invalid input."); return nullptr; } const int dir1 = 1 - dir; if (!(lhs->Domain(dir).m_t[1] == rhs->Domain(dir)[0])) { ON_SUBD_ERROR("Invalid dir or input domains."); return nullptr; } if (!(lhs->Domain(dir1) == rhs->Domain(dir1))) { ON_SUBD_ERROR("Invalid dir or input domains."); return nullptr; } const double knot_tol = 1e-8; // merged->m_knots[dir][...] begins with lhs->m_knot[dir][...] and ends with rhs->m_knot[dir][...] const bool bOverlapMerge = Internal_OverlapingKnots(knot_tol, dir, lhs, rhs); if (false == bOverlapMerge) { if (ON_SubD::NurbsSurfaceType::Large != nurbs_surface_type) return nullptr; // not permitted to modify knots. lhs->ClampEnd(dir, 2); rhs->ClampEnd(dir, 2); } // We need lhs->m_knot[dir1][...] = rhs->m_knot[dir1][...] // and merged->m_knots[dir1][...] = lhs->m_knot[dir1][...] if (false == Internal_EqualKnots(knot_tol, dir1, lhs, rhs)) { if (ON_SubD::NurbsSurfaceType::Large != nurbs_surface_type) return nullptr; // not permitted to modify knots. lhs->ClampEnd(dir1, 2); rhs->ClampEnd(dir1, 2); if (false == Internal_EqualKnots(knot_tol, dir1, lhs, rhs)) { // Insert knots to make lhs->m_knot[dir1][...] = rhs->m_knot[dir1][...] equal double lhs_k = lhs->m_knot[dir1][2]; double rhs_k = rhs->m_knot[dir1][2]; int lhs_i = 3; int rhs_i= 3; while (lhs_i < lhs->m_cv_count[dir1] && rhs_i < rhs->m_cv_count[dir1]) { double lhs_k0 = lhs_k; double rhs_k0 = rhs_k; lhs_k = lhs->m_knot[dir1][lhs_i]; rhs_k = rhs->m_knot[dir1][rhs_i]; if (!(lhs_k0+knot_tol < lhs_k)) { ON_SUBD_ERROR("Invalid lhs knots or a bug."); return nullptr; } if (!(rhs_k0+knot_tol < rhs_k)) { ON_SUBD_ERROR("Invalid rhs knots or a bug."); return nullptr; } if (lhs_k + knot_tol < rhs_k) { // insert knot in rhs at lhs_k if (false == rhs->InsertKnot(dir1, lhs_k, 1)) { ON_SUBD_ERROR("rhs knot insertion failed."); return nullptr; } rhs_k = rhs->m_knot[dir1][rhs_i]; } if (rhs_k + knot_tol < lhs_k) { // insert knot in lhs at rhs_k if ( false == lhs->InsertKnot(dir1, rhs_k, 1) ) { ON_SUBD_ERROR("lhs knot insertion failed."); return nullptr; } lhs_k = lhs->m_knot[dir1][lhs_i]; } if (!(fabs(lhs_k - rhs_k) <= knot_tol)) { ON_SUBD_ERROR("Unexpected knot insertion failure."); return nullptr; } lhs_i++; rhs_i++; } if (false == Internal_EqualKnots(knot_tol, dir1, lhs, rhs)) { ON_SUBD_ERROR("Unexpected different knot vectors.8"); return nullptr; } } } //// DEBUGGING //if (false == lhs->IsValid()) //{ // ON_SUBD_ERROR("lhs is not valid."); // return nullptr; //} // // DEBUGGING //if (false == rhs->IsValid()) //{ // ON_SUBD_ERROR("rhs is not valid."); // return nullptr; //} // Fill in merged surface const int lhs_cv_count0 = lhs->m_cv_count[0]; const int lhs_cv_count1 = lhs->m_cv_count[1]; const int rhs_cv_count0 = rhs->m_cv_count[0]; const int rhs_cv_count1 = rhs->m_cv_count[1]; int cv_count0 = lhs_cv_count0; int cv_count1 = lhs_cv_count1; int rhs_cv_dex0 = 0; int rhs_cv_dex1 = 0; if (0 == dir) { if (lhs_cv_count1 != rhs_cv_count1) { ON_SUBD_ERROR("Bug in dir=0 merging."); return nullptr; } rhs_cv_dex0 = (bOverlapMerge) ? 3 : 1; cv_count0 += (rhs_cv_count0 - rhs_cv_dex0); } else { if (lhs_cv_count0 != rhs_cv_count0) { ON_SUBD_ERROR("Bug in dir=1 merging."); return nullptr; } rhs_cv_dex1 = (bOverlapMerge) ? 3 : 1; cv_count1 += (rhs_cv_count1 - rhs_cv_dex1); } ON_NurbsSurface* merged_srf = new ON_NurbsSurface(3, 0, 4, 4, cv_count0, cv_count1); const double* src; double* dst; for (int i = 0; i < lhs_cv_count0; i++) { for (int j = 0; j < lhs_cv_count1; j++) { src = lhs->CV(i, j); dst = merged_srf->CV(i, j); dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; } } if (0 == dir) { int dst_i = lhs_cv_count0; for (int i = rhs_cv_dex0; i < rhs_cv_count0; i++, dst_i++) { for (int j = 0; j < rhs_cv_count1; j++) { src = rhs->CV(i, j); dst = merged_srf->CV(dst_i, j); dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; } } } else { int dst_j = lhs_cv_count1; for (int j = rhs_cv_dex1; j < rhs_cv_count1; j++, dst_j++) { for (int i = 0; i < rhs_cv_count0; i++) { src = rhs->CV(i, j); dst = merged_srf->CV(i, dst_j); dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; } } } const int lhs_knot_count0 = lhs->KnotCount(0); const int lhs_knot_count1 = lhs->KnotCount(1); for (int i = 0; i < lhs_knot_count0; i++) merged_srf->m_knot[0][i] = lhs->m_knot[0][i]; for (int i = 0; i < lhs_knot_count1; i++) merged_srf->m_knot[1][i] = lhs->m_knot[1][i]; if (0 == dir) { const int rhs_knot_count0 = rhs->KnotCount(0); dst = &merged_srf->m_knot[0][lhs_knot_count0]; for (int i = 2 + rhs_cv_dex0; i < rhs_knot_count0; i++) *dst++ = rhs->m_knot[0][i]; } else { const int rhs_knot_count1 = rhs->KnotCount(1); dst = &merged_srf->m_knot[1][lhs_knot_count1]; for (int i = 2 + rhs_cv_dex1; i < rhs_knot_count1; i++) *dst++ = rhs->m_knot[1][i]; } //// DEBUGGING //if (false == merged_srf->IsValid()) //{ // ON_SUBD_ERROR("merged_srf is not valid."); // delete merged_srf; // merged_srf = nullptr; //} return merged_srf; } bool ON_SubDLimitSurfaceFragment::SetSurfaceFromQuadrants( ON_SubD::NurbsSurfaceType nurbs_surface_type ) { if (nullptr != m_surface) return true; ON_NurbsSurface* s[4] = { 0 }; for (unsigned int quadrant_index = 0; quadrant_index < 4; quadrant_index++) { if (nullptr == m_quadrants[quadrant_index]) return false; if (nullptr == m_quadrants[quadrant_index]->m_surface) return false; s[quadrant_index] = m_quadrants[quadrant_index]->m_surface; if (ON_SubD::NurbsSurfaceType::Large != nurbs_surface_type) { // not permitted to add knots if ( s[0]->m_cv_count[0] != s[quadrant_index]->m_cv_count[0] || s[0]->m_cv_count[1] != s[quadrant_index]->m_cv_count[1] ) { return false; } } } s[0]->SetDomain(0, 0.0, 0.5); s[1]->SetDomain(0, 0.5, 1.0); s[2]->SetDomain(0, 0.5, 1.0); s[3]->SetDomain(0, 0.0, 0.5); s[0]->SetDomain(1, 0.0, 0.5); s[1]->SetDomain(1, 0.0, 0.5); s[2]->SetDomain(1, 0.5, 1.0); s[3]->SetDomain(1, 0.5, 1.0); ON_NurbsSurface* bottom = Internal_MergeC2Neighbors(nurbs_surface_type, 0, s[0], s[1]); if (nullptr == bottom) return false; ON_NurbsSurface* top = Internal_MergeC2Neighbors(nurbs_surface_type, 0, s[3], s[2]); if (nullptr == top) { delete bottom; return false; } m_surface = Internal_MergeC2Neighbors(nurbs_surface_type, 1, bottom, top); delete bottom; delete top; if (nullptr == m_surface) return false; for (unsigned int quadrant_index = 0; quadrant_index < 4; quadrant_index++) { delete m_quadrants[quadrant_index]->m_surface; m_quadrants[quadrant_index]->m_surface = nullptr; m_quadrants[quadrant_index]->m_parent = nullptr; ON_SubDLimitSurfaceFragment::ReturnSurfaceFragment(m_quadrants[quadrant_index]); m_quadrants[quadrant_index] = nullptr; } return true; } const ON_SubDLimitSurfaceFragment ON_SubDLimitSurfaceFragment::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDLimitSurfaceFragment); class Internal_SubDNurbsPatchGetter { public: Internal_SubDNurbsPatchGetter( const ON_SubD& subd, unsigned int patch_density, ON_SubD::NurbsSurfaceType nurbs_surface_type, ON__UINT_PTR nurbs_callback_context, bool(*nurbs_callback_function)(ON__UINT_PTR, const ON_SubDFaceRegion&, class ON_NurbsSurface*) ) : m_subd(subd) , m_patch_density(patch_density) , m_nurbs_surface_type(ON_SubD::NurbsSurfaceType::Unset == nurbs_surface_type ? ON_SubD::NurbsSurfaceType::Medium : nurbs_surface_type) , m_nurbs_callback_context(nurbs_callback_context) , m_nurbs_callback_function(nurbs_callback_function) {} const ON_SubD& m_subd; const unsigned int m_patch_density = 2; const ON_SubD::NurbsSurfaceType m_nurbs_surface_type = ON_SubD::NurbsSurfaceType::Unset; // m_current_face_region identifies the current region being accumulated in m_fragment_tree // and is set in Internal_SubDNurbsPatchGetter::BeginFaceCallback(). // If the level 0 face is a quad, then m_fragments_face_region.m_subdivision_count = 0; // If the level 0 face is an N-gon (N != 4), then m_fragments_face_region.m_subdivision_count = 1. ON_SubDFaceRegion m_current_face_region; enum : unsigned int { fragments_acculator_capacity = 5 }; ON_SubDLimitSurfaceFragment* m_fragment_tree = nullptr; ON_SubDLimitSurfaceFragment* FragmentLeaf( const ON_SubDFaceRegion& face_region ); void AddOutputSurface( const ON_SubDFaceRegion& face_region, ON_NurbsSurface* output_surface ); ON__UINT_PTR m_nurbs_callback_context = 0; bool(*m_nurbs_callback_function)(ON__UINT_PTR, const ON_SubDFaceRegion&, class ON_NurbsSurface*) = nullptr; unsigned int m_bicubic_span_count = 0; void AddPatch( const ON_SubDLimitNurbsFragment* patch_fragment ); void ConvertFragmentsToSurfaces(); void ConvertPatchToSurfaces( const ON_SubDLimitNurbsFragment& patch_fragment ); static bool BeginFaceCallback( ON__UINT_PTR context, // contest = Internal_SubDNurbsPatchGetter* const ON_SubDFaceRegion& face_region //, //const class ON_SubDFace* level0_face_OBSOLETE, //const class ON_SubDFace* level1_face_OBSOLETE, //unsigned int level1_face_region_index_OBSOLETE ); static bool GetLimitSurfaceInPatchesCallback( ON__UINT_PTR context, // contest = Internal_SubDNurbsPatchGetter* const ON_SubDLimitNurbsFragment* patch_fragment ); static bool AddToSurfaceArrayCallback1( ON__UINT_PTR context, // ON_SimpleArray* const ON_SubDFaceRegion& face_region, class ON_NurbsSurface* nurbs_surface ); static bool AddToSurfaceArrayCallback2( ON__UINT_PTR context, // ON_SimpleArray* const ON_SubDFaceRegion& face_region, class ON_NurbsSurface* nurbs_surface ); private: unsigned int m_new_subdivision_edge_id = 0; wchar_t* AppendUnsigned( wchar_t prefix, unsigned int i, wchar_t* s, wchar_t* send ); private: Internal_SubDNurbsPatchGetter() = delete; Internal_SubDNurbsPatchGetter(const Internal_SubDNurbsPatchGetter&) = delete; Internal_SubDNurbsPatchGetter& operator=(const Internal_SubDNurbsPatchGetter&) = delete; }; bool Internal_SubDNurbsPatchGetter::BeginFaceCallback( ON__UINT_PTR context, // contest = Internal_SubDNurbsPatchGetter* const ON_SubDFaceRegion& face_region ) { Internal_SubDNurbsPatchGetter* p = (Internal_SubDNurbsPatchGetter*)context; if (nullptr == p) return true; // Flush accumulated fragments from previous face p->ConvertFragmentsToSurfaces(); p->m_current_face_region = face_region; return true; } bool Internal_SubDNurbsPatchGetter::GetLimitSurfaceInPatchesCallback( ON__UINT_PTR context, const ON_SubDLimitNurbsFragment* patch_fragment ) { ((Internal_SubDNurbsPatchGetter*)context)->AddPatch(patch_fragment); return true; } wchar_t* Internal_SubDNurbsPatchGetter::AppendUnsigned( wchar_t prefix, unsigned int i, wchar_t* s, wchar_t* send ) { if ( 0 != prefix && s < send) *s++ = prefix; wchar_t buffer[64]; wchar_t* sdigit = buffer; wchar_t* sdigit1 = sdigit + (sizeof(buffer)/sizeof(buffer[0])); for ( *sdigit++ = 0; sdigit < sdigit1; sdigit++ ) { *sdigit = (wchar_t)('0' + (i%10)); i /= 10; if (0 == i) { while ( s < send && 0 != (*s = *sdigit--) ) s++; return s; } } return s; } ON_SubDLimitSurfaceFragment* ON_SubDLimitSurfaceFragment::Parent() { return m_parent; } ON_SubDLimitSurfaceFragment* ON_SubDLimitSurfaceFragment::Quadrant(unsigned int quadrant_index, bool bAllocateIfMissing) { if (quadrant_index >= 4) { ON_SUBD_ERROR("Invalid quadrant_index value."); return nullptr; } if (nullptr == m_quadrants[quadrant_index] && bAllocateIfMissing) { m_quadrants[quadrant_index] = ON_SubDLimitSurfaceFragment::AllocateSurfaceFragment(); if (nullptr != m_quadrants[quadrant_index]) { m_quadrants[quadrant_index]->m_parent = this; m_quadrants[quadrant_index]->m_face_region = m_face_region; m_quadrants[quadrant_index]->m_face_region.Push(quadrant_index); } } return m_quadrants[quadrant_index]; } ON_SubDLimitSurfaceFragment* Internal_SubDNurbsPatchGetter::FragmentLeaf( const ON_SubDFaceRegion& patch_face_region ) { const ON_SubDComponentRegion& fragment_fr = m_current_face_region.m_face_region; if (fragment_fr.m_level0_component_id == 0) { ON_SUBD_ERROR("m_fragments_face_region.m_level0_component_id not set."); return nullptr; } const ON_SubDComponentRegion& patch_fr = patch_face_region.m_face_region; if (fragment_fr.m_level0_component_id != patch_fr.m_level0_component_id) { ON_SUBD_ERROR("m_fragments_face_region.m_level0_component_id != patch_face_region.m_level0_component_id"); return nullptr; } if (patch_fr.SubdivisionCount() < fragment_fr.SubdivisionCount()) { ON_SUBD_ERROR("patch_face_region.SubdivisionCount() < m_fragments_face_region.SubdivisionCount()"); return nullptr; } if (patch_fr.SubdivisionCount() > ON_SubDComponentRegionIndex::IndexCapacity) { // unreasonable number of subdivisions ON_SUBD_ERROR("patch_face_region.SubdivisionCount() > ON_SubDComponentRegionIndex::IndexCapacity"); return nullptr; } for ( unsigned short i = 0; i < fragment_fr.SubdivisionCount(); i++ ) { if (fragment_fr.m_region_index.m_index[i] != patch_fr.m_region_index.m_index[i]) { ON_SUBD_ERROR("m_fragments_face_region.m_region_index[] differs from patch_face_region"); return nullptr; } } if (nullptr == m_fragment_tree) { m_fragment_tree = ON_SubDLimitSurfaceFragment::AllocateSurfaceFragment(); m_fragment_tree->m_face_region = m_current_face_region; } ON_SubDLimitSurfaceFragment* fragment_leaf = m_fragment_tree; ON_SubDComponentRegion r = fragment_fr; while (r.SubdivisionCount() < patch_fr.SubdivisionCount()) { unsigned short quadrant_dex = patch_fr.m_region_index.Index(r.SubdivisionCount()); if (quadrant_dex >= 4) { ON_SUBD_ERROR("patch_face_region.m_region_index[] value >= 4."); return nullptr; } r.PushAbsolute(quadrant_dex); // increments r.m_subdivision_count fragment_leaf = fragment_leaf->Quadrant(quadrant_dex, true); if (nullptr == fragment_leaf) { ON_SUBD_ERROR("fragment tree allocation error."); return nullptr; } if (0 != ON_SubDComponentRegion::CompareTypeIdMarkSubregion(&r, &fragment_leaf->m_face_region.m_face_region)) { ON_SUBD_ERROR("corrupt fragment tree."); return nullptr; } } return fragment_leaf; } void Internal_SubDNurbsPatchGetter::AddPatch( const ON_SubDLimitNurbsFragment* patch_fragment ) { if (nullptr == patch_fragment) return; // face_region identifies what part of the SubD region is represented by patch_fragment const ON_SubDFaceRegion face_region = patch_fragment->m_face_region; ON_SubDLimitNurbsFragment local_patch_fragment(*patch_fragment); patch_fragment = &local_patch_fragment; bool rc = false; for (;;) { if (m_current_face_region.m_face_region.m_level0_component_id == 0) { ON_SUBD_ERROR("m_fragments_face_region.m_level0_component_id == 0"); break; } if (patch_fragment->m_face_region.m_face_region.m_level0_component_id == 0) { ON_SUBD_ERROR("patch_fragment->m_face_region.m_level0_component_id == 0"); break; } const unsigned int max_bispan_count = patch_fragment->MaximumBispanCount(); const unsigned int bispan_count = patch_fragment->SetBispanCount(); if (0 == bispan_count || 0 == max_bispan_count) { ON_SUBD_ERROR("No bispans in patch_fragment."); break; } if ( ON_SubD::NurbsSurfaceType::Small == m_nurbs_surface_type || ON_SubD::NurbsSurfaceType::Unprocessed == m_nurbs_surface_type ) { // happens when debugging rc = true; break; } ON_SubDLimitSurfaceFragment* fragment_leaf = FragmentLeaf(patch_fragment->m_face_region); if (nullptr == fragment_leaf) { ON_SUBD_ERROR("Unable to get surface holder for patch_fragment->m_face_region."); break; } if (nullptr != fragment_leaf->m_surface) { ON_SUBD_ERROR("fragment_leaf->m_surface is already set."); break; } // patch_fragment is part of the face we are currently patching if (bispan_count == max_bispan_count) { if (false == fragment_leaf->SetSurface(patch_fragment->GetSurface(nullptr))) { ON_SUBD_ERROR("Failed to set surface."); break; } local_patch_fragment = ON_SubDLimitNurbsFragment::Empty; } else { for (unsigned int quadrant_index = 0; quadrant_index < max_bispan_count; quadrant_index++) { if (ON_SubDLimitNurbsFragment::BispanType::None == patch_fragment->m_bispan_type[quadrant_index]) continue; if (false == fragment_leaf->SetQuadrantSurface(patch_fragment->GetQuadrantSurface(quadrant_index, nullptr), quadrant_index)) { ON_SUBD_ERROR("Failed to set quadrant surface."); continue; } local_patch_fragment.m_bispan_type[quadrant_index] = ON_SubDLimitNurbsFragment::BispanType::None; } } if (0 == local_patch_fragment.SetBispanCount()) rc = true; while (nullptr != fragment_leaf && fragment_leaf->SetSurfaceFromQuadrants(m_nurbs_surface_type)) { fragment_leaf = fragment_leaf->Parent(); } break; } if (false == rc) ConvertFragmentsToSurfaces(); // Convert this patch. Patches should all set to none if we are merging patches.) // Restore face and edge region information local_patch_fragment.m_face_region = face_region; ConvertPatchToSurfaces(*patch_fragment); return; } void Internal_SubDNurbsPatchGetter::ConvertFragmentsToSurfaces() { if (nullptr == m_fragment_tree) return; ON_SimpleArray a(64); a.Append(m_fragment_tree); m_fragment_tree = nullptr; unsigned int a_count = a.UnsignedCount(); while ( a_count > 0 ) { for (unsigned int i = 0; i < a_count; i++) { ON_SubDLimitSurfaceFragment* f = a[i]; if (nullptr == f) continue; a[i] = 0; if (f->m_surface) { AddOutputSurface(f->m_face_region, f->m_surface); f->m_surface = nullptr; // receiver must manage f->m_surface. } for (unsigned int j = 0; j < 4; j++) { ON_SubDLimitSurfaceFragment* q = f->Quadrant(j, false); if (q) a.Append(q); } ON_SubDLimitSurfaceFragment::ReturnSurfaceFragment(f); } unsigned int k = a_count; const unsigned int kmax = a.UnsignedCount(); a_count = 0; while (k < kmax) a[a_count++] = a[k++]; a.SetCount(a_count); } m_current_face_region = ON_SubDFaceRegion::Empty; } void Internal_SubDNurbsPatchGetter::AddOutputSurface( const ON_SubDFaceRegion& face_region, ON_NurbsSurface* output_surface ) { if (nullptr == output_surface) return; if ( ON_SubD::NurbsSurfaceType::Unprocessed != m_nurbs_surface_type ) { output_surface->ClampEnd(0, 2); output_surface->ClampEnd(1, 2); Internal_CheckNurbsSurfaceCVs(*output_surface); } m_nurbs_callback_function(m_nurbs_callback_context, face_region, output_surface); } void Internal_SubDNurbsPatchGetter::ConvertPatchToSurfaces( const ON_SubDLimitNurbsFragment& patch_fragment ) { // Exports patches as is with no merging. unsigned int bispan_count = patch_fragment.SetBispanCount(); if (bispan_count <= 0) return; unsigned int maximum_bispan_count = patch_fragment.MaximumBispanCount(); ON_SubDComponentRegion central_edge_regions[4] = {}; ON_SubDFaceRegion local_face_region = patch_fragment.m_face_region; for (unsigned int quadrant_index = 0; quadrant_index < maximum_bispan_count; quadrant_index++) { if (ON_SubDLimitNurbsFragment::BispanType::None == patch_fragment.m_bispan_type[quadrant_index]) continue; ON_NurbsSurface* output_surface = patch_fragment.GetQuadrantSurface(quadrant_index, nullptr); if (nullptr == output_surface) continue; if (maximum_bispan_count > 1) { // region information must be subdivided local_face_region.Push(quadrant_index); const unsigned short central_edge_region_subdivision_count = local_face_region.m_face_region.SubdivisionCount(); if (0 == central_edge_regions[quadrant_index].m_level0_component_id) central_edge_regions[quadrant_index] = Internal_CreateSubdivisionEdgeRegion(central_edge_region_subdivision_count,false); local_face_region.m_edge_region[(quadrant_index + 1) % 4] = central_edge_regions[quadrant_index]; local_face_region.m_edge_region[(quadrant_index + 1) % 4].m_level0_component = central_edge_regions[quadrant_index].m_level0_component.ToggleMark(); const unsigned int cdex3 = (quadrant_index + 3) % 4; if (0 == central_edge_regions[cdex3].m_level0_component_id) central_edge_regions[cdex3] = Internal_CreateSubdivisionEdgeRegion(central_edge_region_subdivision_count,false); local_face_region.m_edge_region[(quadrant_index+2)%4] = central_edge_regions[cdex3]; } AddOutputSurface(local_face_region, output_surface); if (maximum_bispan_count > 1) local_face_region = patch_fragment.m_face_region; } } unsigned int ON_SubD::GetLimitSurfaceNurbs( const class ON_SubDDisplayParameters& display_parameters, ON_SubD::NurbsSurfaceType nurbs_surface_type, ON__UINT_PTR callback_context, bool(*nurbs_callback_function)(ON__UINT_PTR, const ON_SubDFaceRegion&, class ON_NurbsSurface*) ) const { // TODO: Restructure the code to support this callback function. Internal_SubDNurbsPatchGetter patch_getter( *this, display_parameters.m_display_density, nurbs_surface_type, callback_context, nurbs_callback_function ); GetLimitSurfaceNurbsFragments( display_parameters, (ON__UINT_PTR)&patch_getter, Internal_SubDNurbsPatchGetter::BeginFaceCallback, Internal_SubDNurbsPatchGetter::GetLimitSurfaceInPatchesCallback ); // Flush the final batch of patches. patch_getter.ConvertFragmentsToSurfaces(); return patch_getter.m_bicubic_span_count; } bool Internal_SubDNurbsPatchGetter::AddToSurfaceArrayCallback1( ON__UINT_PTR context, // ON_SimpleArray* const ON_SubDFaceRegion& face_region, class ON_NurbsSurface* nurbs_surface ) { if (nullptr != nurbs_surface && 0 != context) { ON_wString region_id = face_region.ToString(); nurbs_surface->SetUserString(L"SubDRegionId", region_id); ((ON_SimpleArray< ON_NurbsSurface* >*)context)->Append(nurbs_surface); } return true; } bool Internal_SubDNurbsPatchGetter::AddToSurfaceArrayCallback2( ON__UINT_PTR context, // ON_SimpleArray* const ON_SubDFaceRegion& face_region, class ON_NurbsSurface* nurbs_surface ) { if (nullptr != nurbs_surface && 0 != context) { ON_SubDFaceRegionAndNurbs& e = ((ON_SimpleArray< ON_SubDFaceRegionAndNurbs >*)context)->AppendNew(); e.m_face_region = face_region; e.m_nurbs_surface = nurbs_surface; } return true; } unsigned int ON_SubD::GetLimitSurfaceNurbs( const class ON_SubDDisplayParameters& display_parameters, ON_SubD::NurbsSurfaceType nurbs_surface_type, ON_SimpleArray< ON_NurbsSurface* >& patches ) const { return GetLimitSurfaceNurbs(display_parameters, nurbs_surface_type, (ON__UINT_PTR)&patches, Internal_SubDNurbsPatchGetter::AddToSurfaceArrayCallback1); } unsigned int ON_SubD::GetLimitSurfaceNurbs( const class ON_SubDDisplayParameters& display_parameters, ON_SubD::NurbsSurfaceType nurbs_surface_type, ON_SimpleArray< ON_SubDFaceRegionAndNurbs >& patches ) const { return GetLimitSurfaceNurbs(display_parameters, nurbs_surface_type, (ON__UINT_PTR)&patches, Internal_SubDNurbsPatchGetter::AddToSurfaceArrayCallback2); } static bool Internal_ToBrep( ON_SimpleArray< ON_SubDFaceRegionAndNurbs >& patches, ON_Brep& brep ); ON_Brep* ON_SubD::GetLimitSurfaceNurbs( const class ON_SubDDisplayParameters& display_parameters, ON_Brep* destination_brep ) const { if (nullptr != destination_brep) destination_brep->Destroy(); ON_SimpleArray< ON_SubDFaceRegionAndNurbs > patches; GetLimitSurfaceNurbs(display_parameters, ON_SubD::NurbsSurfaceType::Large, (ON__UINT_PTR)&patches, Internal_SubDNurbsPatchGetter::AddToSurfaceArrayCallback2); const unsigned int face_count = patches.UnsignedCount(); if (face_count <= 0) return nullptr; ON_Brep* brep = (nullptr != destination_brep) ? destination_brep : ON_Brep::New(); if (false == Internal_ToBrep(patches, *brep)) { if (brep != destination_brep) delete brep; brep = nullptr; } return brep; } static bool Internal_ToBrep( ON_SimpleArray< ON_SubDFaceRegionAndNurbs >& patches, ON_Brep& brep ) { const unsigned int face_count = patches.UnsignedCount(); if (face_count <= 0) return false; const unsigned int split_edge_mark = 0xFFFFFFFF; const unsigned int bad_edge_mark = 0xFFFFFFFE; const unsigned int bad_vertex_mark = 0xFFFFFFFD; ON_SimpleArray vertex_map(3 * face_count + 32); ON_SimpleArray edge_map(4 * face_count + 32); ON_2udex vm(0, 0); ON_3udex em(0, 0, 0); // Begin working on building vertex_map[] and edge_map[] for (unsigned int fi = 0; fi < face_count; fi++) { const ON_SubDFaceRegionAndNurbs& r = patches[fi]; ON_NurbsSurface* nurbs_surface = r.m_nurbs_surface; if (nullptr == nurbs_surface) continue; for (int i = 0; i < 4; i++) { vm.i = r.m_face_region.m_vertex_id[i]; vm.j = fi; if ( 0 != vm.i) vertex_map.Append(vm); em.i = r.m_face_region.m_edge_region[i].m_level0_component_id; if (0 != em.i) { em.j = r.m_face_region.m_edge_region[i].m_region_index.ToCompressedRegionIndex(); em.k = fi; edge_map.Append(em); } } } // Remove duplicates from vertex_map[] vertex_map.QuickSort(ON_2udex::DictionaryCompare); unsigned int count0 = vertex_map.UnsignedCount(); unsigned int prev_id = 0; unsigned int brep_vertex_count = 0; for (unsigned int i = 0; i < count0; i++) { vm = vertex_map[i]; if (vm.i <= prev_id) continue; prev_id = vm.i; vertex_map[brep_vertex_count++] = vm; } vertex_map.SetCount(brep_vertex_count); // Create brep vertices and set vertex_map[].j to brep index values so we // can use vertex_map[] to go from a subd vertex id to a brep vertex index. brep.m_V.Reserve(brep_vertex_count); for (unsigned int i = 0; i < brep_vertex_count; i++) { ON_3dPoint P(ON_3dPoint::NanPoint); vm = vertex_map[i]; const unsigned fi = vm.j; vertex_map[i].j = bad_vertex_mark; // will be updated below if everything works right vm.j = bad_vertex_mark; const ON_SubDFaceRegionAndNurbs& r = patches[fi]; if (nullptr != r.m_nurbs_surface) { for (int j = 0; j < 4; j++) { if (vm.i == r.m_face_region.m_vertex_id[j]) { const double s = r.m_nurbs_surface->Domain(0)[(1==j||2==j) ? 1 : 0]; const double t = r.m_nurbs_surface->Domain(1)[(j > 1) ? 1 : 0]; P = r.m_nurbs_surface->PointAt(s, t); break; } } } if (false == P.IsValid()) { ON_SUBD_ERROR("Unable to calculate vertex location."); } vm.j = brep.m_V.UnsignedCount(); vertex_map[i].j = vm.j; brep.NewVertex(P, 0.0); } // Remove duplicates from edge_map[] edge_map.QuickSort(ON_3udex::DictionaryCompare); count0 = edge_map.UnsignedCount(); unsigned int edge_map_count = 0; ON_3udex prev_em(0, 0, 0); for (unsigned int i = 0; i < count0; i++) { em = edge_map[i]; if (prev_em.i == em.i && prev_em.j == em.j) continue; prev_em = em; edge_map[edge_map_count++] = em; } // NOTE: final brep edge count <= edge_map_count. edge_map.SetCount(edge_map_count); // create the edges brep.m_C3.Reserve(edge_map_count); brep.m_E.Reserve(edge_map_count); unsigned int brep_edge_count = 0; for (unsigned int i = 0; i < edge_map_count; i++) { em = edge_map[i]; const unsigned int fi = em.k; em.k = bad_edge_mark; edge_map[i].k = bad_edge_mark; // will be update below if we find all the parts. if (0 == em.j && i + 2 < edge_map_count && em.i == edge_map[i + 1].i && em.i == edge_map[i + 2].i) { // This edge is shared between a subd quad face and N-gon with N != 4. // The N-gon had to be subdivided one time to create quads and the // edge will be split on the quad face side. edge_map[i].k = split_edge_mark; // marker on the quad face edge for this situation continue; } ON_Curve* edge_curve = nullptr; const ON_SubDFaceRegionAndNurbs& r = patches[fi]; const ON_NurbsSurface* nurbs_surface = r.m_nurbs_surface; unsigned vertex_id[2] = {}; int brep_vi[2] = { -1,-1 }; if (nullptr != nurbs_surface) { for (int j = 0; j < 4; j++) { if (em.i == r.m_face_region.m_edge_region[j].m_level0_component_id) { const bool bRevEdge = (0 != r.m_face_region.m_edge_region[j].m_level0_component.ComponentMark()); vertex_id[bRevEdge?1:0] = r.m_face_region.m_vertex_id[j]; vertex_id[bRevEdge?0:1] = r.m_face_region.m_vertex_id[(j+1)%4]; for (int k = 0; k < 2; k++) { vm.i = vertex_id[k]; vm.j = 0; int mapdex = vertex_map.BinarySearch(&vm, ON_2udex::CompareFirstIndex); if (mapdex >= 0) { vm = vertex_map[mapdex]; brep_vi[k] = (int)vm.j; } } const int iso_dir = j % 2; const double c = nurbs_surface->Domain(1 - iso_dir)[(1==j || 2==j) ? 1 : 0]; edge_curve = nurbs_surface->IsoCurve(iso_dir, c); bool bReverseCurve = (j >= 2); if (bRevEdge) bReverseCurve = !bReverseCurve; if (bReverseCurve && nullptr != edge_curve) edge_curve->Reverse(); break; } } } if (nullptr != edge_curve) { for (int k = 0; k < 2; k++) { if (brep_vi[k] >= 0) continue; ON_SUBD_ERROR("brep edge vertex creation error."); ON_3dPoint P(ON_3dPoint::NanPoint); if (nullptr != edge_curve) P = (0 == k) ? edge_curve->PointAtStart() : edge_curve->PointAtEnd(); brep_vi[k] = brep.m_V.Count(); brep.NewVertex(P, 0.0); } int c3i = -1; if (nullptr != edge_curve) { c3i = brep.m_C3.Count(); brep.m_C3.Append(edge_curve); } em.k = brep.m_E.UnsignedCount(); edge_map[i].k = em.k; brep.NewEdge(brep.m_V[brep_vi[0]], brep.m_V[brep_vi[1]], c3i, nullptr, 0.0); brep_edge_count++; } else { ON_SUBD_ERROR("brep edge creation error."); } } // Now build faces ON_NurbsCurve trim_curve; ON_2dPoint trim_curve_cvs[2]; double trim_curve_knots[2] = { 0.0,1.0 }; trim_curve.m_dim = 2; trim_curve.m_is_rat = 0; trim_curve.m_order = 2; trim_curve.m_cv_count = 2; trim_curve.m_cv_stride = 2; trim_curve.m_cv = &trim_curve_cvs[0].x; trim_curve.m_knot = trim_curve_knots; for (unsigned int fi = 0; fi < face_count; fi++) { const ON_SubDFaceRegionAndNurbs& r = patches[fi]; if (nullptr == r.m_nurbs_surface) continue; const ON_Interval srf_domain[2] = { r.m_nurbs_surface->Domain(0),r.m_nurbs_surface->Domain(1) }; const ON_2dPoint srf_uv[4] = { ON_2dPoint(srf_domain[0][0], srf_domain[1][0]), ON_2dPoint(srf_domain[0][1], srf_domain[1][0]), ON_2dPoint(srf_domain[0][1], srf_domain[1][1]), ON_2dPoint(srf_domain[0][0], srf_domain[1][1]) }; ON_3udex brep_em[8] = {}; bool trimRev3d[8] = {}; ON_2udex brep_vm[8] = {}; // Fallback if we fail to get complete information bool bFail = false; int fail_vid[4] = { -1,-1,-1,-1 }; int fail_eid[4] = { -1,-1,-1,-1 }; bool fail_bRev3d[4] = { false,false,false,false }; for (int i = 0; i < 4; i++) { brep_vm[2 * i] = ON_2udex::Zero; brep_vm[2 * i + 1] = ON_2udex::Zero; brep_em[2 * i] = ON_3udex::Zero; brep_em[2 * i + 1] = ON_3udex::Zero; vm.i = r.m_face_region.m_vertex_id[i]; vm.j = 0; int k = vertex_map.BinarySearch(&vm, ON_2udex::CompareFirstIndex); if (k < 0) { bFail = true; } else { vm = vertex_map[k]; if (vm.j < bad_vertex_mark) { brep_vm[2 * i] = vm; fail_vid[i] = vm.j; } else bFail = true; } const bool bRev3d = (0 != r.m_face_region.m_edge_region[i].m_level0_component.ComponentMark()) ? true : false; trimRev3d[2 * i] = bRev3d; em.i = r.m_face_region.m_edge_region[i].m_level0_component_id; em.j = r.m_face_region.m_edge_region[i].m_region_index.ToCompressedRegionIndex(); em.k = 0; k = edge_map.BinarySearch(&em, ON_3udex::CompareFirstAndSecondIndex); if (k < 0) { bFail = true; } else { em = edge_map[k]; if (split_edge_mark == em.k) { if (0 != em.j || (unsigned)(k + 2) >= edge_map_count || em.i != edge_map[k+1].i || em.i != edge_map[k+2].i ) bFail = true; else { trimRev3d[2 * i+1] = bRev3d; if (bRev3d) { brep_em[2 * i] = edge_map[k + 2]; brep_em[2 * i + 1] = edge_map[k + 1]; } else { brep_em[2 * i] = edge_map[k + 1]; brep_em[2 * i + 1] = edge_map[k + 2]; } } } else if ( em.k < bad_edge_mark ) { brep_em[2 * i] = em; fail_eid[i] = em.k; fail_bRev3d[i] = bRev3d; } else { bFail = true; } } } if (bFail) { // Use simple face maker and end up with interior naked edges brep.NewFace( r.m_nurbs_surface, fail_vid, fail_eid, fail_bRev3d ); } else { const int si = brep.m_S.Count(); brep.m_S.Append(r.m_nurbs_surface); ON_BrepFace& brep_face = brep.NewFace(si); ON_BrepLoop& brep_loop = brep.NewLoop(ON_BrepLoop::TYPE::outer, brep_face); for (int i = 0; i < 4; i++) { trim_curve_cvs[0] = srf_uv[i]; trim_curve_cvs[1] = srf_uv[(i+1)%4]; if ( 0 == brep_em[2 * i].i || brep_em[2 * i].k >= brep_edge_count ) { ON_SUBD_ERROR("Bog in SubD to joined brep code."); continue; } ON_NurbsCurve* curve2d[2] = {}; if ( 0 != brep_em[2 * i].j && brep_em[2 * i].i == brep_em[2 * i + 1].i && 0 != brep_em[2 * i + 1].j && brep_em[2 * i + 1].k < brep_edge_count ) { // This face/edge corresponds to a subd quad face and edge that // is shared by subd N-gon with N != 4. // This side of the brep face needs 2 brep edges so // and will join to 2 brep faces coming from level 1 subdivision // quads in the original subd. const ON_2dPoint Q( (trim_curve_cvs[0].x == trim_curve_cvs[1].x) ? trim_curve_cvs[0].x : 0.5*(trim_curve_cvs[0].x + trim_curve_cvs[1].x), (trim_curve_cvs[0].y == trim_curve_cvs[1].y) ? trim_curve_cvs[0].y : 0.5*(trim_curve_cvs[0].y + trim_curve_cvs[1].y) ); trim_curve_cvs[1] = Q; curve2d[0] = trim_curve.Duplicate(); trim_curve_cvs[0] = Q; trim_curve_cvs[1] = srf_uv[(i+1)%4]; curve2d[1] = trim_curve.Duplicate(); } else { curve2d[0] = trim_curve.Duplicate(); curve2d[1] = nullptr; } for ( int k = 0; k < 2; k++) { ON_NurbsCurve* c2 = curve2d[k]; if (nullptr == c2) continue; em = brep_em[2 * i + k]; const bool bRev3d = trimRev3d[2 * i + k]; ON_BrepEdge& brep_edge = brep.m_E[em.k]; ON_Interval trim_domain = brep_edge.Domain(); if (bRev3d) trim_domain.Reverse(); c2->SetDomain(trim_domain[0],trim_domain[1]); int c2i = brep.m_C2.Count(); brep.m_C2.Append(c2); ON_BrepTrim& brep_trim = brep.NewTrim(brep_edge, bRev3d, brep_loop, c2i); brep_trim.m_tolerance[0] = 0.0; brep_trim.m_tolerance[1] = 0.0; } } //Internal_DebugLocations(brep_face); } } trim_curve.m_cv = nullptr; trim_curve.m_knot = nullptr; return true; }