diff --git a/opennurbs_brep.cpp b/opennurbs_brep.cpp index 9b97c1c6..476c0668 100644 --- a/opennurbs_brep.cpp +++ b/opennurbs_brep.cpp @@ -12857,6 +12857,12 @@ bool ON_BrepTrim::ChangeTrimCurve( int c2i ) m_pbox = c2->BoundingBox(); m_pbox.m_min.z = 0.0; m_pbox.m_max.z = 0.0; + + ON_BrepLoop* pLoop = Loop(); + if (pLoop && pLoop->m_pbox.IsValid()) + { + pLoop->m_pbox.Union(m_pbox); + } } return true; } diff --git a/opennurbs_curve.cpp b/opennurbs_curve.cpp index bdfd14df..777c2458 100644 --- a/opennurbs_curve.cpp +++ b/opennurbs_curve.cpp @@ -248,6 +248,21 @@ bool ON_Curve::GetSpanVectorIndex( return rc; } +const ON_SimpleArray ON_Curve::SpanVector() const +{ + ON_SimpleArray span_vector; + const int span_count = this->SpanCount(); + if (span_count >= 1) + { + span_vector.Reserve(span_count + 1); + if (this->GetSpanVector(span_vector.Array())) + span_vector.SetCount(span_count + 1); + else + span_vector.Destroy(); + } + return span_vector; // uses Rvalue clone - no array copied. +} + bool ON_Curve::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus double t, // t = parameter in domain diff --git a/opennurbs_curve.h b/opennurbs_curve.h index 0eb06f17..52ff4783 100644 --- a/opennurbs_curve.h +++ b/opennurbs_curve.h @@ -280,6 +280,15 @@ public: ON_Interval* span_domain // [OUT] domain of the span containing "t" ) const; + /// + /// The curve's span vector is a stricltly monotone increasing list of doubles + /// that are the intervals on which the curve is C-infinity. + /// + /// + /// The curve's span vector. + /// + const ON_SimpleArray SpanVector() const; + // Description: // Returns maximum algebraic degree of any span // or a good estimate if curve spans are not algebraic. diff --git a/opennurbs_lookup.cpp b/opennurbs_lookup.cpp index 530d8825..47ba84a3 100644 --- a/opennurbs_lookup.cpp +++ b/opennurbs_lookup.cpp @@ -60,6 +60,36 @@ static ON__UINT32 IdCRC32(const ON_UUID* id) return ON_CRC32(0, sizeof(*id), id); } +// 30 July 2024 S. Baer (RH-82891) +// Rhino uses serial numbers above UIN32_MAX for "system components" +// which results in a serial number map with very large numbers for m_maxsn. +// This caused a huge lag in performance when adding a large number of +// objects to the document because we were always having to do a large +// search for an element. The following class replaces m_maxsn and keeps +// a lower maximum value to help quickly determine if an added element +// exists with a lower value (rhino objects) exists in the list. +class ON_SerialNumberMapPrivate +{ +public: + ON__UINT64 MaxSn() const { return m_maxsn; } + ON__UINT64 LowerMaxSn() const { return m_lower_maxsn; } + void SetMaxSn(ON__UINT64 sn) + { + m_maxsn = sn; + if (sn < UINT32_MAX) + m_lower_maxsn = sn; + } + void SetLowerMaxSn(ON__UINT64 sn) + { + m_lower_maxsn = sn; + if (m_lower_maxsn > m_maxsn) + m_maxsn = m_lower_maxsn; + } + +private: + ON__UINT64 m_maxsn = 0; // largest sn stored anywhere + ON__UINT64 m_lower_maxsn = 0; +}; @@ -122,7 +152,7 @@ public: void ON_SerialNumberMap::EmptyList() { - m_maxsn = 0; + m_private->SetMaxSn(0); m_sn_count = 0; m_sn_purged = 0; m_sn_block0.EmptyBlock(); @@ -153,13 +183,16 @@ void ON_SerialNumberMap::EmptyList() } ON_SerialNumberMap::ON_SerialNumberMap() : m_sn_block0(*(new ON_SN_BLOCK())) -{} +{ + m_private = new ON_SerialNumberMapPrivate(); +} ON_SerialNumberMap::~ON_SerialNumberMap() { EmptyList(); ON_SN_BLOCK* p = &m_sn_block0; delete p; + delete m_private; } void ON_SN_BLOCK::EmptyBlock() @@ -546,9 +579,11 @@ struct ON_SerialNumberMap::SN_ELEMENT* ON_SN_BLOCK::BinarySearchBlockHelper(ON__ void ON_SerialNumberMap::UpdateMaxSNHelper() { - m_maxsn = (m_snblk_list_count > 0) ? m_snblk_list[m_snblk_list_count-1]->m_sn1 : 0; - if ( m_maxsn < m_sn_block0.m_sn1 ) - m_maxsn = m_sn_block0.m_sn1; + ON__UINT64 maxsn = (m_snblk_list_count > 0) ? m_snblk_list[m_snblk_list_count-1]->m_sn1 : 0; + if (maxsn < m_sn_block0.m_sn1 ) + maxsn = m_sn_block0.m_sn1; + + m_private->SetMaxSn(maxsn); } struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FindElementHelper(ON__UINT64 sn) @@ -557,9 +592,12 @@ struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FindElementHelper(ON_ class ON_SN_BLOCK* eblk; size_t i, j; - if ( m_maxsn < sn ) + if (m_private->MaxSn() < sn ) return 0; // happens almost every time an object is added to the doc + if (sn < UINT32_MAX && m_private->LowerMaxSn() < sn) + return 0; + if ( sn <= 0 ) return 0; @@ -1438,7 +1476,7 @@ void ON_SerialNumberMap::GarbageCollectHelper() if ( !m_sn_block0.m_sorted ) m_sn_block0.SortBlockHelper(); if ( 0 == m_snblk_list_count ) - m_maxsn = m_sn_block0.m_sn1; + m_private->SetMaxSn(m_sn_block0.m_sn1); if ( m_sn_block0.m_count < 7*(ON_SN_BLOCK::SN_BLOCK_CAPACITY/8) ) return; } @@ -1446,7 +1484,7 @@ void ON_SerialNumberMap::GarbageCollectHelper() { m_sn_block0.SortBlockHelper(); if ( 0 == m_snblk_list_count ) - m_maxsn = m_sn_block0.m_sn1; + m_private->SetMaxSn(m_sn_block0.m_sn1); } // Remove all purged serial numbers from every block @@ -1663,8 +1701,11 @@ struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::AddSerialNumber(ON__U m_sn_block0.m_sorted = 0; } } - if ( sn > m_maxsn ) - m_maxsn = sn; + if ( sn > m_private->MaxSn() ) + m_private->SetMaxSn(sn); + if (sn < UINT32_MAX && sn > m_private->LowerMaxSn()) + m_private->SetLowerMaxSn(sn); + m_sn_count++; e = &m_sn_block0.m_sn[m_sn_block0.m_count++]; memset(e,0,sizeof(*e)); @@ -2049,7 +2090,7 @@ void ON_SN_BLOCK::Dump(ON_TextLog& text_log) const void ON_SerialNumberMap::Dump(ON_TextLog& text_log) const { - text_log.Print("m_maxsn = %d\n",m_maxsn); + text_log.Print("m_maxsn = %d\n", m_private->MaxSn()); text_log.Print("m_sn_count = %d\n",m_sn_count); text_log.Print("m_sn_purged = %d\n",m_sn_purged); text_log.Print("m_active_id_count = %d\n",m_sn_purged); diff --git a/opennurbs_lookup.h b/opennurbs_lookup.h index 88d11d5e..ed543587 100644 --- a/opennurbs_lookup.h +++ b/opennurbs_lookup.h @@ -349,8 +349,7 @@ private: ON_SerialNumberMap& operator=(const ON_SerialNumberMap&) = delete; private: - ON__UINT64 m_maxsn = 0; // largest sn stored anywhere - + class ON_SerialNumberMapPrivate* m_private = nullptr; // Serial Number list counts ON__UINT64 m_sn_count = 0; // total number of elements diff --git a/opennurbs_material.cpp b/opennurbs_material.cpp index 59e52bdc..7a2fdbd3 100644 --- a/opennurbs_material.cpp +++ b/opennurbs_material.cpp @@ -4137,7 +4137,7 @@ public: virtual ~IClosestPointMapper() {} virtual bool IsValid() const = 0; virtual bool ClosestPointTC(const ON_3dPoint& pt, const ON_3fVector& vtNormalHint, ON_3dPoint& tcOut) const = 0; - virtual bool MatchFaceTC(int count, const ON_3dPoint* pPts, ON_3dPoint* pTcsOut) const = 0; + virtual bool MatchFaceTC(int count, const ON_3dPoint* pPts, ON_3dPoint* pTcsOut, const ON_3fVector& vtNormalHint) const = 0; }; // Closest point projection of texture coordinates for a mesh face. Samples projection close to face vertices and then extrapolates to vertices. @@ -4418,7 +4418,7 @@ public: }; public: SeamTool(const ON_Mesh& mesh, const ON_2fPointArray& tc) - : m_mesh(mesh), m_tc(tc), m_bProcessed(false), m_faceData(nullptr) + : m_mesh(mesh), m_tc(tc), m_bProcessed(false), m_faceData(nullptr), m_bHasSeams(false) { } virtual ~SeamTool() @@ -4426,6 +4426,13 @@ public: delete [] m_faceData; m_faceData = nullptr; } + bool HasSeams() const + { + if (!m_bProcessed) + Process(); + + return m_bHasSeams; + } int SeamlessNeighbours(int fi, int*& pFisOut) const { if (!m_bProcessed) @@ -4442,9 +4449,38 @@ public: pFisOut = m_faceData[fi].m_neighbourFis + m_faceData[fi].m_nSeamless; return m_faceData[fi].m_nSeamed; } + /// + /// Checks if any seams intersect the bounding box of the points expanded by the tolerance + /// + /// Number of points + /// Pointer to an array of points + /// Tolerance + /// Returns true if there might be a seam intersecting and false if no seams intersects + bool IntersectsSeams(int count, const ON_3dPoint* pPts, double tolerance) const + { + if (!m_bProcessed) + Process(); + + ON_BoundingBox bbox; + for (int i = 0; i < count; i++) + { + bbox.Set(pPts[i], 1); + } + bbox.Expand(ON_3dVector(tolerance, tolerance, tolerance)); + ON__INT_PTR sr_id = 0; + ON_RTreeSearchResult sr; + memset(&sr, 0, sizeof(sr)); + sr.m_capacity = 1; + sr.m_id = &sr_id; + m_seamTree.Search(bbox.Min(), bbox.Max(), sr); + if (sr.m_count > 0) + return true; + return false; + } private: void Process() const { + m_bHasSeams = false; m_faceData = new Data[m_mesh.FaceCount()]; for (int fi = 0; fi < m_mesh.FaceCount(); fi++) { @@ -4465,15 +4501,21 @@ public: else { seamedFis.Append(ofi); + const ON_Line seamLine = m_mesh.Topology().TopEdgeLine(tf.m_topei[i]); + m_seamTree.Insert(seamLine.BoundingBox().m_min, seamLine.BoundingBox().m_max, tf.m_topei[i]); + m_bHasSeams = true; } } else if (te.m_topf_count > 2) { + const ON_Line seamLine = m_mesh.Topology().TopEdgeLine(tf.m_topei[i]); + m_seamTree.Insert(seamLine.BoundingBox().m_min, seamLine.BoundingBox().m_max, tf.m_topei[i]); for (int j = 0; j < te.m_topf_count; j++) { if (te.m_topfi[j] != fi) { seamedFis.Append(te.m_topfi[j]); + m_bHasSeams = true; } } } @@ -4488,6 +4530,8 @@ public: mutable bool m_bProcessed; mutable Data* m_faceData; + mutable bool m_bHasSeams; + mutable ON_RTree m_seamTree; }; static ON_3dPoint Average(int count, const ON_3dPoint* pPts) @@ -4770,44 +4814,66 @@ public: std::unordered_map m_patches; }; - virtual bool MatchFaceTC(int count, const ON_3dPoint* pPts, ON_3dPoint* pTcsOut) const + virtual bool MatchFaceTC(int count, const ON_3dPoint* pPts, ON_3dPoint* pTcsOut, const ON_3fVector& vtNormalHint) const { if (nullptr != m_pSourceCPM) - return m_pSourceCPM->MatchFaceTC(count, pPts, pTcsOut); + return m_pSourceCPM->MatchFaceTC(count, pPts, pTcsOut, vtNormalHint); if (nullptr == m_pMeshFaceTree) return false; if (count > 4 || pPts == nullptr || pTcsOut == nullptr) return false; - const double initialMappingTol = 1.1e-5; + // Check if seams are present and if they might intersect the face. + // Tolerance used for the intersection is less than the maximum + // allowed tolerance for performance reasons. + if (m_seamTool.HasSeams() && m_seamTool.IntersectsSeams(count, pPts, 1.1)) + { + // Mapping primitive has seams close to the face. Using seamless + // patches to make sure each face gets mapped to a UV continuous block. + const double initialMappingTol = 1.1e-5; - ON_3dPoint amendedPts[5]; - memcpy(amendedPts, pPts, sizeof(ON_3dPoint) * count); - amendedPts[count] = Average(count, pPts); - const ON_3dPoint* pAmendedPts = amendedPts; - const int amendedCount = count + 1; + ON_3dPoint amendedPts[5]; + memcpy(amendedPts, pPts, sizeof(ON_3dPoint) * count); + amendedPts[count] = Average(count, pPts); + const ON_3dPoint* pAmendedPts = amendedPts; + const int amendedCount = count + 1; - SamplePoint samplePts[5] = { - SamplePoint(amendedPts[0], m_mesh), - SamplePoint(amendedPts[1], m_mesh), - SamplePoint(amendedPts[2], m_mesh), - SamplePoint(amendedPts[3], m_mesh), - SamplePoint(amendedPts[4], m_mesh) - }; + SamplePoint samplePts[5] = { + SamplePoint(amendedPts[0], m_mesh), + SamplePoint(amendedPts[1], m_mesh), + SamplePoint(amendedPts[2], m_mesh), + SamplePoint(amendedPts[3], m_mesh), + SamplePoint(amendedPts[4], m_mesh) + }; - TcSeamlessPatchCache patchCache(m_mesh, m_tc, m_seamTool, 5); + TcSeamlessPatchCache patchCache(m_mesh, m_tc, m_seamTool, 5); - for (double mappingTol = initialMappingTol; mappingTol <= 20.0; mappingTol = mappingTol * 10.0) - { - if (AdaptedMatchFaceTC(amendedCount, pAmendedPts, samplePts, count, pTcsOut, mappingTol, mappingTol == initialMappingTol, patchCache)) - { - if (m_totalMappingTol < mappingTol) - m_totalMappingTol = mappingTol; - return true; - } - } - m_failedFaceMatches++; + for (double mappingTol = initialMappingTol; mappingTol <= 20.0; mappingTol = mappingTol * 10.0) + { + if (AdaptedMatchFaceTC(amendedCount, pAmendedPts, samplePts, count, pTcsOut, mappingTol, mappingTol == initialMappingTol, patchCache)) + { + if (m_totalMappingTol < mappingTol) + m_totalMappingTol = mappingTol; + return true; + } + } + m_failedFaceMatches++; + } + else + { + // There are no seams on the mapping mesh. Simply use closest point to + // for better performance. + for (int i = 0; i < count; i++) + { + if (!ClosestPointTC(pPts[i], vtNormalHint, pTcsOut[i])) + { + m_failedFaceMatches++; + return false; + } + } + return true; + } return false; } @@ -5031,7 +5097,7 @@ static bool ProjectTextureCoordinates(const IClosestPointMapper& mapper, const O ON_3dPoint t[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin }; const int fvc = faceTo.IsQuad() ? 4 : 3; - if (mapper.MatchFaceTC(fvc, vtx, t)) + if (mapper.MatchFaceTC(fvc, vtx, t, vtFaceNormal)) { for (int fvi = 0; fvi < fvc; fvi++) { diff --git a/opennurbs_math.h b/opennurbs_math.h index 44449e54..424584ca 100644 --- a/opennurbs_math.h +++ b/opennurbs_math.h @@ -2567,5 +2567,5 @@ ON_DECL unsigned ON_LeastCommonMultiple( unsigned b ); - #endif + diff --git a/opennurbs_matrix.cpp b/opennurbs_matrix.cpp index cd5b28ba..0521604b 100644 --- a/opennurbs_matrix.cpp +++ b/opennurbs_matrix.cpp @@ -321,6 +321,7 @@ bool ON_Matrix::Create( int row_count, int col_count) if ( i < rows_per_block ) rows_per_block = i; int dblblk_count = rows_per_block*col_count; + // TODO: Make this malloc size a static on the DBLBLK class, this is too ugly struct DBLBLK* p = (struct DBLBLK*)onmalloc(sizeof(*p) + dblblk_count*sizeof(p->a[0])); p->a = (double*)(p+1); p->count = dblblk_count; diff --git a/opennurbs_matrix.h b/opennurbs_matrix.h index d47f5261..bd377c76 100644 --- a/opennurbs_matrix.h +++ b/opennurbs_matrix.h @@ -132,6 +132,8 @@ public: row_count - [in] col_count - [in] M - [in] + M should point to the start of an array of double*, + each pointing to the start of the storage for a row. bDestructorFreeM - [in] If true, ~ON_Matrix will call onfree(M). If false, caller is managing M's memory. @@ -401,7 +403,12 @@ public: bool IsColOrthoganal() const; bool IsColOrthoNormal() const; - + // 2024-09-05, Pierre: + // Note that this does not mean the memory allocated is contiguous. + // m points to the start of m_rowmem[0] in most cases. + // When the matrix is bigger than max_dblblk_size (see ON_Matrix::Create), m_rowmem does + // not point to contiguous pieces of memory, and indexing as **(m + i*m_col_count + j) + // would read into the wrong place. double** m = nullptr; // m[i][j] = value at row i and column j // 0 <= i < RowCount() // 0 <= j < ColCount() diff --git a/opennurbs_point.cpp b/opennurbs_point.cpp index a719d5b5..28a0664c 100644 --- a/opennurbs_point.cpp +++ b/opennurbs_point.cpp @@ -9391,17 +9391,21 @@ const ON_2dPoint ON_4dRect::BottomRight(void) const { return ON_2dPoint(right, b bool ON_4dRect::IntersectRect(const ON_4dRect * r1, const ON_4dRect * r2) { - left = ON_Max(r1->left, r2->left); - top = ON_Max(r1->top, r2->top); - right = ON_Min(r1->right, r2->right); - bottom = ON_Min(r1->bottom, r2->bottom); + // The previous implementation was incorrect. The implementation for ON_4iRect::Intersect + // was/is correct, so its implemenation is now used here. + left = ON_Max(r1->left, r2->left); + right = ON_Min(r1->right, r2->right); + if (right > left) + { + top = ON_Max(r1->top, r2->top); + bottom = ON_Min(r1->bottom, r2->bottom); + if (bottom > top) + return true; + } - if (IsRectEmpty()) { - // degenerate rectangle - SetRectEmpty(); - return false; - } - return true; + // degenerate rectangle at this point... + SetRectEmpty(); + return false; } bool ON_4dRect::IntersectRect(const ON_4dRect & r1, const ON_4dRect & r2) { return IntersectRect(&r1, &r2); } diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index 60b4b91d..bde30c95 100644 --- a/opennurbs_public_version.h +++ b/opennurbs_public_version.h @@ -6,7 +6,7 @@ // To update version numbers, edit ..\build\build_dates.msbuild #define RMA_VERSION_MAJOR 8 -#define RMA_VERSION_MINOR 10 +#define RMA_VERSION_MINOR 12 //////////////////////////////////////////////////////////////// // @@ -14,9 +14,9 @@ // first step in each build. // #define RMA_VERSION_YEAR 2024 -#define RMA_VERSION_MONTH 8 -#define RMA_VERSION_DATE 15 -#define RMA_VERSION_HOUR 13 +#define RMA_VERSION_MONTH 10 +#define RMA_VERSION_DATE 8 +#define RMA_VERSION_HOUR 7 #define RMA_VERSION_MINUTE 0 //////////////////////////////////////////////////////////////// @@ -35,8 +35,8 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 8,10,24228,13000 -#define VERSION_WITH_PERIODS 8.10.24228.13000 +#define VERSION_WITH_COMMAS 8,12,24282,7000 +#define VERSION_WITH_PERIODS 8.12.24282.07000 #define COPYRIGHT "Copyright (C) 1993-2024, Robert McNeel & Associates. All Rights Reserved." #define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library." @@ -44,11 +44,11 @@ #define RMA_VERSION_NUMBER_MAJOR_WSTRING L"8" #define RMA_PREVIOUS_VERSION_NUMBER_MAJOR_WSTRING L"7" -#define RMA_VERSION_NUMBER_SR_STRING "SR10" -#define RMA_VERSION_NUMBER_SR_WSTRING L"SR10" +#define RMA_VERSION_NUMBER_SR_STRING "SR12" +#define RMA_VERSION_NUMBER_SR_WSTRING L"SR12" -#define RMA_VERSION_WITH_PERIODS_STRING "8.10.24228.13000" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.10.24228.13000" +#define RMA_VERSION_WITH_PERIODS_STRING "8.12.24282.07000" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.12.24282.07000" diff --git a/opennurbs_render_content.cpp b/opennurbs_render_content.cpp index 350a7b02..2a13af7a 100644 --- a/opennurbs_render_content.cpp +++ b/opennurbs_render_content.cpp @@ -367,8 +367,13 @@ static void EnsureNameValid(ON_wString& name) name += c; } - // Also disallow ':' inside the name. - name.Replace(':', ' '); + // 2024-08-27 : kike@mcneel.com + // `ON_ModelComponent::IsValidComponentName` allows ':'. + // The hole point of https://mcneel.myjetbrains.com/youtrack/issue/RH-78603 + // is to ensure all type names has same restrictions. + // + //// Also disallow ':' inside the name. + //name.Replace(':', ' '); name.TrimLeftAndRight(); } diff --git a/opennurbs_string.h b/opennurbs_string.h index 1924d265..64d3b5cf 100644 --- a/opennurbs_string.h +++ b/opennurbs_string.h @@ -2739,6 +2739,9 @@ public: /// WARNING SIGN U+26A0 (⚠) static const wchar_t WarningSign = (wchar_t)ON_UnicodeCodePoint::ON_WarningSign; + /// CHECK MARK U+2713 (✓) + static const wchar_t CheckMark = (wchar_t)ON_UnicodeCodePoint::ON_CheckMark; + /// /// REPLACEMENT CHARACTER U+FFFD (�) /// By convention, U+FFFD is used to mark string elements where diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp index 769bb160..4a35fcaf 100644 --- a/opennurbs_subd.cpp +++ b/opennurbs_subd.cpp @@ -1232,6 +1232,51 @@ unsigned int ON_SubDComponentPtr::ComponentId() const return (nullptr != c) ? c->m_id : 0U; } +unsigned int ON_SubDComponentPtr::VertexId() const +{ + const ON_SubDVertex* v = this->Vertex(); + return (nullptr != v) ? v->m_id : 0; +} + +unsigned int ON_SubDComponentPtr::EdgeId() const +{ + const ON_SubDEdge* e = this->Edge(); + return (nullptr != e) ? e->m_id : 0; +} + +unsigned int ON_SubDComponentPtr::FaceId() const +{ + const ON_SubDFace* f = this->Face(); + return (nullptr != f) ? f->m_id : 0; +} + +ON_SubDVertexTag ON_SubDComponentPtr::VertexTag() const +{ + const ON_SubDVertex* v = this->Vertex(); + return (nullptr != v) ? v->m_vertex_tag : ON_SubDVertexTag::Unset; +} + +ON_SubDEdgeTag ON_SubDComponentPtr::EdgeTag() const +{ + const ON_SubDEdge* e = this->Edge(); + return (nullptr != e) ? e->m_edge_tag : ON_SubDEdgeTag::Unset; +} + +double ON_SubDComponentPtr::VertexSharpness() const +{ + const ON_SubDVertex* v = this->Vertex(); + return (nullptr != v) ? v->VertexSharpness() : ON_DBL_QNAN; +} + +const ON_SubDEdgeSharpness ON_SubDComponentPtr::EdgeSharpness(bool bUseCreaseSharpness) const +{ + const ON_SubDEdgePtr eptr = this->EdgePtr(); + return + eptr.IsNotNull() + ? eptr.RelativeSharpness(bUseCreaseSharpness) + : ON_SubDEdgeSharpness::Nan; +} + void ON_SubDComponentPtr::ClearSavedSubdivisionPoints() const { switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr) @@ -5008,7 +5053,7 @@ double ON_SubDEdgeSharpness::VertexSharpness( ON_SubDVertexTag vertex_tag, double interior_crease_vertex_sharpness, unsigned sharp_edge_end_count, - double maximum_edge_end_sharpness + double maximum_edge_sharpness_at_vertex ) { // NOTE WELL: @@ -5033,7 +5078,7 @@ double ON_SubDEdgeSharpness::VertexSharpness( } else if (ON_SubDVertexTag::Crease == vertex_tag) { - if (interior_crease_vertex_sharpness > 0.0) + if (interior_crease_vertex_sharpness > 0.0 && interior_crease_vertex_sharpness <= ON_SubDEdgeSharpness::MaximumValue) { // In the comments below, // VSS = vertex sector sharpness = maximum edges sharpness @@ -5049,7 +5094,7 @@ double ON_SubDEdgeSharpness::VertexSharpness( return interior_crease_vertex_sharpness; } - if (interior_crease_vertex_sharpness > maximum_edge_end_sharpness) + if (interior_crease_vertex_sharpness > maximum_edge_sharpness_at_vertex) { // It should be the case that the origin of this vertex // is an interior crease vertex where: @@ -5078,7 +5123,7 @@ double ON_SubDEdgeSharpness::VertexSharpness( return 0.0; } - return maximum_edge_end_sharpness; + return maximum_edge_sharpness_at_vertex; } double ON_SubDEdgeSharpness::SharpnessFromNormalizedValue( @@ -15246,6 +15291,249 @@ const ON_3dPoint ON_SubDVertex::SubdivisionPoint() const return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint; } +const ON_3dPoint ON_SubDVertex::CreaseVertexSubdivisionPoint( + const ON_3dPoint& P, + double vertex_sharpness, + const ON_3dPoint& A1, + const ON_3dPoint& A2 +) +{ + if (vertex_sharpness >= 1.0 && vertex_sharpness <= ON_SubDEdgeSharpness::MaximumValue) + return P; + + const ON_3dPoint C( + 0.75 * P.x + 0.125 * (A1.x + A2.x), + 0.75 * P.y + 0.125 * (A1.y + A2.y), + 0.75 * P.z + 0.125 * (A1.z + A2.z) + ); + + if (vertex_sharpness > 0.0 && vertex_sharpness < 1.0) + { + // Apply sharp bias to C + const double c = 1.0 - vertex_sharpness; + return ON_3dPoint( + c * C.x + vertex_sharpness * P.x, + c * C.y + vertex_sharpness * P.y, + c * C.z + vertex_sharpness * P.z + ); + } + + return C; +} + +const ON_SubDVertexSharpnessCalculator ON_SubDVertexSharpnessCalculator::Unset = ON_SubDVertexSharpnessCalculator(); + +void ON_SubDVertexSharpnessCalculator::Internal_SetVertex( + ON_SubDVertexTag vertex_tag, + ON_3dPoint vertex_control_net_point, + double maximum_sharpness_at_interior_crease_vertex +) +{ + if (ON_SubDVertexTag::Unset != vertex_tag) + { + m_vertex_tag = vertex_tag; + m_edge_count = 0; + m_crease_edge_count = 0; + m_sharp_edge_count = 0; + m_vertex_control_net_point = vertex_control_net_point; + if (ON_SubDVertexTag::Crease == vertex_tag + && maximum_sharpness_at_interior_crease_vertex > 0.0 + && maximum_sharpness_at_interior_crease_vertex <= ON_SubDEdgeSharpness::MaximumValue) + m_u1.m_max_edge_sharpness_at_vertex = maximum_sharpness_at_interior_crease_vertex; + else + m_u1.m_max_edge_sharpness_at_vertex = 0.0; + m_status = ON_SubDVertexSharpnessCalculator::Status::VertexSet; + } +} + +void ON_SubDVertexSharpnessCalculator::Internal_SetVertex( + const ON_SubDVertex* vertex +) +{ + if (nullptr != vertex) + { + Internal_SetVertex( + vertex->m_vertex_tag, + vertex->ControlNetPoint(), + (ON_SubDVertexTag::Crease == this->m_vertex_tag && vertex->m_face_count > 0 && vertex->m_face_count + 1 == this->m_edge_count) + ? vertex->Internal_InteriorCreaseVertexSharpnessForExperts() + : 0.0 + ); + if (ON_SubDVertexSharpnessCalculator::Status::VertexSet == this->m_status && nullptr != vertex->m_edges) + { + ON_SubDEdgePtr eptr; + const ON_SubDVertex* other_vertex; + unsigned other_vertex_vi; + const unsigned short edge_count = vertex->m_edge_count; + for (unsigned short ei = 0; ei < edge_count; ++ei) + { + eptr = vertex->m_edges[ei]; + if (vertex == eptr.RelativeVertex(0)) + other_vertex_vi = 1; + else if (vertex == eptr.RelativeVertex(1)) + other_vertex_vi = 0; + else + continue; + other_vertex = eptr.RelativeVertex(other_vertex_vi); + if (nullptr == other_vertex || vertex == other_vertex) + continue; + AddEdgeSharpnessAndControlNetPoint(eptr.RelativeSharpness(true).EndSharpness(other_vertex_vi), other_vertex->ControlNetPoint()); + } + Internal_SetVertexSharpnessAndSharpPoint(); + } + } +} + +ON_SubDVertexSharpnessCalculator::ON_SubDVertexSharpnessCalculator(const ON_SubDVertex* vertex) +{ + Internal_SetVertex(vertex); +} + +ON_SubDVertexSharpnessCalculator::ON_SubDVertexSharpnessCalculator(ON_SubDVertexTag vertex_tag, ON_3dPoint vertex_control_net_point, double maximum_sharpness_at_interior_crease_vertex) +{ + Internal_SetVertex( + vertex_tag, + vertex_control_net_point, + ON_SubDVertexTag::Crease == vertex_tag + ? maximum_sharpness_at_interior_crease_vertex + : 0.0 + ); +} + +bool ON_SubDVertexSharpnessCalculator::SetVertex(const ON_SubDVertex* vertex) +{ + *this = ON_SubDVertexSharpnessCalculator::Unset; + Internal_SetVertex(vertex); + return (this->m_status == ON_SubDVertexSharpnessCalculator::Status::SharpnessSet); +} + +bool ON_SubDVertexSharpnessCalculator::SetVertex(ON_SubDVertexTag vertex_tag, ON_3dPoint vertex_control_net_point, double maximum_sharpness_at_interior_crease_vertex) +{ + *this = ON_SubDVertexSharpnessCalculator::Unset; + Internal_SetVertex(vertex_tag, vertex_control_net_point, maximum_sharpness_at_interior_crease_vertex); + return (this->m_status == ON_SubDVertexSharpnessCalculator::Status::VertexSet); +} + +bool ON_SubDVertexSharpnessCalculator::AddEdgeSharpnessAndControlNetPoint(double sharpness_at_vertex, ON_3dPoint other_end_control_net_point) +{ + if (ON_SubDVertexSharpnessCalculator::Status::VertexSet != this->m_status) + return false; + + ++m_edge_count; + if (sharpness_at_vertex > 0.0 && ON_SubDEdgeSharpness::IsValidValue(sharpness_at_vertex, true)) + { + const unsigned i = this->m_crease_edge_count + this->m_sharp_edge_count; + if (i < 2) + this->m_other_end_control_net_points[i] = other_end_control_net_point; + if (ON_SubDEdgeSharpness::CreaseValue == sharpness_at_vertex) + ++m_crease_edge_count; + else + { + ++m_sharp_edge_count; + if (sharpness_at_vertex > this->m_u1.m_max_edge_sharpness_at_vertex) + this->m_u1.m_max_edge_sharpness_at_vertex = sharpness_at_vertex; + } + } + return true; +} + +bool ON_SubDVertexSharpnessCalculator::AddCreaseEdgeControlNetPoint(ON_3dPoint other_end_control_net_point) +{ + return AddEdgeSharpnessAndControlNetPoint(ON_SubDEdgeSharpness::CreaseValue, other_end_control_net_point); +} + +bool ON_SubDVertexSharpnessCalculator::VertexIsSet() const +{ + return (ON_SubDVertexSharpnessCalculator::Status::Unset != this->m_status); +} + +ON_SubDVertexTag ON_SubDVertexSharpnessCalculator::VertexTag() const +{ + return this->VertexIsSet() ? this->m_vertex_tag : ON_SubDVertexTag::Unset; +} + +const ON_3dPoint ON_SubDVertexSharpnessCalculator::VertexControlNetPoint() const +{ + return this->VertexIsSet() ? this->m_vertex_control_net_point : ON_3dPoint::NanPoint; +} + +unsigned ON_SubDVertexSharpnessCalculator::EdgeCount() const +{ + return this->m_edge_count; +} + +unsigned ON_SubDVertexSharpnessCalculator::SharpEdgeCount() const +{ + return this->m_sharp_edge_count; +} + +unsigned ON_SubDVertexSharpnessCalculator::CreaseEdgeCount() const +{ + return this->m_crease_edge_count; +} + + +bool ON_SubDVertexSharpnessCalculator::IsSharpVertex() const +{ + return this->VertexSharpness() > 0.0; +} + +bool ON_SubDVertexSharpnessCalculator::Internal_SetVertexSharpnessAndSharpPoint() const +{ + if (ON_SubDVertexSharpnessCalculator::Status::VertexSet == this->m_status && this->m_edge_count > 0) + { + this->m_status = ON_SubDVertexSharpnessCalculator::Status::Unset; + + const double vertex_sharpness = ON_SubDEdgeSharpness::VertexSharpness( + this->m_vertex_tag, + (ON_SubDVertexTag::Crease == this->m_vertex_tag ? this->m_u1.m_max_edge_sharpness_at_vertex : 0.0), + this->m_sharp_edge_count, + this->m_u1.m_max_edge_sharpness_at_vertex + ); + + ON_3dPoint R = ON_3dPoint::Origin; // origin rather than nan so 0.0 * VertexSharpPoint() is not a nan point + if (vertex_sharpness > 0.0) + { + if (ON_SubDVertexTag::Crease == m_vertex_tag) + R = this->m_vertex_control_net_point; + else if (ON_SubDVertexTag::Smooth == m_vertex_tag || ON_SubDVertexTag::Dart == m_vertex_tag) + { + const unsigned short sharp_and_crease_edge_count = this->m_sharp_edge_count + this->m_crease_edge_count; + if (sharp_and_crease_edge_count > 2) + R = this->m_vertex_control_net_point; + else if (2 == sharp_and_crease_edge_count) + R = ON_SubDVertex::CreaseVertexSubdivisionPoint(this->m_vertex_control_net_point, 0.0, this->m_other_end_control_net_points[0], this->m_other_end_control_net_points[1]); + } + } + + this->m_u1.m_vertex_sharpness = vertex_sharpness; + this->m_other_end_control_net_points[0] = R; + this->m_other_end_control_net_points[1] = ON_3dPoint::NanPoint; + + this->m_status = ON_SubDVertexSharpnessCalculator::Status::SharpnessSet; + } + else + this->m_status = ON_SubDVertexSharpnessCalculator::Status::Unset; + + return (ON_SubDVertexSharpnessCalculator::Status::SharpnessSet == this->m_status) ? this->m_u1.m_vertex_sharpness : 0.0; +} + +double ON_SubDVertexSharpnessCalculator::VertexSharpness() const +{ + if (ON_SubDVertexSharpnessCalculator::Status::VertexSet == this->m_status && this->m_edge_count > 0) + Internal_SetVertexSharpnessAndSharpPoint(); + return (ON_SubDVertexSharpnessCalculator::Status::SharpnessSet == this->m_status) ? this->m_u1.m_vertex_sharpness : 0.0; +} + +const ON_3dPoint ON_SubDVertexSharpnessCalculator::VertexSharpPoint() const +{ + if (this->IsSharpVertex()) + return this->m_other_end_control_net_points[0]; + + // Return origin rather than nan so that 0.0*this->VertexSharpPoint() will not be a nan point. + return ON_3dPoint::Origin; +} + bool ON_SubDVertex::GetSubdivisionPoint( diff --git a/opennurbs_subd.h b/opennurbs_subd.h index 38086708..b8cda2d2 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -845,22 +845,24 @@ public: /// This paramter is important in special situations that occur /// in low level SubD evaluation code where information from only one /// sector is present. In all other cases, this value doesn't matter as long - /// as <= global_vertex_sharpness <= maximum_edge_end_sharpness. - /// When in doubt pass 0.0. + /// as interior_crease_vertex_sharpness <= maximum_edge_sharpness_at_vertex. + /// When in doubt pass 0.0 or ON_DBL_QNAN. /// /// /// Number of sharp edges attached to the vertex that have /// nonzero end sharpness at the vertex. /// - /// + /// /// The largest sharp edge end sharpness at the vertex. /// - /// + /// + /// The vertex sharpness value to use when subdividing the vertex. + /// static double VertexSharpness( ON_SubDVertexTag vertex_tag, double interior_crease_vertex_sharpness, unsigned sharp_edge_end_count, - double maximum_edge_end_sharpness + double maximum_edge_sharpness_at_vertex ); /// @@ -895,6 +897,235 @@ bool operator==(const ON_SubDEdgeSharpness& lhs, const ON_SubDEdgeSharpness& rhs ON_DECL bool operator!=(const ON_SubDEdgeSharpness& lhs, const ON_SubDEdgeSharpness& rhs); +class ON_SubDVertexSharpnessCalculator +{ +public: + ON_SubDVertexSharpnessCalculator() = default; + ~ON_SubDVertexSharpnessCalculator() = default; + ON_SubDVertexSharpnessCalculator(const ON_SubDVertexSharpnessCalculator&) = default; + ON_SubDVertexSharpnessCalculator& operator=(const ON_SubDVertexSharpnessCalculator&) = default; + + static const ON_SubDVertexSharpnessCalculator Unset; + + /// + /// This constructor completely initializes the class + /// and you may immediately call VertexSharpness() and VertexSharpPoint(). + /// + /// + /// Vertex where sharpness information is desired. + /// + ON_SubDVertexSharpnessCalculator( + const ON_SubDVertex* vertex + ); + + /// + /// This constructor begins the initialization of the class. + /// You must call AddEdgeSharpnessAndControlNetPoint() or + /// AddCreaseEdgeControlNetPoint() for each edge attached to the vertex. + /// This technique is useful when iteratove subdivision calculations + /// are being performed an no explicit ON_SubDVertex exists. + /// + /// + /// + /// + /// If the vertex_tag parameter is ON_SubDVertexTag::Crease + /// and the vertex is an interior crease (two crease sectors), + /// then maximum_sharpness_at_interior_crease_vertex should + /// be the maximum value of the edge sharpnesses of all + /// non-crease edges at this vertex. + /// In all other cases pass 0.0. + /// When vertex_tag is not ON_SubDVertexTag::Crease, this + /// parameter is always ignored. + /// + ON_SubDVertexSharpnessCalculator( + ON_SubDVertexTag vertex_tag, + ON_3dPoint vertex_control_net_point, + double maximum_sharpness_at_interior_crease_vertex + ); + + /// + /// Completely initializes the class and you may immediately call VertexSharpness() and VertexSharpPoint(). + /// + /// + /// Vertex where sharpness information is desired. + /// + bool SetVertex( + const ON_SubDVertex* vertex + ); + + /// + /// This constructor begins the initialization of the class. + /// You must call AddEdgeSharpnessAndControlNetPoint() or + /// AddCreaseEdgeControlNetPoint() for each edge attached to the vertex. + /// This technique is useful when iteratove subdivision calculations + /// are being performed an no explicit ON_SubDVertex exists. + /// + /// + /// + /// + /// If the vertex_tag parameter is ON_SubDVertexTag::Crease + /// and the vertex is an interior crease (two crease sectors), + /// then maximum_sharpness_at_interior_crease_vertex should + /// be the maximum value of the edge sharpnesses of all + /// non-crease edges at this vertex. + /// In all other cases pass 0.0. + /// When vertex_tag is not ON_SubDVertexTag::Crease, this + /// parameter is always ignored. + /// + /// + /// True if successful. False otherwise. + /// + bool SetVertex( + ON_SubDVertexTag vertex_tag, + ON_3dPoint vertex_control_net_point, + double maximum_sharpness_at_interior_crease_vertex + ); + + /// + /// If this class was created using the constructor that has a vertex tag + /// and that tag was ON_SubDVertexTag::Smooth or ON_SubDVertexTag::Dart, + /// or ON_SubDVertexTag::Crease, then you you must call this function for + /// every sharp edge connected to the vertex. + /// + /// + /// If the associated edge is a crease, then pass ON_SubDEdgeSharpness::CreaseValue + /// (or call AddCreaseEdge(other_end)). + /// If the associated edge is smooth, then pass the edge sharpness at the + /// the end connect to the vertex in question. + /// + /// + /// The control net point at edge's other vertex. + /// + /// + /// True if successful. False otherwise. + /// + bool AddEdgeSharpnessAndControlNetPoint( + double sharpness_at_vertex, + ON_3dPoint other_end_control_net_point + ); + + /// + /// You may call this function if the edge in question + /// is a crease edge attached to the vertex. + /// + /// + /// The control net point at edge's other vertex. + /// + bool AddCreaseEdgeControlNetPoint( + ON_3dPoint other_end_control_net_point + ); + + /// + /// If the vertex has been set, true is returned. + /// Otherwise, false is returned. + /// + bool VertexIsSet() const; + + /// If the vertex has been set, the vertex's tag is returned. + /// Otherwise, ON_SubDVertexTag::Unset is returned. + /// + ON_SubDVertexTag VertexTag() const; + + /// If the vertex has been set, the vertex's control net point is returned. + /// Otherwise, ON_3dPoint::NanPoint is returned. + /// + const ON_3dPoint VertexControlNetPoint() const; + + /// Number of edges attached to the vertex. + unsigned EdgeCount() const; + + /// Number of sharp edges attached to the vertex. + unsigned SharpEdgeCount() const; + + /// Number of crease edges attached to the vertex. + unsigned CreaseEdgeCount() const; + + /// + /// True if the vertex has nonzero sharpness. + /// + bool IsSharpVertex() const; + + /// + /// When the vertex sharpness is > 0, the vertex subdivision + /// point is a blend of this->VertexSharpPoint() and the + /// Catmull-Clark subdivision point calculated as if there were no sharpness. + /// + /// + /// The vertex sharpness. + /// + double VertexSharpness() const; + + /// + /// + /// + /// + /// If this->VertexSharpness() > 0, then this point must be blended with + /// the Catmull-Clark subdivision point calculated as if there were no sharpness. + /// Otherwise, 0 is returned. + /// + const ON_3dPoint VertexSharpPoint() const; + +private: + void Internal_SetVertex( + ON_SubDVertexTag vertex_tag, + ON_3dPoint vertex_control_net_point, + double maximum_sharpness_at_interior_crease_vertex + ); + + void Internal_SetVertex( + const ON_SubDVertex* vertex + ); + + bool Internal_SetVertexSharpnessAndSharpPoint() const; + + mutable enum class Status : unsigned char + { + Unset, + + /// + /// m_vertex_tag, m_u2.m_vertex_control_net_point are set + /// and edges can be added. + /// + VertexSet, + + /// + /// m_u1.m_vertex_sharpness = VertexSharpness(), + /// m_other_end_control_net_points[0] = VertexSharpPoint(). + /// m_other_end_control_net_points[1] = ON_3dPoint::NanPoint. + /// + SharpnessSet + } + m_status; + + ON_SubDVertexTag m_vertex_tag = ON_SubDVertexTag::Unset; + + /// + /// Total number of edges attached to the vertex + /// + unsigned short m_edge_count = 0; + + /// + /// Number of crease edges attached to the vertex. + /// + unsigned short m_crease_edge_count = 0; + + /// + /// Number of sharp edges attached to the vertex. + /// + unsigned short m_sharp_edge_count = 0; + + ON_3dPoint m_vertex_control_net_point = {}; + + mutable union + { + double m_max_edge_sharpness_at_vertex = 0.0; + double m_vertex_sharpness; + } m_u1; + + mutable + ON_3dPoint m_other_end_control_net_points[2] = {}; +}; + /// /// A ON_SubDFaceCornerDex is a value that identifies a subd face corner. /// @@ -1573,13 +1804,18 @@ public: bool IsNull() const; bool IsNotNull() const; - /* - Returns: - If Vertex() is not nullptr, Vertex()->m_id is returned. - Otherwise, 0 is returned. - */ + /// + /// If Vertex() is not nullptr, Vertex()->m_id is returned. + /// Otherwise, 0 is returned. + /// unsigned int VertexId() const; + /// + /// If Vertex() is not nullptr, Vertex()->m_vertex_tag is returned. + /// Otherwise, ON_SubDVertexTag::Unset is returned. + /// + ON_SubDVertexTag VertexTag() const; + /* Returns: True if this vertex is active in its parent subd or other @@ -2578,12 +2814,65 @@ public: unsigned int ComponentId() const; + /// + /// If Vertex() is not nullptr, Vertex()->m_id is returned. + /// Otherwise, 0 is returned. + /// + unsigned int VertexId() const; + + /// + /// If Edge() is not nullptr, Edge()->m_id is returned. + /// Otherwise, 0 is returned. + /// + unsigned int EdgeId() const; + + /// + /// If Face() is not nullptr, Face()->m_id is returned. + /// Otherwise, 0 is returned. + /// + unsigned int FaceId() const; + + /// + /// If Vertex() is not nullptr, Vertex()->m_vertex_tag is returned. + /// Otherwise, ON_SubDVertexTag::Unset is returned. + /// + ON_SubDVertexTag VertexTag() const; + + /// + /// If Edge() is not nullptr, Edge()->m_edge_tag is returned. + /// Otherwise, ON_SubDEdgeTag::Unset is returned. + /// + ON_SubDEdgeTag EdgeTag() const; + + /// + /// If Vertex() is not nullptr, Vertex()->m_vertex_tag is returned. + /// Otherwise, ON_DBL_NAN is returned. + /// + double VertexSharpness() const; + + /// + /// Used to get edge sharpness when the referenced component is an edge. + /// + /// + /// + /// + /// If Edge() is not nullptr, Edge()->Sharpness(bUseCreaseSharpness) is returned. + /// Otherwise, ON_SubDEdgeTag::Nan is returned. + /// + const ON_SubDEdgeSharpness EdgeSharpness(bool bUseCreaseSharpness) const; + const ON_COMPONENT_INDEX ComponentIndex() const; const ON_3dPoint ControlNetCenterPoint() const; const ON_BoundingBox ControlNetBoundingBox() const; + + /// + /// Get the location of the component's subdivision vertex. + /// + /// Catmull-Clark component subdivision point. const ON_3dPoint SubdivisionPoint() const; + /* Returns: A value suitable for hash table used based on the component type and id. @@ -14345,7 +14634,7 @@ public: /// If the vertex is a dart or crease and and one or more attached edges have positive end sharpness /// at this vertex, then the maximum edge end sharpness at this vertex is returned. /// Otherwise 0.0 is returned. - ///< / returns> + /// double VertexSharpness() const; /// @@ -14445,7 +14734,7 @@ public: /// The vertex sharpness is returned ( >= 0.0 ). /// If the returned value is > 0.0, then the sharp subdivision point /// = sum(0 <= i < count, c[i]*v[i]->ControlNetPoint()) - ///< / returns> + /// double GetSharpSubdivisionPoint( unsigned& count, const ON_SubDVertex* v[3], @@ -14736,6 +15025,22 @@ public: /// const ON_3dPoint SubdivisionPoint() const; + /// + /// Calculate the Catmull-Clark subdivision point for a crease vertex. + /// + /// crease vertex control net point. + /// + /// maximum value of smooth edge sharpnesses at the crease vertex. + /// control net point at the other end of one attached crease edge. + /// control net point at the other end of the other attached crease edge. + /// + static const ON_3dPoint CreaseVertexSubdivisionPoint( + const ON_3dPoint& P, + double vertex_sharpness, + const ON_3dPoint& A1, + const ON_3dPoint& A2 + ); + /* Description: diff --git a/opennurbs_subd.natvis b/opennurbs_subd.natvis deleted file mode 100644 index d7704e09..00000000 --- a/opennurbs_subd.natvis +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - {{vertex id={m_id}}} - - - - {{edge id={m_id}}} - - nullptr!=m_vertex[0] ? m_vertex[0]->m_id : 0 - nullptr!=m_vertex[1] ? m_vertex[1]->m_id : 0 - - - - - {{face id={m_id}}} - - - - - - null - vertex(+) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - vertex(-) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - edge(+) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - edge(-) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - face(+) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - face(-) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - - ((m_ptr>8) && (2==(m_ptr&7)))?(((ON_SubDVertex*)(m_ptr&0xFFFFFFFFFFFFFFF8))):((ON_SubDVertex*)nullptr) - ((m_ptr>8) && (4==(m_ptr&7)))?(*((ON_SubDEdgePtr*)(m_ptr&0xFFFFFFFFFFFFFFF8))):ON_SubDEdgePtr::Null - ((m_ptr>8) && (6==(m_ptr&7)))?(((ON_SubDFace*)(m_ptr&0xFFFFFFFFFFFFFFF8))):((ON_SubDFace*)nullptr) - - - - - - null - vertex(+) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - vertex(-) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - - m_ptr>=8?(((ON_SubDVertex*)(m_ptr&0xFFFFFFFFFFFFFFF8))):nullptr - - - - - - null - edge(+) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - edge(-) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - - (m_ptr>8)?(((ON_SubDEdge*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_vertex[m_ptr%2]->m_id):0 - (m_ptr>8)?(((ON_SubDEdge*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_vertex[1-(m_ptr%2)]->m_id):0 - m_ptr>=8?(((ON_SubDEdge*)(m_ptr&0xFFFFFFFFFFFFFFF8))):nullptr - - - - - - null - face(+) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - face(-) id={((ON_SubDComponentBase*)(m_ptr&0xFFFFFFFFFFFFFFF8))->m_id} - - m_ptr>=8?(((ON_SubDFace*)(m_ptr&0xFFFFFFFFFFFFFFF8))):nullptr - - - - - - {{fragment {m_face_fragment_index+1}/{m_face_fragment_count} (face id={m_face>8?m_face->m_id:0})}}} - - (short int)(m_vertex_count_etc&0x1FFF) - (short int)(m_vertex_capacity_etc&0x1FFF) - (bool)(m_vertex_count_etc&0x8000) - (bool)(m_vertex_count_etc&0x4000) - (bool)(m_vertex_capacity_etc&0x2000) - (bool)(m_vertex_capacity_etc&0x4000) - (bool)(m_vertex_capacity_etc&0x8000) - - - - diff --git a/opennurbs_surface.cpp b/opennurbs_surface.cpp index 84d51be2..151ef1e8 100644 --- a/opennurbs_surface.cpp +++ b/opennurbs_surface.cpp @@ -108,6 +108,21 @@ bool ON_Surface::SetDomain( return false; } +const ON_SimpleArray ON_Surface::SpanVector(int dir) const +{ + ON_SimpleArray span_vector; + const int span_count = this->SpanCount(dir); + if (span_count >= 1) + { + span_vector.Reserve(span_count + 1); + if (this->GetSpanVector(dir, span_vector.Array())) + span_vector.SetCount(span_count + 1); + else + span_vector.Destroy(); + } + return span_vector; // uses Rvalue clone - no array copied. +} + ////////// // If t is in the domain of the surface, GetSpanVectorIndex() returns the // span vector index "i" such that span_vector[i] <= t <= span_vector[i+1]. diff --git a/opennurbs_surface.h b/opennurbs_surface.h index 412217bb..fbd746be 100644 --- a/opennurbs_surface.h +++ b/opennurbs_surface.h @@ -217,6 +217,19 @@ public: double* span_vector // array of length SpanCount() + 1 ) const = 0; // + /// + /// The surface's span vectors are a stricltly monotone increasing lists of doubles + /// that specify the rectangles in the domain where the surface is C-infinity. + /// + /// + /// 0 selects the first surface parameter's span vector. + /// 1 selects the second surface parameter's span vector. + /// + /// + /// The selected surface span vector. + /// + const ON_SimpleArray SpanVector(int dir) const; + ////////// // If t is in the domain of the surface, GetSpanVectorIndex() returns the // span vector index "i" such that span_vector[i] <= t <= span_vector[i+1]. diff --git a/opennurbs_system_compiler.h b/opennurbs_system_compiler.h index 9fcfde2a..2c4d9e02 100644 --- a/opennurbs_system_compiler.h +++ b/opennurbs_system_compiler.h @@ -294,41 +294,6 @@ #undef ON_HAS_RVALUEREF #endif -#elif defined( ON_COMPILER_ANDROIDNDK ) -/* -//////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////// -// -// ON_COMPILER_ANDROIDNDK -// -*/ - -/* -// McNeel defines ON_COMPILER_ANDROIDNDK in makefiles -*/ - -#if defined(__GNUC__) && (__GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 7)) -// C++11 noexcept and Rvalue references are in gcc 4.7 and later -#undef ON_NOEXCEPT -#define ON_NOEXCEPT noexcept -#if !defined(ON_HAS_RVALUEREF) -#define ON_HAS_RVALUEREF -#endif - -#else -#undef ON_HAS_RVALUEREF -#undef ON_NOEXCEPT - -#endif - -// You may need to define __GXX_EXPERIMENTAL_CXX0X__ to get -// C++11 std::shared_ptr to work as you expect when using -// the Android NDK gcc 4.7. See -// http://stackoverflow.com/questions/14532057/smart-pointers-not-working-with-android-ndk-r8 -// for more details. -// -//#define __GXX_EXPERIMENTAL_CXX0X__ - #elif defined(__GNUG_) || defined(__GNUG__) || defined(__GNUC_) || defined(__GNUC__) || defined(_GNU_SOURCE) || defined(__GNU_SOURCE) /* //////////////////////////////////////////////////////////// diff --git a/opennurbs_textlog.cpp b/opennurbs_textlog.cpp index 28eec8f9..842352ae 100644 --- a/opennurbs_textlog.cpp +++ b/opennurbs_textlog.cpp @@ -780,39 +780,41 @@ void ON_TextLog::PrintPointList( int dim, bool is_rat, int count, int stride, co if ( count == 0 ) { Print( "%sEMPTY point list\n", preamble.Array() ); } - else if ( !P ) { + else if ( nullptr == P ) { Print( "%sNULL point list\n", preamble.Array() ); } - - for ( i = 0; i < count; i++ ) { - Print( "%s[%2d] %c", preamble.Array(), i, (is_rat) ? '[' : '(' ); - Print( static_cast< const char* >(m_double_format), P[0] ); - for ( j = 1; j < cvdim; j++ ) { - Print( ", "); - Print(static_cast< const char* >(m_double_format), P[j] ); - } - Print("%c", (is_rat) ? ']' : ')' ); - if ( is_rat ) - { - w = P[dim]; - if ( w != 0.0 ) - { - // print euclidean coordinates - w = 1.0/w; - x = w*P[0]; - Print( " = ("); - Print( static_cast< const char* >(m_double_format), x ); - for ( j = 1; j < dim; j++ ) - { - x = w*P[j]; - Print( ", "); - Print( static_cast< const char* >(m_double_format), x ); - } - Print(")"); + else + { + for (i = 0; i < count; i++) { + Print("%s[%2d] %c", preamble.Array(), i, (is_rat) ? '[' : '('); + Print(static_cast(m_double_format), P[0]); + for (j = 1; j < cvdim; j++) { + Print(", "); + Print(static_cast(m_double_format), P[j]); } + Print("%c", (is_rat) ? ']' : ')'); + if (is_rat) + { + w = P[dim]; + if (w != 0.0) + { + // print euclidean coordinates + w = 1.0 / w; + x = w * P[0]; + Print(" = ("); + Print(static_cast(m_double_format), x); + for (j = 1; j < dim; j++) + { + x = w * P[j]; + Print(", "); + Print(static_cast(m_double_format), x); + } + Print(")"); + } + } + Print("\n"); + P += stride; } - Print("\n"); - P += stride; } } diff --git a/opennurbs_unicode.h b/opennurbs_unicode.h index f2dfb2d0..80371228 100644 --- a/opennurbs_unicode.h +++ b/opennurbs_unicode.h @@ -324,6 +324,9 @@ enum ON_UnicodeCodePoint /// WARNING SIGN U+26A0 (⚠) ON_WarningSign = 0x26A0, + /// CHECK MARK U+2713 (✓) + ON_CheckMark = 0x2713, + /// /// REPLACEMENT CHARACTER U+FFFD (�) /// By convention, U+FFFD is used to mark string elements where diff --git a/opennurbs_xform.cpp b/opennurbs_xform.cpp index 55c001d3..7688d9fd 100644 --- a/opennurbs_xform.cpp +++ b/opennurbs_xform.cpp @@ -383,13 +383,13 @@ const ON_Xform ON_Xform::DiagonalTransformation( #if defined(ON_COMPILER_MSC) ON_Xform::ON_Xform( double m[4][4] ) { - memcpy( &m_xform[0][0], &m[0][0], sizeof(m_xform) ); + memcpy( m_xform, m, sizeof(m_xform) ); } #endif ON_Xform::ON_Xform( const double m[4][4] ) { - memcpy( &m_xform[0][0], &m[0][0], sizeof(m_xform) ); + memcpy( m_xform, m, sizeof(m_xform) ); } #if defined(ON_COMPILER_MSC) @@ -442,7 +442,7 @@ ON_Xform::ON_Xform( const float m[4][4] ) ON_Xform::ON_Xform( const double* m ) { - memcpy( &m_xform[0][0], m, sizeof(m_xform) ); + memcpy( m_xform, m, sizeof(m_xform) ); } ON_Xform::ON_Xform( const float* m )