diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..32079c6f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig is awesome:http://EditorConfig.org + +# ignore .editorconfig files above this +root = true + +[*] +charset = utf-8 + +# source code +[*.{cpp,h,c}] +end_of_line = lf +indent_style = space +indent_size = 2 + +# windows-specific project files +[*.{vcxproj,sln,vcxproj.filters,props}] +charset = utf-8-bom diff --git a/example_convert/example_convert.cpp b/example_convert/example_convert.cpp index 9e03ebe6..12dbb35c 100644 --- a/example_convert/example_convert.cpp +++ b/example_convert/example_convert.cpp @@ -30,13 +30,13 @@ static bool HasErrorsOrWarnings(ON_TextLog* log, const char* operation) ON_String msg; if (ON_GetErrorCount() > 0) { - msg.Format("%d errors: %s", ON_GetErrorCount(), operation); + msg.Format("%d errors: %s\n", ON_GetErrorCount(), operation); log->Print(msg); return true; } if (ON_GetWarningCount() > 0) { - msg.Format("%d warnings: %s", ON_GetErrorCount(), operation); + msg.Format("%d warnings: %s\n", ON_GetErrorCount(), operation); log->Print(msg); return true; } @@ -162,7 +162,7 @@ int main(int argc, const char *argv[]) // Write file model.m_sStartSectionComments = "Converted by example_convert.exe"; bool outrc = model.Write(output, version, dump); - if (HasErrorsOrWarnings(dump, "writing output file")) + if (HasErrorsOrWarnings(dump, "writing output file\n")) return 1; if (outrc) diff --git a/example_test/example_test.cpp b/example_test/example_test.cpp index f2e2d638..c7c3a287 100644 --- a/example_test/example_test.cpp +++ b/example_test/example_test.cpp @@ -564,6 +564,8 @@ static ON_String Internal_PlatformId(bool bVerbose) "Apple" #elif defined(ON_RUNTIME_ANDROID) "Android" +#elif defined(ON_RUNTIME_LINUX) + "Linux" #else "Runtime" #endif diff --git a/example_test/example_test.vcxproj b/example_test/example_test.vcxproj index 6399073f..902b2161 100644 --- a/example_test/example_test.vcxproj +++ b/example_test/example_test.vcxproj @@ -23,7 +23,6 @@ {865E8D8D-8E03-4601-A7B5-A8C4094ECB8B} Win32Proj exampletest - 10.0.16299.0 diff --git a/makefile b/makefile index 753fc03b..3a48e468 100644 --- a/makefile +++ b/makefile @@ -368,6 +368,7 @@ ON_SRC = opennurbs_3dm_attributes.cpp \ opennurbs_subd_ref.cpp \ opennurbs_subd_ring.cpp \ opennurbs_subd_sector.cpp \ + opennurbs_subd_texture.cpp \ opennurbs_sum.cpp \ opennurbs_sumsurface.cpp \ opennurbs_surface.cpp \ @@ -543,6 +544,7 @@ ON_OBJ = opennurbs_3dm_attributes.o \ opennurbs_subd_ref.o \ opennurbs_subd_ring.o \ opennurbs_subd_sector.o \ + opennurbs_subd_texture.o \ opennurbs_sum.o \ opennurbs_sumsurface.o \ opennurbs_surface.o \ diff --git a/opennurbs.additional.manifest b/opennurbs.additional.manifest new file mode 100644 index 00000000..4fd0be92 --- /dev/null +++ b/opennurbs.additional.manifest @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/opennurbs.h b/opennurbs.h index 73b9d744..e91c76f3 100644 --- a/opennurbs.h +++ b/opennurbs.h @@ -88,6 +88,7 @@ #include "opennurbs_sphere.h" // simple 3d sphere #include "opennurbs_box.h" // simple 3d box #include "opennurbs_torus.h" // simple 3d torus +#include "opennurbs_convex_poly.h" // simple 3d simplex and 3d convex polyhedra #include "opennurbs_bezier.h" // simple bezier and polynomial curves and surfaces #include "opennurbs_math.h" // utilities for performing simple calculations #include "opennurbs_intersect.h" // utilities for performing simple intersections @@ -119,7 +120,9 @@ #include "opennurbs_pointcloud.h" // point set #include "opennurbs_curveproxy.h" // proxy curve provides a way to use an existing curve #include "opennurbs_surfaceproxy.h" // proxy surface provides a way to use another surface -#include "opennurbs_mesh.h" // render mesh object +#include "opennurbs_mesh.h" // mesh object + + #include "opennurbs_pointgrid.h" // point grid object #include "opennurbs_linecurve.h" // line as a paramtric curve object #include "opennurbs_arccurve.h" // arc/circle as a paramtric curve object diff --git a/opennurbs_3dm_properties.cpp b/opennurbs_3dm_properties.cpp index 899d6e58..f475fb38 100644 --- a/opennurbs_3dm_properties.cpp +++ b/opennurbs_3dm_properties.cpp @@ -514,7 +514,7 @@ bool ON_3dmProperties::Write(ON_BinaryArchive& file) const = file.ArchiveFullPath().IsEmpty() ? m_3dmArchiveFullPathName : file.ArchiveFullPath(); - if (archive_full_path.IsNotEmpty()) + if (rc && archive_full_path.IsNotEmpty()) { if (!file.BeginWrite3dmChunk(TCODE_PROPERTIES_AS_FILE_NAME, 0)) return false; diff --git a/opennurbs_annotationbase.cpp b/opennurbs_annotationbase.cpp index c39acf10..7a197890 100644 --- a/opennurbs_annotationbase.cpp +++ b/opennurbs_annotationbase.cpp @@ -24,6 +24,7 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif +#include "opennurbs_textiterator.h" ON_VIRTUAL_OBJECT_IMPLEMENT(ON_Annotation, ON_Geometry, "B5802E0C-5B16-43C9-BD43-A3E0AC18203B"); ON::AnnotationType ON::AnnotationTypeFromUnsigned( @@ -589,22 +590,113 @@ ON_3dVector ON_Annotation::GetDefaultHorizontal(const ON_Plane& plane) return ON_3dVector::XAxis; } +void ON_Annotation::CalcTextFlip( + const ON_3dVector& text_xdir, const ON_3dVector& text_ydir, const ON_3dVector& text_zdir, + const ON_3dVector& view_xdir, const ON_3dVector& view_ydir, const ON_3dVector& view_zdir, + const ON_Xform* model_xform, + const double fliptol, + bool& flip_x, + bool& flip_y) +{ + + double XoX = text_xdir * view_xdir; + double XoY = text_xdir * view_ydir; + double YoX = text_ydir * view_xdir; + double YoY = text_ydir * view_ydir; + bool from_the_back = (view_zdir * text_zdir < 0.0); + if (nullptr != model_xform && model_xform->Determinant() < 0.0) + from_the_back = !from_the_back; + + double upsign = 1.0; + + // This part shifts text to the correct side of the dimension line + if (fabs(XoX) > fabs(XoY)) // more horizontal + { + if (YoY > 0.0) + upsign = 1.0; + else + upsign = -1.0; + } + else // more vertical + { + if (from_the_back) + { + if (YoX < 0.0) + { + if (XoX < fliptol) + upsign = 1.0; + else + upsign = -1.0; + } + else + { + if (XoX > -fliptol) + upsign = -1.0; + else + upsign = 1.0; + } + } + else + { + if (YoX > 0.0) + { + if (XoX > fliptol) + upsign = 1.0; + else + upsign = -1.0; + } + else + { + if (XoX < -fliptol) + upsign = -1.0; + else + upsign = 1.0; + } + } + } + flip_x = false; + flip_y = false; + if (from_the_back) + upsign = -upsign; + flip_x = upsign < 0.0; + if (from_the_back) + flip_y = !flip_x; + else + flip_y = flip_x; +} + + const ON_wString ON_Annotation::PlainText() const { - return (nullptr == m_text) - ? ON_wString::EmptyString - : m_text->PlainText(); + if (nullptr == m_text) + return ON_wString::EmptyString; + const ON_TextRunArray* runs = m_text->TextRuns(true); + if (nullptr != runs && 0 == runs->Count()) + BoundingBox(); // Side effect of building text runs + return m_text->PlainText(); } const ON_wString ON_Annotation::PlainTextWithFields() const { - const ON_TextContent* text = Text(); - - return (nullptr == text) - ? ON_wString::EmptyString - : text->PlainTextWithFields(); + if (nullptr == m_text) + return ON_wString::EmptyString; + const ON_TextRunArray* runs = m_text->TextRuns(true); + if (nullptr != runs && 0 == runs->Count()) + BoundingBox(); + return m_text->PlainTextWithFields(); } +const ON_wString ON_Annotation::PlainTextWithFields(ON_SimpleArray* runmap) const +{ + if (nullptr == m_text) + return ON_wString::EmptyString; + const ON_TextRunArray* runs = m_text->TextRuns(true); + if (nullptr != runs && 0 == runs->Count()) + BoundingBox(); + return m_text->PlainTextWithFields(runmap); +} + + const ON_wString ON_Annotation::RichText() const { return (nullptr == m_text) @@ -1250,6 +1342,29 @@ bool ON_Annotation::ReplaceTextString( return text->ReplaceTextString(RtfString, Type(), dimstyle); } + +bool ON_Annotation::RunReplaceString( + const ON_DimStyle* dimstyle, + const wchar_t* repl_str, + int start_run_idx, + int start_run_pos, + int end_run_idx, + int end_run_pos) +{ + ON_TextContent* text_content = Text(); + if (nullptr == text_content) + return false; + bool rc = text_content->RunReplaceString(repl_str, start_run_idx, start_run_pos, end_run_idx, end_run_pos); + + text_content->ComposeText(); + + text_content->RebuildRuns(Type(), dimstyle); + + + return rc; +} + + ////double ON_Annotation::Height() const ////{ //// //// Delete this function ASAP @@ -2823,6 +2938,23 @@ void ON_Annotation::SetDimScale(const ON_DimStyle* parent_style, double value) } } +wchar_t ON_Annotation::DecimalSeparator(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::DecimalSeparator).DecimalSeparator(); +} + +void ON_Annotation::SetDecimalSeparator(const ON_DimStyle* parent_style, wchar_t separator) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (separator != parent_style->DecimalSeparator()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetDecimalSeparator(separator); + override_style->SetFieldOverride(ON_DimStyle::field::DecimalSeparator, bCreate); + } +} + const class ON_Font& ON_Annotation::Font(const ON_DimStyle* parent_style) const { return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Font).Font(); @@ -2909,7 +3041,6 @@ ON::LengthUnitSystem ON_Annotation::AlternateDimensionLengthDisplayUnit( model_sn = parent_style->ModelSerialNumber(); return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::AlternateDimensionLengthDisplay).DimensionLengthDisplayUnit(model_sn); } - //-------------------------------- bool ON_Annotation::ClearRtfFmt(const wchar_t* fmt_str_on, const wchar_t* fmt_str_off, ON_wString& rtf_in) diff --git a/opennurbs_annotationbase.h b/opennurbs_annotationbase.h index 189fa3e8..c299d608 100644 --- a/opennurbs_annotationbase.h +++ b/opennurbs_annotationbase.h @@ -183,6 +183,16 @@ public: // Use this function when you don't have a known horizontal direction static ON_3dVector GetDefaultHorizontal(const ON_Plane& plane); + + static void CalcTextFlip( + const ON_3dVector& text_xdir, const ON_3dVector& text_ydir, const ON_3dVector& text_zdir, + const ON_3dVector& view_xdir, const ON_3dVector& view_ydir, const ON_3dVector& view_zdir, + const ON_Xform* model_xform, + const double flip_tol, + bool& flip_x, + bool& flip_y); + + /* Returns: Rich text that can contain rich text formatting instructions. @@ -204,6 +214,18 @@ public: */ const ON_wString PlainTextWithFields() const; + /* + Finds the positions and lengths of substrings in the string returned by PlainTextWithFields() + That string is the plain text (no rtf formatting) with field source unevaluated + Each 3dex in the array is + i: run index, + j: position in the string where text from run[i] starts, + k: length of text from run[i] + + Returns the same string that PlainTextWithFields() returns + */ + const ON_wString PlainTextWithFields(ON_SimpleArray* runmap) const; + // Return the id of the main (parent) dimstyle used by this object. // The style with this id should not be used directly if there is // an override dimstyle present. @@ -377,6 +399,15 @@ public: const ON_DimStyle* dimstyle ); + bool RunReplaceString( + const ON_DimStyle* dimstyle, + const wchar_t* str, + int start_run_idx, + int start_run_pos, + int end_run_idx, + int end_run_pos); + + void GetAlignment(ON::TextHorizontalAlignment& horz, ON::TextVerticalAlignment& vert) const; void SetAlignment(ON::TextHorizontalAlignment horz, ON::TextVerticalAlignment vert); @@ -746,6 +777,9 @@ public: double DimScale(const ON_DimStyle* parent_style) const; void SetDimScale(const ON_DimStyle* parent_style, double scale); + wchar_t DecimalSeparator(const ON_DimStyle* parent_style) const; + void SetDecimalSeparator(const ON_DimStyle* parent_style, wchar_t separator); + ON_DimStyle::LengthDisplay DimensionLengthDisplay(const ON_DimStyle* parent_style) const; void SetDimensionLengthDisplay(const ON_DimStyle* parent_style, ON_DimStyle::LengthDisplay length_display); @@ -842,6 +876,8 @@ public: static bool FirstCharTextProperties(const wchar_t* rtf_in, bool& bold, bool& italic, bool& underline, ON_wString& facename); const ON_Font* FirstCharFont() const; + + friend class ON_Dimension; }; diff --git a/opennurbs_apple_nsfont.cpp b/opennurbs_apple_nsfont.cpp index 2e763f1f..34954018 100644 --- a/opennurbs_apple_nsfont.cpp +++ b/opennurbs_apple_nsfont.cpp @@ -184,9 +184,8 @@ bool ON_Font::SetFromAppleCTFont(CTFontRef apple_font, bool bAnnotationFont) const ON_wString face_name = ON_Font::AppleCTFontFaceName(apple_font); // Set Windows LOGFONT.lfFaceName to something not empty there is some hope this - // font might work in Rhino 5 for Windows too. - // https://mcneel.myjetbrains.com/youtrack/issue/RH-37074 - const ON_wString windows_logfont_name = family_name; + // font might work in Rhino 6/7 for Windows too. + const ON_wString windows_logfont_name = ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames(family_name, postscript_name); const bool rc = postscript_name.IsNotEmpty() || family_name.IsNotEmpty(); if (rc) @@ -259,7 +258,7 @@ bool ON_Font::SetFromAppleCTFont(CTFontRef apple_font, bool bAnnotationFont) if (nullptr != appleWidth) { - double x = apple_font_weight_trait; + double x = apple_font_width_trait; if ( CFNumberGetValue(appleWidth, kCFNumberFloat64Type, &x) && -1.0 <= x && x <= 1.0 ) { // Use the kCTFontWidthTrait key to access the normalized proportion trait from the font traits dictionary. @@ -321,6 +320,14 @@ bool ON_Font::SetFromAppleCTFont(CTFontRef apple_font, bool bAnnotationFont) m_panose1 = ON_Font::AppleCTFontPANOSE1(apple_font); SetFontOrigin(ON_Font::Origin::AppleFont); + + if (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0) + m_apple_font_weight_trait = apple_font_weight_trait; + + if (-1.0 <= apple_font_width_trait && apple_font_width_trait <= 1.0) + m_apple_font_width_trait = apple_font_width_trait; + else + m_apple_font_width_trait = ON_UNSET_VALUE; } return rc; diff --git a/opennurbs_archive.cpp b/opennurbs_archive.cpp index efe62154..4c1cf582 100644 --- a/opennurbs_archive.cpp +++ b/opennurbs_archive.cpp @@ -1187,6 +1187,34 @@ ON_BinaryArchive::ReadColor( ON_Color& color ) return rc; } +bool +ON_BinaryArchive::ReadColor(ON_4fColor& color) +{ + float f = 0.f; + + bool rc = ReadFloat(&f); + if (rc) + { + color.SetRed(f); + rc = ReadFloat(&f); + } + if (rc) + { + color.SetGreen(f); + rc = ReadFloat(&f); + } + if (rc) + { + color.SetBlue(f); + rc = ReadFloat(&f); + } + if (rc) + { + color.SetAlpha(f); + } + return rc; +} + bool ON_BinaryArchive::ReadPoint ( ON_2dPoint& p @@ -3202,6 +3230,16 @@ ON_BinaryArchive::WriteColor( const ON_Color& color ) return WriteByte( 4, (const unsigned char*)&colorref ); // WriteByte prevents big endian swaps } +bool +ON_BinaryArchive::WriteColor(const ON_4fColor& color) +{ + if (!WriteFloat(color.Red())) return false; + if (!WriteFloat(color.Green())) return false; + if (!WriteFloat(color.Blue())) return false; + if (!WriteFloat(color.Alpha())) return false; + return true; +} + bool ON_BinaryArchive::WritePoint ( const ON_2dPoint& p @@ -7065,15 +7103,6 @@ bool ON_BinaryArchive::Save3dmPreviewImage() const return m_bSave3dmPreviewImage; } - - -int ON_BinaryArchive::CurrentArchiveVersion() -{ - // Latest version of opennurbs binary archives supported by - // this version of opennurbs. - return ON::VersionMajor()*10; // *10 means 64-bit chunk values -} - void ON_BinaryArchive::SetModelSerialNumber( unsigned int model_serial_number, unsigned int reference_model_serial_number, @@ -7211,7 +7240,7 @@ bool ON_BinaryArchive::Begin3dmTable( if ( table == ON_3dmArchiveTableType::start_section ) { // m_3dm_version is set during reading of the start section. - if (0 != m_3dm_version) + if (0 != m_3dm_version && ON::archive_mode::read3dm == Mode()) { ON_ERROR("Archive m_3dm_version is set during start section reading."); return End3dmTable(table,false); @@ -7498,7 +7527,6 @@ bool ON_BinaryArchive::Write3dmStartSection( int version, const char* sInformati return End3dmTable(ON_3dmArchiveTableType::start_section,false); } - m_crc_error_count = 0; m_critical_error_count = 0; m_3dm_version = version; @@ -16541,7 +16569,7 @@ ON_BinaryArchive::SetArchive3dmVersion(int v) // 5 was used for early V5 betas with 4 byte chunk lengths // 50 is used for V5 files with 8 bytes chunk lengths. // 60, 70, ... will be used for Rhino V6, V7, etc. - if ( (v >= 1 && v <= 5) || ( v >= 50 && 0 == (v % 10) ) ) + if ( (v >= 1 && v <= 5) || ( v >= 50 && 0 == (v % 10) && v <= ON_BinaryArchive::CurrentArchiveVersion() ) ) { m_3dm_version = v; rc = true; @@ -16970,8 +16998,13 @@ bool ON_WriteMultipleObjectArchive( model.AddManagedModelComponent( mg, bResolveIdAndNameConflicts ); } - if (version < ON_BinaryArchive::CurrentArchiveVersion()-1 || version > ON_BinaryArchive::CurrentArchiveVersion()) + if ( (0 != version % 10) + || version < ON_BinaryArchive::CurrentArchiveVersion() - 10 + || version > ON_BinaryArchive::CurrentArchiveVersion() + ) + { version = ON_BinaryArchive::CurrentArchiveVersion(); + } model.m_sStartSectionComments = "Archive created by ON_WriteMultipleObjectArchive"; bool rc = model.Write(archive, version ); diff --git a/opennurbs_archive.h b/opennurbs_archive.h index 4fa351e4..59a386e9 100644 --- a/opennurbs_archive.h +++ b/opennurbs_archive.h @@ -2216,6 +2216,10 @@ public: ON_Color& ); + bool ReadColor( + ON_4fColor& + ); + bool ReadPoint ( ON_2dPoint& ); @@ -2512,6 +2516,10 @@ public: const ON_Color& ); + bool WriteColor( + const ON_4fColor& + ); + bool WritePoint ( const ON_2dPoint& ); @@ -3030,13 +3038,12 @@ public: ) const; /* + Remarks: + In a stable commercially released Rhino version N, CurrentArchiveVersion() = 10*N. + In "early" Rhino N WIP, CurrentArchiveVersion() = 10*(N-1). + In "later" Rhino N WIP, CurrentArchiveVersion() = 10*N. Returns: - 10*OPENNURBS_VERSION_MAJOR - (The Rhino opennurbs file version.) - This is the value of version to pass to ON_BinaryArchive - functions like Write3dmStartSection() when you want to use the - the current opennurbs version number and you do not want to have - to update your code when this version number changes. + The current 3dm archive version that is saved by Rhino. */ static int CurrentArchiveVersion(); diff --git a/opennurbs_array.h b/opennurbs_array.h index dae53784..577c492e 100644 --- a/opennurbs_array.h +++ b/opennurbs_array.h @@ -211,6 +211,11 @@ public: // See Also: ON_CompareIncreasing and ON_CompareDeccreasing bool QuickSort( int (*)(const T*,const T*) ); + ////////// + // Sorts the array using the quick sort algorithma and then removes duplicates. + // See Also: ON_CompareIncreasing and ON_CompareDeccreasing + bool QuickSortAndRemoveDuplicates( int (*)(const T*,const T*) ); + /* Description: Sort() fills in the index[] array so that diff --git a/opennurbs_array_defs.h b/opennurbs_array_defs.h index 320b0891..a77a4e2d 100644 --- a/opennurbs_array_defs.h +++ b/opennurbs_array_defs.h @@ -782,6 +782,36 @@ bool ON_SimpleArray::QuickSort( int (*compar)(const T*,const T*) ) return rc; } +template +bool ON_SimpleArray::QuickSortAndRemoveDuplicates( int (*compar)(const T*,const T*) ) +{ + bool rc = false; + if ( m_a && m_count > 0 && compar ) + { + if (m_count > 1) + { + ON_qsort(m_a, m_count, sizeof(T), (int(*)(const void*, const void*))compar); + const T* prev_ele = &m_a[0]; + int clean_count = 1; + for (int i = 1; i < m_count; ++i) + { + if (0 == compar(prev_ele, &m_a[i])) + continue; // duplicate + if (i > clean_count) + m_a[clean_count] = m_a[i]; + ++clean_count; + } + if (clean_count < m_count) + { + memset( (void*)(&m_a[clean_count]), 0, (m_count-clean_count)*sizeof(T) ); + SetCount(clean_count); + } + } + rc = true; + } + return rc; +} + template bool ON_SimpleArray::Sort( ON::sort_algorithm sa, int* index, int (*compar)(const T*,const T*) ) const { @@ -1154,6 +1184,11 @@ ON_ClassArray& ON_ClassArray::operator=( ON_ClassArray&& src ) ON_NOEXC { if( this != &src ) { + // TODO - investigate why we should use std::move(src) + // instead of the code below + //ON_ClassArray::operator=(std::move(src)); + // Then investigate why the change was requested only for class array. + // What about the other dynamic array classes? this->Destroy(); m_a = src.m_a; m_count = src.m_count; diff --git a/opennurbs_bounding_box.cpp b/opennurbs_bounding_box.cpp index 29571e21..ce0651d2 100644 --- a/opennurbs_bounding_box.cpp +++ b/opennurbs_bounding_box.cpp @@ -991,16 +991,13 @@ unsigned int ON_ClippingRegion::TransformPoint( if (y < -w) out |= 0x04; else if (y > w) out |= 0x08; z = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11]; if (z < -w) out |= 0x10; else if (z > w) out |= 0x20; - if ( w <= 0.0 ) + if ( false == (w > 0.0) ) { - w = (0.0==w) ? 1.0 : 1.0/w; + if (0.0 == w || false == ON_IsValid(w)) + w = 1.0; out |= 0x80000000; } - else - { - w = 1.0/w; - } - Q.x = x*w; Q.y = y*w; Q.z = z*w; + Q.x = x/w; Q.y = y/w; Q.z = z/w; return out; } @@ -1413,6 +1410,7 @@ bool ON_ClippingRegionPoints::AppendClipPoints( return true; } + bool ON_ClippingRegionPoints::AppendClipPoint( ON_3dPoint clip_point, unsigned int clip_flag diff --git a/opennurbs_brep.h b/opennurbs_brep.h index 078b4c76..0dc12c80 100644 --- a/opennurbs_brep.h +++ b/opennurbs_brep.h @@ -118,6 +118,7 @@ public: // number of edges that begin or end at this vertex. int EdgeCount() const; + ///////////////////////////////////////////////////////////////// // Implementation @@ -332,6 +333,7 @@ public: */ void UnsetPlineEdgeParameters(); + // index of 3d curve in m_C3[] array // (edge.m_curve also points to m_C3[m_c3i]) int m_c3i = -1; @@ -1144,6 +1146,7 @@ public: const ON_Surface* SurfaceOf() const; + ON_SimpleArray m_li; // loop indices (outer loop is m_li[0]) int m_si = -1; // index of surface in b-rep m_S[] array bool m_bRev = false; // true if face orientation is opposite diff --git a/opennurbs_brep_tools.cpp b/opennurbs_brep_tools.cpp index aa714bff..5789cb77 100644 --- a/opennurbs_brep_tools.cpp +++ b/opennurbs_brep_tools.cpp @@ -408,6 +408,9 @@ void ON_Brep::SetTolerancesBoxesAndFlags( static bool CheckForMatchingVertexIndices( int i, int j, int corner_vi[4] ) { + if (corner_vi[i] == corner_vi[j]) + return true; + bool rc = false; if ( corner_vi[i] >= 0 || corner_vi[j] >= 0 ) { @@ -421,12 +424,9 @@ bool CheckForMatchingVertexIndices( int i, int j, int corner_vi[4] ) corner_vi[j] = corner_vi[i]; rc = true; } - else if ( corner_vi[i] == corner_vi[j] ) - { - rc = true; - } } - return true; + + return rc; } @@ -3274,6 +3274,7 @@ static bool ON_BrepRemoveSlits(ON_BrepLoop& L) } } } + pB->MatchTrimEnds(nL); pB->SetTrimBoundingBoxes(nL, true); } return true; diff --git a/opennurbs_color.cpp b/opennurbs_color.cpp index eaf71d5a..62d8de8b 100644 --- a/opennurbs_color.cpp +++ b/opennurbs_color.cpp @@ -8,7 +8,7 @@ // 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 . // //////////////////////////////////////////////////////////////// @@ -19,16 +19,16 @@ #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 +// 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 ON_Color::ON_Color(unsigned int colorref) - : m_color(colorref) + : m_color(colorref) { - // No adjustments are required on big endian computers because all - // unsigned int conversion and all IO preserves the order of the + // No adjustments are required on big endian computers because all + // unsigned int conversion and all IO preserves the order of the // ON_Color::m_RGBA[4] bytes. } @@ -50,8 +50,8 @@ unsigned int ON_Color::WindowsRGB() const ON_Color::operator unsigned int() const { - // No adjustments are required on big endian computers because all - // unsigned int conversion and all IO preserves the order of the + // No adjustments are required on big endian computers because all + // unsigned int conversion and all IO preserves the order of the // ON_Color::m_RGBA[4] bytes. return m_color; } @@ -84,25 +84,25 @@ int ON_Color::Alpha() const { return m_RGBA[ON_Color::kAlphaByteIndex];} double ON_Color::FractionRed() const -{ +{ //return Red()/255.0; return m_RGBA[ON_Color::kRedByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer } -double ON_Color::FractionGreen() const -{ +double ON_Color::FractionGreen() const +{ //return Green()/255.0; return m_RGBA[ON_Color::kGreenByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer } -double ON_Color::FractionBlue() const -{ +double ON_Color::FractionBlue() const +{ //return Blue()/255.0; return m_RGBA[ON_Color::kBlueByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer } -double ON_Color::FractionAlpha() const -{ +double ON_Color::FractionAlpha() const +{ //return Alpha()/255.0; return m_RGBA[ON_Color::kAlphaByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer } @@ -119,23 +119,23 @@ void ON_Color::SetFractionalRGB(double r,double g,double b) void ON_Color::SetAlpha(int alpha) { - if (alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255; + if (alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255; m_RGBA[ON_Color::kAlphaByteIndex] = (unsigned char)alpha; } void ON_Color::SetFractionalAlpha(double alpha) { - if (alpha < 0.0 ) alpha = 0.0; else if ( alpha > 1.0 ) alpha = 1.0; + if (alpha < 0.0 ) alpha = 0.0; else if ( alpha > 1.0 ) alpha = 1.0; SetAlpha((int)(alpha*255.0)); } void ON_Color::SetRGBA( int red, int green, int blue, int alpha ) { - if (red < 0 ) red = 0; else if ( red > 255 ) red = 255; - if (green < 0 ) green = 0; else if ( green > 255 ) green = 255; - if (blue < 0 ) blue = 0; else if ( blue > 255 ) blue = 255; - if (alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255; + if (red < 0 ) red = 0; else if ( red > 255 ) red = 255; + if (green < 0 ) green = 0; else if ( green > 255 ) green = 255; + if (blue < 0 ) blue = 0; else if ( blue > 255 ) blue = 255; + if (alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255; m_RGBA[ON_Color::kRedByteIndex] = (unsigned char)red; m_RGBA[ON_Color::kGreenByteIndex] = (unsigned char)green; m_RGBA[ON_Color::kBlueByteIndex] = (unsigned char)blue; @@ -146,9 +146,9 @@ void ON_Color::SetFractionalRGBA( double red, double green, double blue, double alpha ) { int r,g,b,a; - if (red < 0.0 ) red = 0.0; else if ( red > 1.0 ) red = 1.0; - if (green < 0.0 ) green = 0.0; else if ( green > 1.0 ) green = 1.0; - if (blue < 0.0 ) blue = 0.0; else if ( blue > 1.0 ) blue = 1.0; + if (red < 0.0 ) red = 0.0; else if ( red > 1.0 ) red = 1.0; + if (green < 0.0 ) green = 0.0; else if ( green > 1.0 ) green = 1.0; + if (blue < 0.0 ) blue = 0.0; else if ( blue > 1.0 ) blue = 1.0; if (alpha < 0.0 ) alpha = 0.0; else if ( alpha > 1.0 ) alpha = 1.0; red *= 255.0; @@ -172,8 +172,8 @@ ON_Color::SetFractionalRGBA( double red, double green, double blue, double alpha double ON_Color::Hue() const { - // returns 0 to 2*pi - // 0 = red, pi/3 = yellow, 2*pi/3 = green, + // returns 0 to 2*pi + // 0 = red, pi/3 = yellow, 2*pi/3 = green, // pi = cyan, 4*pi/3 = blue, 5*pi/3 = magenta, // 2*pi = red double h; @@ -192,7 +192,7 @@ double ON_Color::Hue() const } else if ( g == maxrgb) h = 2.0 + (b - r)*d; - else + else h = 4.0 + (r - g)*d; h *= ON_PI/3.0; } @@ -229,10 +229,10 @@ double ON_Color::Value() const return (maxrgb/255.0); } -void ON_Color::SetHSV( - double hue, // hue in radians +void ON_Color::SetHSV( + double hue, // hue in radians double saturation, // satuation 0.0 = gray, 1.0 = saturated - double value // value + double value // value ) { int i; @@ -250,15 +250,15 @@ void ON_Color::SetHSV( if ( hue < 0.0 ) hue += 6.0; i = (int)floor(hue); - } - f = hue - i; + } + f = hue - i; p = value * ( 1.0 - saturation); q = value * ( 1.0 - ( saturation * f) ); t = value * ( 1.0 - ( saturation * ( 1.0 - f) ) ); switch( i) { case 0: - r = value; g = t; b = p; break; + r = value; g = t; b = p; break; case 1: r = q; g = value; b = p; break; case 2: @@ -268,11 +268,170 @@ void ON_Color::SetHSV( case 4: r = t; g = p; b = value; break; case 5: - r = value; g = p; b = q; break; + r = value; g = p; b = q; break; default: r = 0; g = 0; b = 0; break; // to keep lint quiet - } + } } SetFractionalRGB(r,g,b); } +ON_ColorStop::ON_ColorStop(const ON_Color& color, double position) + : m_color(color) + , m_position(position) +{ +} + +ON_4fColor::ON_4fColor() +{ + for (int i = 0; i < 4; i++) + { + m_color[i] = ON_UNSET_FLOAT; + } +} + +//Note that these function will set the alpha correctly from ON_Colors "inverted" alpha. +ON_4fColor::ON_4fColor(const ON_Color& in) +{ + *this = in; +} + +ON_4fColor& ON_4fColor::operator=(const ON_Color& in) +{ + SetRed((float)in.FractionRed()); + SetGreen((float)in.FractionGreen()); + SetBlue((float)in.FractionBlue()); + SetAlpha(1.0f - (float)in.FractionAlpha()); + return *this; +} + +//Will invert the opacity alpha to transparency. +ON_4fColor::operator ON_Color(void) const +{ + ON_Color out; + out.SetFractionalRGBA(Red(), Green(), Blue(), 1.0 - Alpha()); + return out; +} + +float ON_4fColor::Red(void) const { return m_color[0];} +void ON_4fColor::SetRed(float f) { m_color[0] = f;} + +float ON_4fColor::Green(void) const { return m_color[1];} +void ON_4fColor::SetGreen(float f) { m_color[1] = f;} + +float ON_4fColor::Blue(void) const { return m_color[2]; } +void ON_4fColor::SetBlue(float f) { m_color[2] = f; } + +float ON_4fColor::Alpha(void) const { return m_color[3]; } +void ON_4fColor::SetAlpha(float f) { m_color[3] = f; } + +void ON_4fColor::SetRGBA(float r, float g, float b, float a) +{ + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; + m_color[3] = a; +} + +bool ON_4fColor::IsValid(class ON_TextLog* text_log) const +{ + for (int i = 0; i < 4; i++) + { + if (ON_IS_UNSET_FLOAT(m_color[i])) + return false; + } + return true; +} + + + +bool ON_ColorStop::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0); + if (!rc) + return false; + + for (;;) + { + rc = archive.WriteColor(m_color); + if (!rc) break; + rc = archive.WriteDouble(m_position); + if (!rc) break; + + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_ColorStop::Read(ON_BinaryArchive& file) +{ + int major_version = 0; + int minor_version = 0; + bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version); + if (!rc) + return false; + for (;;) + { + rc = (1 == major_version); + if (!rc) break; + rc = file.ReadColor(m_color); + if (!rc) break; + rc = file.ReadDouble(&m_position); + if (!rc) break; + + break; + } + if (!file.EndRead3dmChunk()) + rc = false; + + return rc; +} + + +static int CompareNans(double a, double b) +{ + if (a == a) + { + if (b == b) + { + return ((a < b) ? -1 : ((a > b) ? 1 : 0)); + } + // a is not a NAN, b is a NAN + return -1; // a < b - NAN's are last + } + else if (b == b) + { + // a is a NAN, b is not a NAN + return -1; // b < a - NAN's are last + } + return 0; // a and b are both NaNs +} + +static int CompareDouble(double a, double b) +{ + return ((a < b) ? -1 : ((a > b) ? 1 : (a == b ? 0 : CompareNans(a, b)))); +} + +// < 0 if this < arg, 0 ir this==arg, > 0 if this > arg +int ON_4fColor::Compare(const ON_4fColor& b) const +{ + int rc = CompareDouble(Red(), b.Red()); + if (0 != rc) + return rc; + + rc = CompareDouble(Green(), b.Green()); + if (0 != rc) + return rc; + + rc = CompareDouble(Blue(), b.Blue()); + if (0 != rc) + return rc; + + rc = CompareDouble(Alpha(), b.Alpha()); + + return rc; +} diff --git a/opennurbs_color.h b/opennurbs_color.h index 82b4436d..6f76a3c5 100644 --- a/opennurbs_color.h +++ b/opennurbs_color.h @@ -214,4 +214,74 @@ private: }; }; +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_ColorStop +// +// Combination of a color and a single value. Typically used for defining +// gradient fills over a series of colors. +class ON_CLASS ON_ColorStop +{ +public: + ON_ColorStop() = default; + ON_ColorStop(const ON_Color& color, double position); + + bool Write(class ON_BinaryArchive& archive) const; + bool Read(class ON_BinaryArchive& archive); + + ON_Color m_color = ON_Color::UnsetColor; + double m_position = 0; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; +#endif + + +class ON_CLASS ON_4fColor +{ +public: + ON_4fColor(); + ~ON_4fColor() = default; + ON_4fColor(const ON_4fColor&) = default; + ON_4fColor& operator=(const ON_4fColor&) = default; + + static const ON_4fColor Unset; + + //Note that these function will set the alpha correctly from ON_Colors "inverted" alpha. + ON_4fColor(const ON_Color&); + ON_4fColor& operator=(const ON_Color&); + + //Will invert the opacity alpha to transparency. + operator ON_Color(void) const; + + float Red(void) const; + void SetRed(float); + + float Green(void) const; + void SetGreen(float); + + float Blue(void) const; + void SetBlue(float); + + //Alpha in ON_4fColor is OPACITY - not transparency as in ON_Color. + float Alpha(void) const; + void SetAlpha(float); + + void SetRGBA(float r, float g, float b, float a); + + bool IsValid(class ON_TextLog* text_log = nullptr) const; + + // < 0 if this < arg, 0 ir this==arg, > 0 if this > arg + int Compare(const ON_4fColor&) const; + +private: + float m_color[4]; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; +#endif + + #endif diff --git a/opennurbs_compstat.cpp b/opennurbs_compstat.cpp index 4d5c8109..65fa7bbe 100644 --- a/opennurbs_compstat.cpp +++ b/opennurbs_compstat.cpp @@ -283,8 +283,12 @@ unsigned int ON_ComponentStatus::ClearStates( s1 &= ~HIGHLIGHTED_BIT; } + // preserve value of runtime mark bit on m_status_flags const unsigned char mark = (m_status_flags&RUNTIME_MARK_BIT); + + // change m_status_flags to new value m_status_flags = (s1|mark); + return 1; } @@ -388,33 +392,33 @@ bool ON_ComponentStatus::RuntimeMark() const } -unsigned int ON_ComponentStatus::SetRuntimeMark( +bool ON_ComponentStatus::SetRuntimeMark( bool bRuntimeMark ) { return bRuntimeMark ? SetRuntimeMark() : ClearRuntimeMark(); } -unsigned int ON_ComponentStatus::SetRuntimeMark() +bool ON_ComponentStatus::SetRuntimeMark() { const unsigned char c = (m_status_flags | RUNTIME_MARK_BIT); if (c != m_status_flags) { m_status_flags = c; - return 1; + return true; } - return 0; + return false; } -unsigned int ON_ComponentStatus::ClearRuntimeMark() +bool ON_ComponentStatus::ClearRuntimeMark() { const unsigned char c = (m_status_flags & ~RUNTIME_MARK_BIT); if (c != m_status_flags) { m_status_flags = c; - return 1; + return true; } - return 0; + return false; } diff --git a/opennurbs_compstat.h b/opennurbs_compstat.h index 50fe1bfa..80fd4d20 100644 --- a/opennurbs_compstat.h +++ b/opennurbs_compstat.h @@ -247,11 +247,26 @@ public: // RuntimeMark // bool RuntimeMark() const; - unsigned int SetRuntimeMark( + + /* + Returns: + Input value of RuntimeMark(); + */ + bool SetRuntimeMark( bool bRuntimeMark ); - unsigned int SetRuntimeMark(); - unsigned int ClearRuntimeMark(); + + /* + Returns: + Input value of RuntimeMark(); + */ + bool SetRuntimeMark(); + + /* + Returns: + Input value of RuntimeMark(); + */ + bool ClearRuntimeMark(); ////////////////////////////////////////////////////////////////////////// // diff --git a/opennurbs_convex_poly.cpp b/opennurbs_convex_poly.cpp new file mode 100644 index 00000000..84075397 --- /dev/null +++ b/opennurbs_convex_poly.cpp @@ -0,0 +1,1028 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see . +// +//////////////////////////////////////////////////////////////// +*/ + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +ON_3dSimplex::ON_3dSimplex() { m_n = 0; } + +/* this function checks the validity of ClosetPoint results*/ +bool ClosestPointIsValid(const ON_ConvexPoly& AHull, const ON_ConvexPoly& BHull, + ON_4dex Adex, ON_4dex Bdex, ON_4dPoint ABBary, double atmost, ON_TextLog* log = nullptr); + + +ON_3dSimplex::ON_3dSimplex(const ON_3dPoint& a) { m_n = 1; m_V[0] = a; } +ON_3dSimplex::ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b) { m_n = 2; m_V[0] = a; m_V[1] = b; } +ON_3dSimplex::ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c) { m_n = 3; m_V[0] = a; m_V[1] = b; m_V[2] = c; } +ON_3dSimplex::ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c, const ON_3dPoint& d) { m_n = 4; m_V[0] = a; m_V[1] = b; m_V[2] = c; m_V[3] = d; } + +int ON_3dSimplex::Count() const { return m_n; }; + +bool ON_3dSimplex::IsValid(double eps) const // true if the Verticies are affinely independent +{ + bool rc = true; + if (m_n >= 2) + { + ON_3dVector V = m_V[1] - m_V[0]; + if( m_n==2) + rc = ( V.Length() > eps ); + else + { + ON_3dVector W = m_V[2] - m_V[0]; + ON_3dVector X = ON_CrossProduct(V, W); + // TODO put something smart here.... + if( m_n==3) + rc = (X.Length() > eps); + else + { + // TODO and here.... + double triple = X * (m_V[3] - m_V[0]); + rc = (fabs(triple) > eps); + } + } + } + return rc; +} + +const ON_3dPoint& ON_3dSimplex::operator[](int i) const { + return *reinterpret_cast(m_V + i); +} + +ON_3dPoint& ON_3dSimplex::operator[](int i) { + return *reinterpret_cast(m_V + i); +} + + +ON_3dPoint ON_3dSimplex::Vertex(int i) const { return ON_3dPoint(m_V[i]); } +ON_3dPoint& ON_3dSimplex::Vertex(int i) { return *reinterpret_cast(&m_V[i]); } + + + +ON_3dPoint ON_3dSimplex::Evaluate(const double* b) const +{ + ON_3dVector p(0, 0, 0); + for (int i = 0; i < m_n; i++) + p += b[i] * m_V[i]; + return p; +} + +ON_3dPoint ON_3dSimplex::Evaluate(const ON_4dPoint& b) const { return Evaluate(&b.x); } + +double ON_3dSimplex::Volume() const +{ + double vol = 0.0; + int n = Count(); + if (n >= 2) + { + ON_3dVector V = m_V[1] - m_V[0]; + if (n == 2) + vol = V.Length(); + else + { + ON_3dVector X = ON_CrossProduct(V, m_V[2]-m_V[0]); + if (n == 3) + vol = 0.5 * X.Length(); + else + vol = 1.0/6.0 * fabs(X*(m_V[3] - m_V[0])); + } + } + return vol; +} + +double ON_3dSimplex::SignedVolume() const +{ + double vol = ON_UNSET_VALUE; + if (Count() == 3) + { + ON_3dVector V = m_V[1] - m_V[0]; + ON_3dVector X = ON_CrossProduct(V, m_V[2] - m_V[0]); + vol = 1.0 / 6.0 * (X*(m_V[3] - m_V[0])); + } + return vol; +} + +ON_BoundingBox ON_3dSimplex::BoundingBox() const +{ + ON_BoundingBox box; + box.Set(3, false, m_n, 3, m_V[0], false); + return box; +} + +bool ON_3dSimplex::GetBoundingBox( + ON_BoundingBox& bbox, int bGrowBox ) const +{ + return bbox.Set(3, false, m_n, 3, m_V[0], bGrowBox); +} + +bool ON_3dSimplex::GetTightBoundingBox( + ON_BoundingBox& tight_bbox, bool bGrowBox, + const ON_Xform* xform ) const +{ + if (bGrowBox && !tight_bbox.IsValid()) + { + bGrowBox = false; + } + if (!bGrowBox) + { + tight_bbox.Destroy(); + } + + int i; + for (i = 0; i < m_n; i++) + { + if (ON_GetPointListBoundingBox(3, 0, m_n, 3, m_V[i], tight_bbox, bGrowBox, xform)) + bGrowBox = true; + } + return bGrowBox ? true : false; +} + +bool ON_3dSimplex::Transform( + const ON_Xform& xform) +{ + for (int i = 0; i < m_n; i++) + m_V[i].Transform(xform); + return true; +} + +bool ON_3dSimplex::Rotate( + double sin_angle, + double cos_angle, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation) +{ + ON_Xform R; + R.Rotation(sin_angle, cos_angle, axis_of_rotation, center_of_rotation); + return Transform(R); +} + +bool ON_3dSimplex::Rotate( + double angle_in_radians, + const ON_3dVector& axis_of_rotation, + const ON_3dPoint& center_of_rotation ) +{ + ON_Xform R; + R.Rotation(angle_in_radians, axis_of_rotation, center_of_rotation); + return Transform(R); +} + +bool ON_3dSimplex::Translate( const ON_3dVector& delta ) +{ + ON_Xform T = ON_Xform::TranslationTransformation(delta); + return Transform(T); +} + + +bool ON_3dSimplex::RemoveVertex(int i) +{ + bool rc = false; + if (i < m_n) + { + m_n--; + for (/**/; i < m_n; i++) + m_V[i] = m_V[i + 1]; + } + return rc; +} + +bool ON_3dSimplex::AddVertex(const ON_3dPoint& P) +{ + bool rc = false; + if (m_n < 4) + { + m_V[m_n++] = P; + } + return rc; +} + + +bool ON_3dSimplex::SetVertex(int i, ON_3dPoint P0) +{ + bool rc = false; + if (i >= 0 && i < Count()) + { + m_V[i] = P0; + // todo clear any cashed data + rc = true; + } + return rc; +} + +ON_3dVector ON_3dSimplex::Edge(int e0, int e1) const +{ + ON_3dVector V = ON_3dVector::UnsetVector; + if (e0 < Count() && e1 < Count()) + { + V = Vertex(e1) - Vertex(e0); + } + return V; +} + + +/* + This is a carefull implementation of cross product that tries to get an accurate result +*/ +ON_3dVector ON_CrossProductwCare(const ON_3dVector& A, ON_3dVector& B) +{ + double norm[3]; + norm[0] = A.MaximumCoordinate(); + norm[1] = B.MaximumCoordinate(); + ON_3dVector AxB = ON_CrossProduct(A, B); + const double thresh = 1.0e-8; // sin(A,B) ~< thresh^(1/2) + double ab = norm[0] * norm[1]; + double ab2 = ab * ab; + if (AxB.LengthSquared() < ab2*thresh) + { + // TODO - this is a good start but we could do something better... + ON_3dVector V[3] = { A, B, A - B }; + norm[2] = V[2].MaximumCoordinate(); + int maxi = (norm[0] > norm[1]) ? 0 : 1; + if (norm[2] < norm[maxi]) // else C is longest so we are done. + { + AxB = ON_CrossProduct(V[(maxi + 1) % 3], V[(maxi + 2) % 3]); + if (maxi == 0) + AxB = - AxB; + } + } + return AxB; +} + + +ON_3dVector ON_3dSimplex::FaceNormal(int noti) const +{ + ON_3dVector N = ON_3dVector::UnsetVector; + if (Count() == 3 || (Count() == 4 && noti <= 3 && noti >= 0)) + { + int ind[3] = { 0,1,2 }; + if (Count() == 4 && noti < 3) + { + for (int ii = 0; ii < 3; ii++) + ind[ii] = (noti + 1 + ii) % 4; + } + ON_3dVector A = Vertex(ind[1]) - Vertex(ind[0]); + ON_3dVector B = Vertex(ind[2]) - Vertex(ind[0]); + N = ON_CrossProductwCare(A, B ); + } + return N; +} + +ON_3dVector ON_3dSimplex::FaceUnitNormal(int noti) const +{ + ON_3dVector N = FaceNormal(noti); + if (N != ON_3dVector::UnsetVector && N != ON_3dVector::ZeroVector) + N.Unitize(); + return N; +} + +bool ON_3dSimplex::GetClosestPoint(const ON_3dPoint& P0, ON_4dPoint& Bary, double atmost) const +{ + ON_3dVector V = P0; + ON_3dSimplex Trans; + bool toofar = ((atmost <= 0.0) ? false : true); + bool rval = false; + for (int i = 0; i < Count(); i++) + { + + Trans.AddVertex(Vertex(i) - V); + if (toofar && Trans[i].MaximumCoordinate() < .5 * atmost) + toofar = false; + } + if (!toofar) + { + rval = Trans.GetClosestPointToOrigin(Bary); + if (rval && atmost >= 0) + { + ON_3dVector CP = Trans.Evaluate(Bary); + if (CP.LengthSquared() > atmost*atmost) + rval = false; + } + } + return rval; +} + +bool ON_3dSimplex::GetClosestPointToOrigin(ON_4dPoint& Bary) const +{ + bool rc = false; + if (m_n == 4) + rc = Closest3plex(Bary); + else if (m_n == 3) + rc = Closest2plex(Bary); + else if (m_n == 2) + rc = Closest1plex(Bary); + else if (m_n == 1) + { + Bary = ON_4dPoint(1.0, 0, 0, 0); + rc = true; + } + return rc; +} + +// return true if a and b are non zero and of same sign +static bool SameSign(double a, double b) +{ + return (a*b) > 0; +} + + +// closest point to a 1-simplex +bool ON_3dSimplex::Closest1plex(ON_4dPoint& Bary) const +{ + bool rc = false; + ON_3dVector Del = m_V[1] - m_V[0]; + double Del2 = Del.LengthSquared(); + if (Del2 > 0.0) + { + rc = true; + double dot = -m_V[0] * Del; + if (dot >= Del2) + Bary = ON_4dPoint(0, 1, 0, 0); + else if (dot <= 0) + Bary = ON_4dPoint(1, 0, 0, 0); + else + { + double b0 = dot / Del2; + Bary = ON_4dPoint(1 - b0, b0, 0, 0); + } + } + return rc; +} + +// closest point to origin for a 2-simplex +bool ON_3dSimplex::Closest2plex(ON_4dPoint& Bary) const +{ + bool rc = false; + ON_3dVector N = FaceNormal(0); + double N2 = N.LengthSquared(); + if (N2 > 0) + { + ON_3dPoint P3 = (m_V[0] * N)*N / N2; // origin projected to Affine_Span < V_0, V_1, V_2 > + int J = N.MaximumCoordinateIndex(); + + ON_3dPoint Planar[3]; // We reduce to a planar closest point to origin problem + for (int i = 0; i < 3; i++) + Planar[i] = Vertex(i) - P3; + + // Finding the barycentric coordintes of the origin will guild the rest of the algorithm + // We simplify this by projecting to Not J plane + + double DetM = 0.0; + double C3[3]; + int j0 = (J + 1) % 3; + int j1 = (J + 2) % 3; + for (int i = 0; i < 3; i++) + { + int i0 = (i + 1) % 3; + int i1 = (i + 2) % 3; + C3[i] = Planar[i0][j0] * Planar[i1][j1] - Planar[i1][j0] * Planar[i0][j1]; + DetM += C3[i]; + } + + if (DetM != 0.0) + { + bool interior = true; + for (int j = 0; interior && j < 3; j++) + interior = SameSign(DetM, C3[j]); + if (interior) + { + for (int i = 0; i < 3; i++) + Bary[i] = C3[i] / DetM; + rc = true; + } + else + { + for (int j = 0; j < 3; j++) + { + if (!SameSign(DetM, C3[j])) + { + ON_3dSimplex S(Planar[(j + 1) % 3], Planar[(j + 2) % 3]); // S is a 1-simplex + ON_4dPoint bary; + if (S.GetClosestPointToOrigin(bary)) + { + rc = true; + bool OnEnd = (bary[0] == 1.0 || bary[1] == 1.0); + Bary[j] = 0.0; + Bary[3] = 0.0; + for (int i = 0; i < 2; i++) + Bary[(j + 1 + i) % 3] = bary[i]; + if (!OnEnd) + break; + } + } + } + } + } + } + return rc; +} + +// closest point to a 3-simplex +bool ON_3dSimplex::Closest3plex(ON_4dPoint& Bary) const +{ + bool rc = false; + // Solving + // [ V_0 V_1 V_2 V_3 ] [ 0 ] + // M*B = [ 1 1 1 1 ] * B = [ 1 ] + + int ind[3] = { 1,2,3 }; + double detM = 0.0; + double C4[4]; // C4[j] = C_{4,j} is a cofactor of M + double sign = 1.0; + for (int j = 0; j < 4; j++) + { + C4[j] = sign * ON_TripleProduct(m_V[ind[0]], m_V[ind[1]], m_V[ind[2]]); + if (j < 3) + { + ind[j] = j; // {1,2,3}, {0,2,3}, {0,1,3}, {0,1,2} + sign *= -1.0; + } + detM += C4[j]; + } + + + if (detM != 0.0) + { + bool interior = true; + int j = 0; + for (j = 0; interior && j < 4; j++) + interior = SameSign(detM, C4[j]); + if (interior) + { + for (int i = 0; i < 4; i++) + Bary[i] = C4[i] / detM; + rc = true; + } + else + { + j--; + double D2 = ON_DBL_MAX; // best answer so far + int N = 5; // size of support + do + { + if (!SameSign(detM, C4[j])) + { + ON_3dSimplex S = (*this); + S.RemoveVertex(j); + ON_4dPoint bary; + if (S.Closest2plex(bary)) + { + int n = 0; // size of support + for (int i = 0; i < 3; i++) if (bary[i] > 0)n++; + if (n == 3) + { + for (int i = 3; i > j; i--) + bary[i] = bary[i - 1]; + bary[j] = 0.0; + Bary = bary; + rc = true; + break; + } + else + { + ON_3dVector cp = S.Evaluate(bary); + double d2 = cp.LengthSquared(); + + if (d2 < D2 || d2 == D2 && n < N) + { + D2 = d2; + N = n; + for (int i = 3; i > j; i--) + bary[i] = bary[i - 1]; + bary[j] = 0.0; + rc = true; + Bary = bary; + } + } + } + else + rc = false; + } + } while (++j < 4); + } + } + return rc; +} + +bool ON_ConvexPoly::Standardize(ON_4dex& dex, ON_4dPoint& B) +{ + bool rc = true; + ON_4dex rdex = { -1,-1,-1,-1 }; // results + ON_4dPoint rB(0, 0, 0, 0); + int ri = 0; // index into result + for (int ii = 0; ii < 4; ii++) // index in input + { + while ((dex[ii] < 0 || B[ii] == 0.0) && ii < 4) ii++; + if (ii == 4) + break; + int j = 0; + while (j < ri && rdex[j] != dex[ii]) j++; + if (j == ri) + { + rdex[ri] = dex[ii]; + rB[ri++] = 0.0; + } + rB[j] += B[ii]; + } + + if (rc) + { + dex = rdex; + B = rB; + } + return rc; +} + + +void ON_ConvexHullRef::Initialize(const ON_3dVector* V0, int n) +{ + m_n = n; + m_v = *V0; + m_is_rat = false; + m_stride = 3; +} + +void ON_ConvexHullRef::Initialize(const ON_4dPoint* V0, int n) +{ + m_n = n; + m_v = *V0; + m_is_rat = true; + m_stride = 4; +} + +// style must be either not_rational or homogeneous_rational = 2, +void ON_ConvexHullRef::Initialize(const double* V0, ON::point_style style, int count) +{ + if (style == ON::homogeneous_rational) + Initialize(reinterpret_cast(V0), count); + else + Initialize(reinterpret_cast(V0), count); +} + +ON_ConvexHullRef::ON_ConvexHullRef(const ON_3dVector* V0, int n) +{ + m_n = n; + m_v = *V0; + m_is_rat = false; + m_stride = 3; +} + +ON_ConvexHullRef::ON_ConvexHullRef(const ON_3dPoint* P0, int n) +{ + m_n = n; + m_v = *P0; + m_is_rat = false; + m_stride = 3; +} + +ON_ConvexHullRef::ON_ConvexHullRef(const ON_4dPoint* V0, int n) +{ + m_n = n; + m_v = *V0; + m_is_rat = true; + m_stride = 4; +} + +ON_ConvexHullRef::ON_ConvexHullRef(const double* V0, bool is_rat, int n) +{ + m_n = n; + m_v = V0; + m_is_rat = is_rat; + m_stride = is_rat ? 4 : 3; +} + +ON_ConvexHullRef::ON_ConvexHullRef(const double* V0, bool is_rat, int n, int stride) +{ + m_n = n; + m_v = V0; + m_is_rat = is_rat; + m_stride = stride; +} + +ON_3dVector ON_ConvexHullRef::Vertex(int j) const +{ + ON_3dVector v; + if (m_is_rat) + { + ON_4dPoint hv = *(reinterpret_cast(m_v + m_stride*j)); + v = ON_3dVector(hv.EuclideanX(), hv.EuclideanY(), hv.EuclideanZ()); + } + else + { + v = *(reinterpret_cast(m_v + m_stride*j)); + } + return v; +} + +int ON_ConvexHullRef::SupportIndex(ON_3dVector W, int) const +{ + int j0 = 0; + double dot = Vertex(0)*W; + for (int j = 1; j < m_n; j++) + { + ON_3dVector v = Vertex(j); + double d = v * W; + if (d > dot) + { + dot = d; + j0 = j; + } + } + return j0; +} + +double ON_ConvexHullRef::MaximumCoordinate() const +{ + return ON_MaximumCoordinate(m_v, 3, m_is_rat, m_n); +} + +int ON_ConvexHullPoint2::AppendVertex(const ON_3dPoint& P) // return index of new vertex. must set Adjacent Indicies. +{ + m_Vert.Append(P); + Ref.Initialize(m_Vert, m_Vert.Count()); + return m_Vert.Count()-1; +} + +void ON_ConvexHullPoint2::Empty() +{ + Ref.Initialize(m_Vert, 0); + m_Vert.Empty(); +} + +double ON_ConvexHullPoint2::MaximumCoordinate() const +{ + return ON_MaximumCoordinate(m_Vert[0], 3, false, m_Vert.Count()); +} + +bool ClosestPoint(const ON_3dPoint P0, const ON_ConvexPoly& poly, + ON_4dex& dex, ON_4dPoint& Bary, double atmost ) +{ + ON_ConvexHullRef CvxPt(&P0, 1); + // Set pdex to match the support of dex + ON_4dex pdex = dex; + for (int i = 0; i < 4; i++) + { + if (dex[i] >= 0) + pdex[i] = 0; + } + bool rc = ClosestPoint(CvxPt, poly, pdex, dex, Bary, atmost); + ON_ConvexPoly::Standardize(dex, Bary); + return rc; +} + +// MatchingSupport(A, B) retuns a positive number if +// A[i]<0 iff B[i]<0 and at least one coordinate pair has valid indicies A[i]>=0 and B[i]>=0. +static int MatchingSupport(const ON_4dex& A, const ON_4dex& B) +{ + int nsup = 0; + int i =0; + for (; i < 4; i++) + { + if ((A[i] < 0) != (B[i] < 0)) + break; + if (A[i] >= 0) + nsup++; + } + return (i == 4) ? nsup : -1; +} + +static int Gregdebugcounter = 0; + + +static void DebugCounter() +{ +#ifdef ON_DEBUG + Gregdebugcounter++; +#endif +} + +// Gilbert Johnson Keerthi algorithm + +// To supply an inital seed simplex Adex and Bdex must be valid and +// have matching support specifically +// Adex[i]=0 for some i for some i +// By satisfying this condition Adex and Bdex will define a simplex in A - B +// Note the result of a ClosestPoint calculation Adex and Bdex satisfy these conditions +bool ClosestPoint(const ON_ConvexPoly& A, const ON_ConvexPoly& B, + ON_4dex& Adex, ON_4dex& Bdex, ON_4dPoint& Bary, double atmost) +{ + bool rc = false; + if (A.Count() == 0 || B.Count() == 0) + return false; + + int Aind[4] = { -1,-1,-1,-1 }; + int Bind[4] = { -1,-1,-1,-1 }; + ON_3dSimplex Simp; + Bary = ON_4dPoint(1, 0, 0, 0); + ON_3dVector v(0,0,0); + + + // If Adex and Bdex are valid on entry we use them as an inital + // seed for the trial simplex. This case is indicated by setting + // bFirstPass + bool bFirstPass = false; + if (A.IsValid4Dex(Adex) && B.IsValid4Dex(Bdex) && MatchingSupport(Adex, Bdex)>0 ) + { + // Set the initial condition for Aind, Bind and Simp from Adex and Bdex + int j = 0; + int i = 0; + for (i = 0; i < 4; i++) + { + if (Adex[i] < 0 || Bdex[i] < 0) + continue; + int jj; + for (jj = 0; jj < j; jj++) + { + if (Aind[jj] == Adex[i] && Bind[jj] == Bdex[i]) + break; + } + if (jj < j) + break; + Aind[j] = Adex[i]; + Bind[j] = Bdex[i]; + ON_3dVector vert = A.Vertex(Aind[j]) - B.Vertex(Bind[j]); + Simp.AddVertex(vert); + j++; + } + + if( i==4) + bFirstPass = true; + else + bFirstPass = false; + } + + bool done = false; + double vlen = ON_DBL_MAX; + double vlenlast = ON_DBL_MAX; + while (!done) + { + if (!bFirstPass) + { + // Default initial simplex is a point A.Vertex(0) - B.Vertex(0); + Aind[0] = Bind[0] = 0; + Aind[1] = Aind[2] = Aind[3] = -1; + Bind[1] = Bind[2] = Bind[3] = -1; + v = A.Vertex(0) - B.Vertex(0); + vlenlast = ON_DBL_MAX; + vlen = v.Length(); + + Simp = ON_3dSimplex(); + Simp.AddVertex(v); + } + // The key to the implemetation of this algorithm is contained in Simplex::GetClosestPointToOrigin() + + // These lines are for ON_3dSimplex + const bool SimplexReindexing = true; + + // These lines are for ON_JSimplex + //const bool SimplexReindexing = false; + //ON_JSimplex Simp; + + double mu = 0.0; + const double epsilon = 10000.0 * ON_EPSILON; + + int wA = 0, wB = 0; + while (!done && (bFirstPass || vlen > 0)) + { + ON_3dVector W; + if (!bFirstPass) + { + wA = A.SupportIndex(-v, wA); + wB = B.SupportIndex(v, wB); + W = A.Vertex(wA) - B.Vertex(wB); + ON_3dVector unit_v = 1 / vlen * v; + double del = unit_v * W ; // this is lower bound on the distance + if (del > mu) + mu = del; + + // 18-July-19 Considered Adding vlen>=vlenlast to ensure distance is decreasing + // See RH-47044 for a case that got hung up here + // See RH-30343 for a case where the 2.0 factor is needed + // WRONG!!! RH-30343 again. the 2.0 factor doest't work either + // TODO: testing... If the support vertex is already in simplex were done + int i = 0; + for (i = 0; i < 4; i++) + { + if (Aind[i] == wA && Bind[i] == wB) break; + } + + // See 100818MatchPoints.3dm if Bary>0 then we are done since closest point is + // in the interior of a Simplex + if (i < 4 || Simp.Count()==4) + done = true; + else + done = ((vlen - mu) <= 2.0* mu *epsilon) || mu > atmost || vlen >= vlenlast ;//TODO "1+" ?? got rid of it + } + if (!done) + { + // n0 is the index of the newly added vertex + int n0; + if (!bFirstPass) + { + n0 = Simp.Count(); // this is specific to ON_3dSimplex + Simp.AddVertex(W); + Aind[n0] = wA; + Bind[n0] = wB; + } + else + n0 = Simp.Count() - 1; + + + + if (Simp.GetClosestPointToOrigin(Bary)) + { + DebugCounter(); + bFirstPass = false; + v = Simp.Evaluate(Bary); + vlenlast = vlen; + vlen = v.Length(); + int imax = 3; + if (SimplexReindexing) + imax = n0; + for (int i = imax; i >= 0; i--) + { + if (Bary[i] == 0.0) + { + Simp.RemoveVertex(i); + if (SimplexReindexing) + { + /* The JSimplex the indicies are fixed so the following step is not needed. + the ON_3dSimplex type does change the index of verticies and this piece of + code adjusts for this.*/ + for (int j = i; j < n0; j++) + { + Bary[j] = Bary[j + 1]; + Aind[j] = Aind[j + 1]; + Bind[j] = Bind[j + 1]; + } + Bary[n0] = 0.0; + n0--; + } + } + } + } + else + { + // In this case I am going to terminte the iteration. If this was a FirstPass with user supplied initial guess + // then we restart the algorithm without the initial guess. + break; + } + } + } + if (!done) + { + if (bFirstPass) + bFirstPass = false; + else + done = true; + + } + + /* TODO + RH-54751 + vlen is nearly 0. but it should be 0.0 + If (0,0,0) s in the interior of A-B then solution is vlen == 0.0 + */ + if (Simp.Count() == 4 && Simp.Volume() > ON_SQRT_EPSILON) + { + vlen = 0.0; + } + rc = (vlen <= atmost); + if (rc) + { + if (SimplexReindexing) + { + if (Simp.Count() > 0) + { + for (int i = Simp.Count(); i < 4; i++) + { + Bary[i] = 0.0; + Aind[i] = Bind[i] = -1; + } + } + } + + + Adex = ON_4dex(Aind[0], Aind[1], Aind[2], Aind[3]); + Bdex = ON_4dex(Bind[0], Bind[1], Bind[2], Bind[3]); + } + } + return rc; +} + + +// Is PQR a right hnd turn +static bool IsLeftTurn(const ON_2dPoint& P, const ON_2dPoint& Q, const ON_2dPoint& R) +{ + ON_2dVector A = R - Q; + ON_2dVector B = P - Q; + double det = A.x* B.y - B.x*A.y; + return det > 0.0; +} + + +int ON_ConvexHull2d(const ON_SimpleArray& Pnt, ON_SimpleArray& Hull, ON_SimpleArray< int>* PntInd) +{ + int rval = -1; + Hull.Empty(); + ON_SimpleArray Ind(Pnt.Count()); + Ind.SetCount(Pnt.Count()); + if (PntInd) + PntInd->Empty(); + if (!Pnt.Sort(ON::sort_algorithm::quick_sort, Ind, + [](const ON_2dPoint* A, const ON_2dPoint* B) { return ON_2dPoint::Compare(*A, *B); })) + return rval; + if (Pnt.Count() == 0) + return rval; + + Hull.Append(Pnt[Ind[0]]); + if (PntInd) + PntInd->Append(Ind[0]); + int fixed = 1; // This is the count of Hull that is fixed + int ri = 1; + + for (int inc = 1; inc >= -1; inc -= 2) + { + for ( /*empty*/; ri < Pnt.Count() && ri >= 0; ri += inc) + { + ON_2dPoint R = Pnt[Ind[ri]]; + + if (Hull.Count() == fixed) + { + if (R != *Hull.Last()) + { + Hull.Append(R); + if (PntInd) + PntInd->Append(Ind[ri]); + } + } + else + { + int pi = Hull.Count() - 2; + ON_2dPoint P = Hull[pi]; + ON_2dPoint Q = *Hull.Last(); + if (IsLeftTurn(P, Q, R)) + { + Hull.Append(R); + if (PntInd) + PntInd->Append(Ind[ri]); + } + else + { + bool done = false; + while (!done) + { + Hull.Remove(); + if (PntInd) + PntInd->Remove(); + Q = P; + done = (pi == ((inc==1)?0:fixed-1)); + if (!done) + { + P = Hull[--pi]; + done = IsLeftTurn(P, Q, R); + } + + } + Hull.Append(R); + if (PntInd) + PntInd->Append(Ind[ri]); + } + } + } + if (Hull.Count() == 1) { + rval = 0; + break; + } + fixed = Hull.Count(); + ri = Pnt.Count() - 2; + } + + if (Hull.Count() == 2) + rval = 1; + else + rval = 2; + return rval; +} + + + diff --git a/opennurbs_convex_poly.h b/opennurbs_convex_poly.h new file mode 100644 index 00000000..3635038e --- /dev/null +++ b/opennurbs_convex_poly.h @@ -0,0 +1,398 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2012 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 . +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(ON_CONVEX_POLY_INC_) +#define ON_CONVEX_POLY_INC_ + + +// A Simplex in 3d +class ON_CLASS ON_3dSimplex +{ +public: + ON_3dSimplex(); // An empty simplex + explicit ON_3dSimplex(const ON_3dPoint& a); // 0-simplex in 3d + ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b); // 1-simplex + ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c); // 2-simplex + ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c, const ON_3dPoint& d); // 3-simplex + + ON_3dSimplex(const ON_3dSimplex& rhs) = default; + ON_3dSimplex& operator=(const ON_3dSimplex& rhs) = default; + ~ON_3dSimplex() = default; + + int Count() const; // Number of Verticies <=4 + bool IsValid(double eps) const; // true if the Verticies are affinely independent + + /* + Description: + Evaluate a point in a Simplex from a barycentric coordinate b. + Returns: + The point + b[0] * Vertex[0] + ... + b[Count()-1] * Vertex[Count()-1] + Notes: + If b[0] + ... + b[Count()-1] = 1 and b[i]>=0 for i=0 to Count()-1 then the + returned point is on the simplex + */ + ON_3dPoint Evaluate(const double* b) const; + ON_3dPoint Evaluate(const ON_4dPoint& b) const; + + /* + Description: + Find Closest Point to this simplex from a base point P0 or the Origin. + If true is retuned then Evaluate(Bary) is the closest point on the Simplex. + maximum_distance - optional upperbound on closest point. If maximum_distance>=0 is specified and + Dist(P0, Simplex)>maximum_distance then false is returned. + */ + bool GetClosestPoint(const ON_3dPoint& P0, ON_4dPoint& Bary, double maximum_distance = ON_DBL_MAX) const; + bool GetClosestPointToOrigin(ON_4dPoint& Bary) const; + + /* + Count() Volume() returns + 0 0.0 + 1 0.0 + 2 length >=0 + 3 area >=0 + 4 volume >=0 + */ + double Volume() const; + double SignedVolume() const; // returns ON_UNSET_VALUE if Count()<4 else the signed volume + + /* + FaceNormal(noti) is the oriented face normal obtained by omitting vertex noti. + FaceNormal returns ON_UNSET_VALUE if Count()<3 or Count()==4 noti not 0,1,2 or 3. + FaceUnitNormal returns ON_UNSET_VALUE if Count()<3 or Count()==4 noti not 0,1,2 or 3 or if FaceNormal(noti)=Zero_Vector + */ + ON_3dVector FaceNormal(int noti = 0) const; + ON_3dVector FaceUnitNormal(int noti = 0) const; + + /* + Edge vector from Vertex(e0) to Vertex(e1) + */ + ON_3dVector Edge(int e0, int e1)const; + + /* If 0<=i=0 + */ + virtual int Count() const = 0; + /* + Returns: Vertex[i] for i=0,...,Count()-1 + */ + virtual ON_3dVector Vertex(int i) const = 0; + + /* + Description: + Let K be this ON_ConvexPoly then for a non-zero vector W the support Support(W) is a point x in K that maximizes + min arg x * W + x \in K + i0 is an optional initial index seed value. It may provide a performance enhancement toward finding + a minimizer. + */ + ON_3dPoint Support(ON_3dVector W, int i0 =0) const + { + return Vertex(SupportIndex(W, i0)); + } + + /* + Description: + For any vector W there is a vetex that is Support(W) + SupportIndex( W, i0) returns a vertex index for a vertex that is the support. + Veretx( K.SupportIndex( W )) = K.Support(W ); + */ + virtual int SupportIndex(ON_3dVector W, int i0=0) const = 0; + + /* + Description: + Points in a Convex Polytope are parameterized , not necessaily uniquely, + by an ON_4dex of vertex indicies and a 4d barycentric point B + Evaluate(Ind, B ) = Sum_{i=0,..,3} Vertex(Ind[i])*B[i], where the sum is taken over i such that Ind[i]>=0 + If B is a barycentric coordinte + B[i]>=0 and B[0] + B[1] + B[2] + B[3] = 1.0 + then Evaluate( Ind, B) is a point in the convex polytope + */ + ON_3dPoint Evaluate(ON_4dex dex, ON_4dPoint B)const + { + ON_3dVector v(0, 0, 0); + if (dex.i >= 0) + v = B[0] * Vertex(dex.i); + if (dex.j >= 0) + v += B[1] * Vertex(dex.j); + if (dex.k >= 0) + v += B[2] * Vertex(dex.k); + if (dex.l >= 0) + v += B[3] * Vertex(dex.l); + return v; + }; + + /* + Description: + This is a bound on the collection of verticies. + Vertex(i).MaximumCoordinate()<= MaximumCoordinate() for all i + */ + virtual double MaximumCoordinate() const = 0; + + /* + Description: + A point represented by a ON_4dex D and a barycentric coordinate B + can be put in a standard form so that non-negative elements of D are unique and + corresponding coordinates are positive. Furthemore, the non-negative + indicies are all listed before the unset ( neagative ) values + */ + static bool Standardize(ON_4dex& D, ON_4dPoint& B); + + /* + Returns: + true if d[i] n) return false; + } + return true; + } + bool IsValid4Dex(const ON_4dex& D) const { return IsValid4DexN(D, Count()); }; + + virtual ~ON_ConvexPoly() {}; +}; + +// 3d convex hull defined by an explicit collection of points called verticies. +// Note: verticies need not be extreme points + +// WARNING: Points are referenced not stored for optimal performance in' +// some applications. +// The list of points must remain alive and in there initial location +// For the duration of this object. +class ON_CLASS ON_ConvexHullRef : public ON_ConvexPoly +{ +public: + ON_ConvexHullRef() { m_n = 0; m_is_rat = false; m_stride = 3; }; + ON_ConvexHullRef(const ON_3dVector* V0, int count); // a 3d point array + ON_ConvexHullRef(const ON_3dPoint* V0, int count); // a 3d point array + ON_ConvexHullRef(const ON_4dPoint* V0, int count); // a array of homogeneous points + ON_ConvexHullRef(const double* v0, bool is_rat, int n); // v0 is an array of 3dpoints or homo 4d points + ON_ConvexHullRef(const double* v0, bool is_rat, int n, int stride); // v0 is an array of 3dpoints or homo 4d points + + void Initialize(const ON_3dVector* V0, int count); + void Initialize(const ON_4dPoint* V0, int count); + void Initialize(const double* V0, ON::point_style style, int count); // style must be either not_rational or homogeneous_rational = 2, + + int Count() const override { return m_n; } + ON_3dVector Vertex(int j) const override; + + // Support map O( Vertes.Count + virtual int SupportIndex(ON_3dVector W, int i0) const override; + virtual double MaximumCoordinate() const override; + + virtual ~ON_ConvexHullRef() override {}; +private: + + int m_n = 0; + bool m_is_rat= false; + const double* m_v = nullptr; + int m_stride=3; +}; + +// 3d convex hull defined by an explicit collection of points called verticies. +// Note: verticies need not be extreme points +class ON_CLASS ON_ConvexHullPoint2 : public ON_ConvexPoly +{ +public: + ON_ConvexHullPoint2() = default; + ON_ConvexHullPoint2(int init_capacity) : m_Vert(init_capacity) {}; + + virtual int Count() const override { return m_Vert.Count(); } + virtual ON_3dVector Vertex(int j) const override { return m_Vert[j]; } + + // Support map + virtual int SupportIndex(ON_3dVector W, int i0) const override { + return Ref.SupportIndex(W, i0); + }; + + virtual double MaximumCoordinate() const override; + + virtual ~ON_ConvexHullPoint2() override {}; + + int AppendVertex(const ON_3dPoint& P); // return index of new vertex. must set Adjacent Indicies. + void Empty(); + + bool SetCapacity(int vcnt) { + m_Vert.SetCapacity(vcnt); + return true; + }; + +private: + ON_ConvexHullRef Ref; + ON_SimpleArray m_Vert; +}; + + +/* + +Computes a closest point between convex polytopes AHull and BHull. +Returns true if a closest point is found and it is within optional maximum_distance bound; + +Specifically, when true is returned parameters (Adex, ABbary) on AHull +and (Bdex, ABbary) on BHull are found such that: +if a* = AHull.Evaluate(Adex,ABbary), and + b* = BHull.Evaluate(Bdex,ABbary), +then + d = dist(a*, b*) <= maximum_distance, and +for any a in AHull and b in BHull + dist(a,b) => d. + + +Setting maximum_distance = tol can speedup the calculation in cases where d>tol + +On input Adex and Bdex are used to define an intial simplex +use Adex = Bdex = ON_4dex::Unset if you have no good initial guess +Notice the result is indepentent of the initial guess. + +*/ +ON_DECL +bool ClosestPoint(const ON_ConvexPoly& AHull, const ON_ConvexPoly& BHull, + ON_4dex& Adex, ON_4dex& Bdex, ON_4dPoint& ABbary, double maximum_distance = ON_DBL_MAX); + +ON_DECL +bool ClosestPoint(const ON_3dPoint P0, const ON_ConvexPoly& poly, + ON_4dex& dex, ON_4dPoint& Bary, double maximum_distance = ON_DBL_MAX); + +/* + Compute Convex hull of 2d points + Parameters: + Pnt - array of points, this is array of working data. The points are sorted in place as part of the algorithm + HUll - the sequence Hull[0], HUll[1]... ,*Hull.Last() == Hull[0] defines the convex hull with a positive orientation retuns 2. + PntInd - otional array to be filled in so that Hull[i] = Pnt[ PntInd[i]] where Pnt is the original input point + Returns + dimension of the convex hull + 2 - Hull is 2 dimensional + 1 - Hull is a line segments + 0 - hull is a point + <0 error +*/ +ON_DECL +int ON_ConvexHull2d(const ON_SimpleArray& Pnt, ON_SimpleArray& Hull, ON_SimpleArray< int>* PntInd = nullptr); + +#endif + + diff --git a/opennurbs_curve.cpp b/opennurbs_curve.cpp index a7676bb2..203fdb5e 100644 --- a/opennurbs_curve.cpp +++ b/opennurbs_curve.cpp @@ -585,7 +585,10 @@ bool ON_Curve::IsClosed() const { // Note: The point compare test should be the same // as the one used in ON_PolyCurve::HasGap(). - // + // June 2019 - sometime in the past decade ON_PolyCurve::HasGap() + // changed and the test there is different from this test. + // The initial "Note" no longer applies becaue it's no longer + // clear why the current ON_PolyCurve::HasGap() was changed. if ( ON_PointsAreCoincident( dim, false, a, p ) ) { if ( Evaluate( d.ParameterAt(1.0/3.0), 0, dim, b, 0 ) @@ -593,7 +596,7 @@ bool ON_Curve::IsClosed() const ) { if ( false == ON_PointsAreCoincident( dim, false, a, b ) - && false == ON_PointsAreCoincident( dim, false, p, c ) + && false == ON_PointsAreCoincident( dim, false, a, c ) && false == ON_PointsAreCoincident( dim, false, p, b ) && false == ON_PointsAreCoincident( dim, false, p, c ) ) @@ -2515,6 +2518,10 @@ static int CompareJoinEnds(void* ctext, const void* aA, const void* bB) if (a->tan_dot <= context->dot_tol && b->tan_dot > context->dot_tol) return 1; if (a->dist < b->dist) return -1; if (a->dist > b->dist) return 1; + if (a->id[0] < b->id[0]) return -1; + if (a->id[0] > b->id[0]) return 1; + if (a->id[1] < b->id[1]) return -1; + if (a->id[1] > b->id[1]) return 1; return 0; } else { @@ -2522,6 +2529,10 @@ static int CompareJoinEnds(void* ctext, const void* aA, const void* bB) if (a->dist > b->dist) return 1; if (a->tan_dot > b->tan_dot) return -1; if (a->tan_dot < b->tan_dot) return 1; + if (a->id[0] < b->id[0]) return -1; + if (a->id[0] > b->id[0]) return 1; + if (a->id[1] < b->id[1]) return -1; + if (a->id[1] > b->id[1]) return 1; return 0; } } @@ -2841,6 +2852,8 @@ static bool GetCurveEndData(int count, ON_SimpleArray& EData) { + join_tol = join_tol * join_tol; + EData.Reserve(count); bool bHaveTans = (StartTans && EndTans) ? true : false; if (dot_tol < 0.0) @@ -2863,7 +2876,7 @@ static bool GetCurveEndData(int count, const ON_3dPoint& Pi = (endi) ? EndPoints[i] : StartPoints[i]; for (int endj=0; endj<2; endj++){ const ON_3dPoint& Pj = (endj) ? EndPoints[j] : StartPoints[j]; - dist[endi][endj] = Pi.DistanceTo(Pj); + dist[endi][endj] = (Pi-Pj).LengthSquared(); if (dist[endi][endj] >= join_tol) bDoIt[endi][endj] = false; } diff --git a/opennurbs_curve.h b/opennurbs_curve.h index bb8576d3..5c078328 100644 --- a/opennurbs_curve.h +++ b/opennurbs_curve.h @@ -261,13 +261,13 @@ public: // Description: // Get number of parameters of "knots". // Parameters: - // knots - [out] an array of length SpanCount()+1 is filled in + // span_parameters - [out] an array of length SpanCount()+1 is filled in // with the parameters where the curve is not smooth (C-infinity). // Returns: // true if successful virtual bool GetSpanVector( - double* knots + double* span_parameters ) const = 0; // ////////// diff --git a/opennurbs_defines.cpp b/opennurbs_defines.cpp index dfc1af6b..be404ed9 100644 --- a/opennurbs_defines.cpp +++ b/opennurbs_defines.cpp @@ -2317,6 +2317,32 @@ ON_4dex::ON_4dex( , l(lValue) {} +int ON_4dex::operator[](int ind) const +{ + switch (ind) { + case 0: + return i; + case 1: + return j; + case 2: + return k; + } + return l; +} + +int& ON_4dex::operator[](int ind) +{ + switch (ind) { + case 0: + return i; + case 1: + return j; + case 2: + return k; + } + return l; +} + ON_2udex::ON_2udex( unsigned int iValue, unsigned int jValue) diff --git a/opennurbs_defines.h b/opennurbs_defines.h index 284ae850..d61426d0 100644 --- a/opennurbs_defines.h +++ b/opennurbs_defines.h @@ -135,9 +135,11 @@ #if defined(PI) #define ON_PI PI #else -#define ON_PI 3.141592653589793238462643 +#define ON_PI 3.141592653589793238462643 #endif +#define ON_2PI (2.0*ON_PI) + #define ON_DEGREES_TO_RADIANS (ON_PI/180.0) #define ON_RADIANS_TO_DEGREES (180.0/ON_PI) @@ -324,6 +326,13 @@ double ON_DoubleFromFloat( float x ); +/* +Returns: + A nonzero runtime unsigned that is incremented every call to ON_NextContentSerialNumber(). + This value is useful as a "content serial number" that can be used to detect + when the content of an object has changed. +*/ +ON__UINT64 ON_NextContentSerialNumber(); ON_END_EXTERNC @@ -399,10 +408,13 @@ bool ON_IsNullPtr(const ON__INT_PTR ptr); #define ON_RELATIVE_CURVATURE_TOLERANCE 0.05 /* default value for angle tolerances = 1 degree */ -#define ON_DEFAULT_ANGLE_TOLERANCE (ON_PI/180.0) +#define ON_DEFAULT_ANGLE_TOLERANCE_RADIANS (ON_PI/180.0) +#define ON_DEFAULT_ANGLE_TOLERANCE_DEGREES (ON_DEFAULT_ANGLE_TOLERANCE_RADIANS * 180.0/ON_PI) +#define ON_DEFAULT_ANGLE_TOLERANCE ON_DEFAULT_ANGLE_TOLERANCE_RADIANS #define ON_DEFAULT_ANGLE_TOLERANCE_COSINE 0.99984769515639123915701155881391 #define ON_MINIMUM_ANGLE_TOLERANCE (ON_DEFAULT_ANGLE_TOLERANCE/10.0) +#define ON_DEFAULT_DISTANCE_TOLERANCE_MM 0.01 /* */ @@ -553,6 +565,9 @@ public: ON_4dex(const ON_4dex&) = default; ON_4dex& operator=(const ON_4dex&) = default; + + int operator[](int i) const; + int& operator[](int i); public: // do not initialize i, j, k, l for performance reasons int i; @@ -673,6 +688,24 @@ enum class ON_ChainDirection : unsigned char Both = 3 }; +/// +///Style of color gradient +/// +enum class ON_GradientType : int +{ + ///No gradient + None = 0, + ///Linear (or axial) gradient between two points + Linear = 1, + ///Radial (or spherical) gradient using a center point and a radius + Radial = 2, + ///Disabled linear gradient. Useful for keeping gradient information around, but not having it displayed + LinearDisabled = 3, + ///Disabled radial gradient. Useful for keeping gradient information around, but not having it displayed + RadialDisabled = 4 +}; + + // OpenNurbs enums class ON_CLASS ON { @@ -1972,14 +2005,22 @@ public: morph_control_object = 0x20000, // some type of ON_MorphControl subd_object = 0x40000, // some type of ON_SubD, ON_SubDRef, ON_SubDComponentRef, ON_SubD.... loop_object = 0x80000, // some type of ON_BrepLoop - brepvertex_filter = 0x100000, // selection filter value - not a real object type (ON_BrepEdge, ON_SubDVertex) + brepvertex_filter = 0x100000, // selection filter value - not a real object type (ON_BrepVertex) polysrf_filter = 0x200000, // selection filter value - not a real object type - edge_filter = 0x400000, // selection filter value - not a real object type (ON_BrepEdge, ON_SubDEdge) + edge_filter = 0x400000, // selection filter value - not a real object type (ON_BrepEdge with associated ON_BrepTrim) polyedge_filter = 0x800000, // selection filter value - not a real object type + + + // NOTE WELL: + // The "mesh" vertex/edge/face filters and "meshcomponent_reference" + // are used to identify ON_Mesh and ON_SubD components. + // By the time subd_object was added, there were not enough unused bits + // for separate subd component filters. meshvertex_filter = 0x01000000, // selection filter value - not a real object type (ON_MeshTopologyVertex, ON_SubDVertex) meshedge_filter = 0x02000000, // selection filter value - not a real object type (ON_MeshTopologyEdge, ON_SubDEdge) meshface_filter = 0x04000000, // selection filter for ON_Mesh triangle, quad, ngon, or ON_SubDFace - not a real object type - meshcomponent_reference = 0x07000000, // an ON_MeshComponentRef or ON_SubDComponentRef + meshcomponent_reference = 0x07000000, // an ON_MeshComponentRef or ON_SubDComponentRef) + cage_object = 0x08000000, // some type of ON_NurbsCage phantom_object = 0x10000000, clipplane_object = 0x20000000, @@ -2237,6 +2278,13 @@ public: /// Attach point at right text horizontal advance (not glyph bounding box) /// Right = 2, + /// + /// Used for Leaders only + /// Attach point adjusts to Right or Left depending on leader tail direction in view + /// If tail direction is to the Left, alignment is Right + /// If tail direction is to the Right, alignment is Left + /// + Auto = 3, }; #pragma endregion @@ -2353,6 +2401,8 @@ public: subd_edge = 72, // m_index = ON_SubDEdge.m_id subd_face = 73, // m_index = ON_SubDFace.m_id + hatch_loop = 81, // m_index = ON_Hatch::m_loops[] array index + dim_linear_point = 100, dim_radial_point = 101, dim_angular_point = 102, @@ -2377,6 +2427,13 @@ public: static ON_COMPONENT_INDEX::TYPE Type(int i); + /* + Description: + Compare on m_type (as an int). + */ + static + int CompareType( const ON_COMPONENT_INDEX* lhs, const ON_COMPONENT_INDEX* rhs); + /* Description: Dictionary compare on m_type, m_index as ints. @@ -2427,7 +2484,7 @@ public: /* Returns: True if m_type is set to a TYPE enum value between - brep_vertex and polycurve_segment. + brep_vertex and dim_leader_point. */ bool IsSet() const; @@ -2524,6 +2581,12 @@ public: */ bool IsAnnotationComponentIndex() const; + /* + Returns: + True if m_type = hatch_loop and m_index >= 0. + */ + bool IsHatchLoopComponentIndex() const; + void Dump( class ON_TextLog& text_log )const; @@ -2565,6 +2628,8 @@ public: extrusion_cap_surface 0 = bottom cap, 1 = top cap extrusion_path -1 = entire path, 0 = start of path, 1 = end of path + hatch_loop ON_Hatch::m_loops[] array index + dim_linear_point linear dimension point index dim_radial_point radial dimension point index dim_angular_point angular dimension point index diff --git a/opennurbs_dimension.cpp b/opennurbs_dimension.cpp index 6a804f3e..226fbe13 100644 --- a/opennurbs_dimension.cpp +++ b/opennurbs_dimension.cpp @@ -77,8 +77,6 @@ void ON_Dimension::Internal_CopyFrom(const ON_Dimension& src) m_detail_measured = src.m_detail_measured; m_flip_arrow_1 = src.m_flip_arrow_1; m_flip_arrow_2 = src.m_flip_arrow_2; - m_force_arrows = src.m_force_arrows; - m_force_textpos = src.m_force_textpos; } bool ON_Dimension::IsValid(ON_TextLog* text_log) const @@ -294,23 +292,90 @@ void ON_Dimension::SetDetailMeasured(ON_UUID uuid) ON_Dimension::ForceArrow ON_Dimension::ForceArrowPosition() const { - return m_force_arrows; + ON_ERROR("Use ON_Dimension::ArrowFit(const ON_DimStyle* parent_style)"); + return ON_Dimension::ForceArrow::Auto; } void ON_Dimension::SetForceArrowPosition(ON_Dimension::ForceArrow force) { - m_force_arrows = force; + // + ON_ERROR("Use ON_Dimension::SetArrowFit(const ON_DimStyle* parent_style,ON_DimStyle::arrow_fit arrowfit)"); } ON_Dimension::ForceText ON_Dimension::ForceTextPosition() const { - return m_force_textpos; + ON_ERROR("Use ON_Dimension::TextFit(const ON_DimStyle* parent_style)"); + return ON_Dimension::ForceText::Auto; } void ON_Dimension::SetForceTextPosition(ON_Dimension::ForceText force) { - m_force_textpos = force; + ON_ERROR("Use ON_Dimension::SetTextFit(const ON_DimStyle* parent_style,ON_DimStyle::text_fit textfit)"); } +//-------------------------------- + +void ON_Dimension::SetForceDimLine( + const ON_DimStyle* parent_style, + bool force_dimline +) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (force_dimline != parent_style->ForceDimLine()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetForceDimLine(force_dimline); + override_style->SetFieldOverride(ON_DimStyle::field::ForceDimLine, bCreate); + } +} + +bool ON_Dimension::ForceDimLine( + const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::ForceDimLine).ForceDimLine(); + +} + +void ON_Dimension::SetTextFit( + const ON_DimStyle* parent_style, + ON_DimStyle::text_fit textfit) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (textfit != parent_style->TextFit()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetTextFit(textfit); + override_style->SetFieldOverride(ON_DimStyle::field::TextFit, bCreate); + } +} + +ON_DimStyle::text_fit ON_Dimension::TextFit( + const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::TextFit).TextFit(); +} + +void ON_Dimension::SetArrowFit( + const ON_DimStyle* parent_style, + ON_DimStyle::arrow_fit arrowfit) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (arrowfit != parent_style->ArrowFit()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetArrowFit(arrowfit); + override_style->SetFieldOverride(ON_DimStyle::field::TextFit, bCreate); + } +} + +ON_DimStyle::arrow_fit ON_Dimension::ArrowFit( + const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::ArrowFit).ArrowFit(); +} + //---------------------------------------------------------- // Class ON_DimLinear @@ -365,6 +430,9 @@ bool ON_Dimension::Internal_WriteDimension( if (!ON_Annotation::Internal_WriteAnnotation(archive)) break; + const ON_DimStyle& ds = archive.ArchiveCurrentDimStyle(); + + if (!archive.WriteString(m_user_text)) break; if (!archive.WriteDouble(0.0)) // OBSOLETE m_text_rotation @@ -377,8 +445,8 @@ bool ON_Dimension::Internal_WriteDimension( break; if (!archive.WriteBool(m_flip_arrow_2)) break; - unsigned int u = static_cast(m_force_arrows); - if (!archive.WriteInt(u)) + const unsigned int legacy_arrow_fit = static_cast(ArrowFit(&ds)); + if (!archive.WriteInt(legacy_arrow_fit)) break; if (!archive.WriteUuid(m_detail_measured)) break; @@ -386,8 +454,8 @@ bool ON_Dimension::Internal_WriteDimension( break; // content_version 1 - const unsigned int force_textpos_as_unsigned = static_cast(m_force_textpos); - if (!archive.WriteInt(force_textpos_as_unsigned)) + const unsigned int legacy_text_fit = static_cast(TextFit(&ds)); + if (!archive.WriteInt(legacy_text_fit)) break; rc = true; @@ -410,6 +478,9 @@ bool ON_Dimension::Internal_ReadDimension( if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) return false; + unsigned int legacy_arrow_fit = 0; + unsigned int legacy_text_fit = 0; + bool rc = false; for (;;) { @@ -431,14 +502,14 @@ bool ON_Dimension::Internal_ReadDimension( break; if (!archive.ReadBool(&m_flip_arrow_2)) break; - unsigned int u = static_cast(m_force_arrows); - if (!archive.ReadInt(&u)) + if (!archive.ReadInt(&legacy_arrow_fit)) break; - m_force_arrows = ON_Dimension::ForceArrowFromUnsigned(u); if (!archive.ReadUuid(m_detail_measured)) break; if (!archive.ReadDouble(&m_distance_scale)) break; + if (ON_nil_uuid == m_detail_measured) + m_distance_scale = 1.0; if (content_version <= 0) { @@ -447,10 +518,8 @@ bool ON_Dimension::Internal_ReadDimension( } // content_version 1 - unsigned int force_textpos_as_unsigned = static_cast(m_force_textpos); - if (!archive.ReadInt(&force_textpos_as_unsigned)) + if (!archive.ReadInt(&legacy_text_fit)) break; - m_force_textpos = ON_Dimension::ForceTextFromUnsigned(force_textpos_as_unsigned); rc = true; break; @@ -459,6 +528,23 @@ bool ON_Dimension::Internal_ReadDimension( if (!archive.EndRead3dmChunk()) rc = false; + const unsigned int version_v7_may_8_2019 = ON_VersionNumberConstruct(7, 0, 2019, 5, 8, 0); + if (rc && archive.ArchiveOpenNURBSVersion() < version_v7_may_8_2019 ) + { + // may 2019 - "arrow fit" and "text fit" moved from member settings on ON_Dimension + // to settings on ON_DimStyle. + // The file being read is older than the change. + const ON_DimStyle::arrow_fit new_arrow_fit = ON_DimStyle::ArrowFitFromUnsigned((unsigned int)legacy_arrow_fit); + const ON_DimStyle::text_fit new_text_fit = ON_DimStyle::TextFitFromUnsigned((unsigned int)legacy_text_fit); + const ON_DimStyle& ds = archive.ArchiveCurrentDimStyle(); + bool bSetArrowFit = (new_arrow_fit != ArrowFit(&ds)); + if (bSetArrowFit) + SetArrowFit(&ds, new_arrow_fit); + bool bSetTextFit = (new_text_fit != TextFit(&ds)); + if (bSetTextFit) + SetTextFit(&ds, new_text_fit); + } + return rc; } @@ -640,7 +726,7 @@ bool ON_DimLinear::GetTextXform( ON_Xform text_rotation(1.0); // Text rotation around text plane origin point // The amount past vertical where text flips to the other orientation - const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : cos(80.0*ON_PI / 180.0); + const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : cos(80.001 * ON_DEGREES_TO_RADIANS); ON_3dPoint text_center = ON_3dPoint::Origin; // Text starts out approximately centered at origin @@ -665,12 +751,12 @@ bool ON_DimLinear::GetTextXform( // See if arrows and text will all fit inside extension lines // or what has to be moved outside bool arrowflipped[2] = { false, false }; - //{ ArrowIsFlipped(0), ArrowIsFlipped(1) }; // manual override - ON_Dimension::ForceArrow force_arrow = ForceArrowPosition(); - if (ForceArrow::Outside == force_arrow) + + ON_DimStyle::arrow_fit arrow_fit = dimstyle->ArrowFit(); + if (ON_DimStyle::arrow_fit::ArrowsOutside == arrow_fit) arrowflipped[0] = arrowflipped[1] = true; - ON_Dimension::ForceText force_text = ForceTextPosition(); + ON_DimStyle::text_fit text_fit = dimstyle->TextFit(); bool text_outside = false; double dist = Measurement(); @@ -687,12 +773,13 @@ bool ON_DimLinear::GetTextXform( double total_text_width = (ON_DimStyle::ContentAngleStyle::Horizontal == text_angle_style) ? text_height : text_width; - if (force_text == ON_Dimension::ForceText::Left || force_text == ON_Dimension::ForceText::Right) + if (text_fit == ON_DimStyle::text_fit::TextLeft || text_fit == ON_DimStyle::text_fit::TextRight) { total_text_width = 0.0; text_outside = true; } - else if (force_text == ON_Dimension::ForceText::Inside) + //else if (force_text == ON_Dimension::ForceText::Inside) + else if (text_fit == ON_DimStyle::text_fit::TextInside) { total_text_width = 0.0; text_outside = false; @@ -702,7 +789,7 @@ bool ON_DimLinear::GetTextXform( static double arrow_width_factor = 1.1; double total_arrow_width = asz * arrow_width_factor * 2; - if (ForceArrow::Outside == force_arrow) + if (ON_DimStyle::arrow_fit::ArrowsOutside == arrow_fit) total_arrow_width = 0.0; if (total_arrow_width + total_text_width > dist) // arrows + text dont fit @@ -712,13 +799,14 @@ bool ON_DimLinear::GetTextXform( { // move text outside text_outside = true; - if (total_arrow_width > dist && ForceArrow::Auto == force_arrow) // arrows dont fit either + if (total_arrow_width > dist && ON_DimStyle::arrow_fit::Auto == arrow_fit) // arrows dont fit either { arrowflipped[0] = true; arrowflipped[1] = true; } } - else if (ForceArrow::Auto == force_arrow) // text fits + //else if (ForceArrow::Auto == force_arrow) // text fits + else if (ON_DimStyle::arrow_fit::Auto == arrow_fit) // text fits { // flip arrows arrowflipped[0] = true; @@ -735,9 +823,9 @@ bool ON_DimLinear::GetTextXform( { // move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width double x = (text_width * 0.5) + (text_gap * 3.0); - if (force_text == ON_Dimension::ForceText::Left || force_text == ON_Dimension::ForceText::HintLeft) + if (text_fit == ON_DimStyle::text_fit::TextLeft || text_fit == ON_DimStyle::text_fit::TextHintLeft) { - if (arrowflipped[0]) + if (arrowflipped[0]) x += (asz * arrow_width_factor); text_pt = ArrowPoint1().x < ArrowPoint2().x ? ArrowPoint1() : ArrowPoint2(); text_pt.x -= x; @@ -745,7 +833,7 @@ bool ON_DimLinear::GetTextXform( else // right or auto { if (arrowflipped[1]) - x += asz * (arrow_width_factor); + x += (asz * arrow_width_factor); text_pt = ArrowPoint1().x < ArrowPoint2().x ? ArrowPoint2() : ArrowPoint1(); text_pt.x += x; } @@ -795,71 +883,26 @@ bool ON_DimLinear::GetTextXform( } } - double XoX = dim_xaxis * view_xdir; - double XoY = dim_xaxis * view_ydir; - double YoX = dim_yaxis * view_xdir; - double YoY = dim_yaxis * view_ydir; - bool from_the_back = (view_zdir * dim_zaxis < 0.0); - if (nullptr != model_xform && model_xform->Determinant() < 0.0) - from_the_back = !from_the_back; + bool flip_x = false; + bool flip_y = false; - double upsign = 1.0; - - // This part shifts text to the correct side of the dimension line - if (fabs(XoX) > fabs(XoY)) // more horizontal - { - if (YoY > 0.0) - upsign = 1.0; - else - upsign = -1.0; - } - else // more vertical - { - if (from_the_back) - { - if (YoX < 0.0) - { - if (XoX < fliptol) - upsign = 1.0; - else - upsign = -1.0; - } - else - { - if (XoX > -fliptol) - upsign = -1.0; - else - upsign = 1.0; - } - } - else - { - if (YoX > 0.0) - { - if (XoX > fliptol) - upsign = 1.0; - else - upsign = -1.0; - } - else - { - if (XoX < -fliptol) - upsign = -1.0; - else - upsign = 1.0; - } - } - } + CalcTextFlip( + dim_xaxis, dim_yaxis, dim_zaxis, + view_xdir, view_ydir, view_zdir, + model_xform, + fliptol, + flip_x, + flip_y); if (ON_DimStyle::TextLocation::AboveDimLine == text_location) { // Moves the text to AboveLine if that's the alignment mode - double d = (text_height * 0.5 + text_gap) * upsign; - //if (from_the_back) - // d = -d; + double dy = flip_y ? -1.0 : 1.0; + double d = (text_height * 0.5 + text_gap) * dy; text_pt.y += d; } + ON_3dPoint text_point_3d = Plane().PointAt(text_pt.x, text_pt.y); // 3d text point dimplane_to_textpoint = ON_Xform::TranslationTransformation(text_point_3d - Plane().origin); // Move from dimplane origin to text point @@ -893,23 +936,13 @@ bool ON_DimLinear::GetTextXform( } else if (draw_forward) { - bool fx = false; - bool fy = false; - if (from_the_back) - upsign = -upsign; - fx = upsign < 0.0; - if (from_the_back) - fy = !fx; - else - fy = fx; - ON_Xform mxf; // Mirror xform for backwards text to adjust DrawForward - if (fx) + if (flip_x) { mxf.Mirror(text_center, ON_3dVector::XAxis); text_xform_out = text_xform_out * mxf; } - if (fy) + if (flip_y) { mxf.Mirror(text_center, ON_3dVector::YAxis); text_xform_out = text_xform_out * mxf; @@ -1859,32 +1892,36 @@ bool ON_DimLinear::GetDisplayLines( isline[2] = true; isline[3] = false; - if (UseDefaultTextPoint() && ON_DimStyle::TextLocation::InDimLine != text_location) + if (/*UseDefaultTextPoint() &&*/ ON_DimStyle::TextLocation::InDimLine != text_location) { - // If the dimline is under the text, and the text extends past the end of the dimline, - // make the dim line as long as the text if the text is offset sideways from the - // extension lines. - // If the text overlaps the extensions in both directions, it is centered and the - // dimension line will hang out just a little each way, so don't do it in that case - double t0, t1; - lines[2].ClosestPointTo(text_rect[0], &t0); - lines[2].ClosestPointTo(text_rect[1], &t1); - if (fabs(t0 - t1) > 0.00001) // if text rect has some width + if (m_use_default_text_point || fabs(m_user_text_point.y - m_dimline_pt.y) < style->TextGap() * dimscale * 0.75) { - if (t0 > t1) + // If the dimline is under the text, and the text extends past the end of the dimline, + // make the dim line as long as the text if the text is offset sideways from the + // extension lines. + // If the text is within 3/4 * text gap of default vertical position, draw the extended line + // If the text overlaps the extensions in both directions, it is centered and the + // dimension line will hang out just a little each way, so don't do it in that case + double t0, t1; + lines[2].ClosestPointTo(text_rect[0], &t0); + lines[2].ClosestPointTo(text_rect[1], &t1); + if (fabs(t0 - t1) > 0.00001) // if text rect has some width { - double t = t0; t0 = t1; t1 = t; + if (t0 > t1) + { + double t = t0; t0 = t1; t1 = t; + } + ON_Line l = lines[2]; + if (t0 < 0.0 && t1 < 1.0) + l.from = lines[2].PointAt(t0); + if (t1 > 1.0 && t0 > 0.0) + l.to = lines[2].PointAt(t1); + lines[2] = l; } - ON_Line l = lines[2]; - if (t0 < 0.0 && t1 < 1.0) - l.from = lines[2].PointAt(t0); - if (t1 > 1.0 && t0 > 0.0) - l.to = lines[2].PointAt(t1); - lines[2] = l; } } - if (ArrowIsFlipped(0) && ArrowIsFlipped(1)) + if (ArrowIsFlipped(0) && ArrowIsFlipped(1) && !style->ForceDimLine()) { // Don't draw dimline between extensions if arrows are flipped lines[3].from = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y); @@ -1960,6 +1997,7 @@ void ON_DimLinear::GetArrowXform( arrow_xform_out = xf; } + //---------------------------------------------------------- // Class ON_DimAngular @@ -2853,7 +2891,7 @@ bool ON_DimAngular::GetTextXform( if (!text->Get3dCorners(cp)) return false; - text_center = (cp[0] * dimscale + cp[2] * dimscale) / 2.0; + text_center = (cp[0] + cp[2]) / 2.0; text_width = (cp[1].x - cp[0].x) * dimscale; text_height = (cp[3].y - cp[0].y) * dimscale; @@ -2866,93 +2904,90 @@ bool ON_DimAngular::GetTextXform( bool arrowflipped[2] = { false, false }; bool text_outside = false; - ON_Dimension::ForceText force_text = ForceTextPosition(); - ON_Dimension::ForceArrow force_arrow = ForceArrowPosition(); - if (ForceArrow::Outside == force_arrow) + + ON_DimStyle::arrow_fit arrow_fit = dimstyle->ArrowFit(); + if (ON_DimStyle::arrow_fit::ArrowsOutside == arrow_fit) arrowflipped[0] = arrowflipped[1] = true; - else if (ForceArrow::Inside == force_arrow) - arrowflipped[0] = arrowflipped[1] = false; - //else if (ON_PI < Measurement()) // No flipping - // arrowflipped[0] = arrowflipped[1] = false; - //else // Flipping arrows won't happen on more than half-circle angles + + ON_DimStyle::text_fit text_fit = dimstyle->TextFit(); + + // See if arrows and text will all fit inside extension lines + // or what has to be moved outside + double asz = dimstyle->ArrowSize() * dimscale; + double dist = Radius() * Measurement(); + + double total_text_width = text_width; + if (text_fit != ON_DimStyle::text_fit::Auto) + total_text_width = 0.0; + else if (0.0 < total_text_width) + total_text_width += text_gap; + + if (text_fit != ON_DimStyle::text_fit::Auto && + text_fit != ON_DimStyle::text_fit::TextInside) + text_outside = true; + + static double arrow_width_factor = 1.5; + double total_arrow_width = asz * arrow_width_factor * 2; // min arrow tail space is asz/2 + + if (arrowflipped[0]) + total_arrow_width -= (asz * arrow_width_factor); + if (arrowflipped[1]) + total_arrow_width -= (asz * arrow_width_factor); + + if (total_arrow_width + total_text_width > dist) // arrows + text dont fit { - // See if arrows and text will all fit inside extension lines - // or what has to be moved outside - double asz = dimstyle->ArrowSize() * dimscale; - double dist = Radius() * Measurement(); - - double total_text_width = text_width; - if (force_text != ON_Dimension::ForceText::Auto) - total_text_width = 0.0; - else if (0.0 < total_text_width) - total_text_width += text_gap; - - if (force_text != ON_Dimension::ForceText::Auto && - force_text != ON_Dimension::ForceText::Inside) - text_outside = true; - - static double arrow_width_factor = 1.5; - double total_arrow_width = asz * arrow_width_factor * 2; // min arrow tail space is asz/2 - - if (arrowflipped[0]) - total_arrow_width -= (asz * arrow_width_factor); - if (arrowflipped[1]) - total_arrow_width -= (asz * arrow_width_factor); - - if (total_arrow_width + total_text_width > dist) // arrows + text dont fit + if (total_text_width > dist) // text doesnt fit { - if (total_text_width > dist) // text doesnt fit + // move text outside + text_outside = true; + if (total_arrow_width > dist && ON_DimStyle::arrow_fit::Auto == arrow_fit) // arrows dont fit either { - // move text outside - text_outside = true; - if (total_arrow_width > dist && ForceArrow::Auto == force_arrow) // arrows dont fit either - { - arrowflipped[0] = true; - arrowflipped[1] = true; - } - } - else if (ForceArrow::Auto == force_arrow) // text fits - { - // flip arrows arrowflipped[0] = true; arrowflipped[1] = true; } } - - // Dimension's ON_TextContent display text is stored at wcs origin coords until it is drawn - // text_xform positions text at the 3d point and rotation to draw - if (fabs(text_pt_2d.x) < ON_SQRT_EPSILON && fabs(text_pt_2d.y) < ON_SQRT_EPSILON) - text_pt_2d.Set(0.0, 0.0); - - if (text_outside && ON_DimStyle::ContentAngleStyle::Horizontal != text_angle_style && UseDefaultTextPoint()) + else if (ON_DimStyle::arrow_fit::Auto == arrow_fit) // text fits { - double radius = Radius(); - // move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width - double x = text_width * 0.5 + text_gap; - if (force_text == ON_Dimension::ForceText::Left) + // flip arrows + arrowflipped[0] = true; + arrowflipped[1] = true; + } + } + + // Dimension's ON_TextContent display text is stored at wcs origin coords until it is drawn + // text_xform positions text at the 3d point and rotation to draw + if (fabs(text_pt_2d.x) < ON_SQRT_EPSILON && fabs(text_pt_2d.y) < ON_SQRT_EPSILON) + text_pt_2d.Set(0.0, 0.0); + + if (text_outside && ON_DimStyle::ContentAngleStyle::Horizontal != text_angle_style && UseDefaultTextPoint()) + { + double radius = Radius(); + // move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width + double x = text_width * 0.5 + text_gap; + if (text_fit == ON_DimStyle::text_fit::TextLeft) + { + //if (arrowflipped[0]) + x += 1.5 * asz * arrow_width_factor; + text_pt_2d = ArrowPoint1(); + if (0.0 < radius) { - //if (arrowflipped[0]) - x += 1.5 * asz * arrow_width_factor; - text_pt_2d = ArrowPoint1(); - if (0.0 < radius) - { - double d_ang = x / radius; - text_pt_2d.Rotate(-d_ang, ON_2dPoint::Origin); - } + double d_ang = x / radius; + text_pt_2d.Rotate(-d_ang, ON_2dPoint::Origin); } - else + } + else + { + //if (arrowflipped[1]) + x += asz * arrow_width_factor; + text_pt_2d = ArrowPoint2(); + if (0.0 < radius) { - //if (arrowflipped[1]) - x += asz * arrow_width_factor; - text_pt_2d = ArrowPoint2(); - if (0.0 < radius) - { - double d_ang = x / radius; - text_pt_2d.Rotate(d_ang, ON_2dPoint::Origin); - } + double d_ang = x / radius; + text_pt_2d.Rotate(d_ang, ON_2dPoint::Origin); } } } + FlipArrow(0, arrowflipped[0]); FlipArrow(1, arrowflipped[1]); @@ -4195,13 +4230,17 @@ bool ON_DimRadial::GetTextXform( if (fx) { mxf.Mirror(text_center, textplane.xaxis); - text_xform_out = text_xform_out * mxf; + textpt_xf = textpt_xf * mxf; } if (fy) { mxf.Mirror(ON_3dPoint::Origin, textplane.yaxis); - text_xform_out = text_xform_out * mxf; + textpt_xf = textpt_xf * mxf; } + text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale); + text_xform_out = textrot_xf * text_xform_out; + text_xform_out = textpt_xf * text_xform_out; + text_xform_out = dimplane_xf * text_xform_out; } } } diff --git a/opennurbs_dimension.h b/opennurbs_dimension.h index e59b0a59..4d71c71b 100644 --- a/opennurbs_dimension.h +++ b/opennurbs_dimension.h @@ -28,7 +28,7 @@ class ON_CLASS ON_Dimension : public ON_Annotation public: #pragma region RH_C_SHARED_ENUM [ON_Dimension::ForceArrow] [Rhino.Geometry.Dimension.ForceArrow] [nested:int] /// - /// Arrowheads forced Inside, or Outside of extension lines, or moved to fit. + /// OBSOLETE enum do not use. /// enum class ForceArrow : unsigned int { @@ -46,7 +46,7 @@ public: #pragma region RH_C_SHARED_ENUM [ON_Dimension::ForceText] [Rhino.Geometry.Dimension.ForceText] [nested:int] /// - /// Text forced Inside, Right or Left of extension lines, or moved to fit (Auto). + /// OBSOLETE enum do not use. /// enum class ForceText : unsigned int { @@ -58,9 +58,9 @@ public: Right = 2, /// Left = 3, - /// If override isn't specified and text doesn't fit, move it right + /// HintRight = 4, - /// If override isn't specified and text doesn't fit, move it left + /// HintLeft = 5, }; #pragma endregion @@ -170,13 +170,43 @@ public: ON_3dPoint points[13], bool ispoint[13]); - + // Obsolete + ON_DEPRECATED_MSG("ON_Dimension::ArrowFit(const ON_DimStyle* parent_style)") ON_Dimension::ForceArrow ForceArrowPosition() const; + + ON_DEPRECATED_MSG("ON_Dimension::SetArrowFit(const ON_DimStyle* parent_style,ON_DimStyle::arrow_fit arrowfit)") void SetForceArrowPosition(ForceArrow force); + ON_DEPRECATED_MSG("ON_Dimension::TextFit(const ON_DimStyle* parent_style)") ON_Dimension::ForceText ForceTextPosition() const; + + ON_DEPRECATED_MSG("ON_Dimension::SetTextFit(const ON_DimStyle* parent_style,ON_DimStyle::text_fit textfit)") void SetForceTextPosition(ForceText force); + void SetForceDimLine( + const ON_DimStyle* parent_style, + bool forcedimline + ); + + bool ForceDimLine( + const ON_DimStyle* parent_style) const; + + void SetTextFit( + const ON_DimStyle* parent_style, + ON_DimStyle::text_fit textfit); + + ON_DimStyle::text_fit TextFit( + const ON_DimStyle* parent_style) const; + + void SetArrowFit( + const ON_DimStyle* parent_style, + ON_DimStyle::arrow_fit arrowfit); + + ON_DimStyle::arrow_fit ArrowFit( + const ON_DimStyle* parent_style) const; + + + protected: ON_wString m_user_text = L"<>"; // If user overridden, or "<>" to use default double m_reserved = 0.0; @@ -188,8 +218,8 @@ protected: mutable bool m_flip_arrow_1 = false; mutable bool m_flip_arrow_2 = false; mutable bool m_text_outside = false; - ForceArrow m_force_arrows = ForceArrow::Auto; - ForceText m_force_textpos = ForceText::Auto; + unsigned int m_reserved98 = 0; + unsigned int m_reserved99 = 0; // UUID of detail if dimension is in page space measuring model space geometry @@ -416,6 +446,7 @@ public: bool from_the_back, ON_Xform& arrow_xform_out) const; + protected: ON_2dPoint m_def_pt_2 = ON_2dPoint::UnsetPoint; ON_2dPoint m_dimline_pt = ON_2dPoint::UnsetPoint; diff --git a/opennurbs_dimensionformat.cpp b/opennurbs_dimensionformat.cpp index a312331b..cd1b93c8 100644 --- a/opennurbs_dimensionformat.cpp +++ b/opennurbs_dimensionformat.cpp @@ -229,7 +229,10 @@ bool ON_NumberFormatter::FormatNumber( { if (0 != wholenumber) { - sFormat.Format(L"%d ", (int)wholenumber); + if (0 != numerator) + sFormat.Format(L"%d ", (int)wholenumber); + else + sFormat.Format(L"%d", (int)wholenumber); } if (0 != numerator) diff --git a/opennurbs_dimensionstyle.cpp b/opennurbs_dimensionstyle.cpp index 5c8aba37..41f80718 100644 --- a/opennurbs_dimensionstyle.cpp +++ b/opennurbs_dimensionstyle.cpp @@ -421,6 +421,10 @@ ON_DimStyle::field ON_DimStyle::FieldFromUnsigned( // OBSOLETE // //ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::OBSOLETE_AlternateDimensionUnitSystem_); ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimensionLengthDisplay); ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AlternateDimensionLengthDisplay); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ForceDimLine); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextFit); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArrowFit); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DecimalSeparator); } if (field_as_unsigned > static_cast(ON_DimStyle::field::AlternateDimensionLengthDisplay)) { @@ -488,6 +492,38 @@ ON_DimStyle::OBSOLETE_length_format ON_DimStyle::OBSOLETE_LengthFormatFromUnsign return (ON_DimStyle::OBSOLETE_length_format::Decimal); } +ON_DimStyle::arrow_fit ON_DimStyle::ArrowFitFromUnsigned( + unsigned int arrow_fit_as_unsigned +) +{ + switch (arrow_fit_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::arrow_fit::Auto); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::arrow_fit::ArrowsInside); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::arrow_fit::ArrowsOutside); + } + ON_ERROR("invalid arrow_fit_as_unsigned parameter."); + return (ON_DimStyle::arrow_fit::Auto); +} + +ON_DimStyle::text_fit ON_DimStyle::TextFitFromUnsigned( + unsigned int text_fit_as_unsigned +) +{ + switch (text_fit_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::Auto); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::TextInside); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::TextRight); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::TextLeft); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::TextHintRight); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::TextHintLeft); + } + ON_ERROR("invalid text_fit_as_unsigned parameter."); + return (ON_DimStyle::text_fit::Auto); +} + + ON_DimStyle::OBSOLETE_length_format ON_DimStyle::OBSOLETE_LengthFormatFromLengthDisplay( ON_DimStyle::LengthDisplay dimension_length_display, ON::LengthUnitSystem model_unit_system @@ -551,7 +587,7 @@ ON_DimStyle::suppress_zero ON_DimStyle::ZeroSuppressFromUnsigned( ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::suppress_zero::SuppressZeroFeetAndZeroInches); break; } - ON_ERROR("invalid suppress_ero_as_unsigned parameter."); + ON_ERROR("invalid suppress_zero_as_unsigned parameter."); return ON_DimStyle::suppress_zero::None; } @@ -1239,7 +1275,8 @@ void ON_DimStyle::Internal_ContentChange() const ON_DimStyle::ON_DimStyle() : ON_ModelComponent(ON_ModelComponent::Type::DimStyle) -{} +{ +} struct V5_to_V6_field_id_map { @@ -2102,7 +2139,12 @@ bool ON_DimStyle::CompareFields(const ON_DimStyle& style) const m_dimradial_text_orientation == style.m_dimradial_text_orientation && m_dim_text_angle_style == style.m_dim_text_angle_style && m_dimradial_text_angle_style == style.m_dimradial_text_angle_style && - m_text_underlined == style.m_text_underlined + m_text_underlined == style.m_text_underlined && + + m_bForceDimLine == style.m_bForceDimLine && + m_ArrowFit == style.m_ArrowFit && + m_TextFit == style.m_TextFit && + m_decimal_separator == style.m_decimal_separator ) return true; else @@ -2142,7 +2184,7 @@ bool ON_DimStyle::Write( ON_BinaryArchive& file // serialize definition to binary archive ) const { - if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 7)) + if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 9)) return false; bool rc = false; @@ -2471,6 +2513,19 @@ bool ON_DimStyle::Write( if (!file.WriteInt(u)) break; // END chunk version 1.7 information + if (!file.WriteBool(m_bForceDimLine)) break; + + u = static_cast(m_TextFit); + if (!file.WriteInt(u)) break; + + u = static_cast(m_ArrowFit); + if (!file.WriteInt(u)) break; + // END chunk version 1.8 information + + u = static_cast(m_decimal_separator); + if (!file.WriteInt(u)) break; + // END chunk version 1.9 information + rc = true; break; } @@ -2998,6 +3053,33 @@ bool ON_DimStyle::Read( m_centermark_style = ON_DimStyle::CentermarkStyleFromUnsigned(u); // END chunk version 1.7 information + if (minor_version <= 7) + { + rc = true; + break; + } + + if (!file.ReadBool(&m_bForceDimLine)) break; + + u = static_cast(m_TextFit); + if (!file.ReadInt(&u)) break; + m_TextFit = ON_DimStyle::TextFitFromUnsigned(u); + + u = static_cast(m_ArrowFit); + if (!file.ReadInt(&u)) break; + m_ArrowFit = ON_DimStyle::ArrowFitFromUnsigned(u); + // END chunk version 1.8 information + + if (minor_version <= 8) + { + rc = true; + break; + } + u = static_cast(m_decimal_separator); + if (!file.ReadInt(&u)) break; + m_decimal_separator = (wchar_t)u; + // END chunk version 1.9 information + rc = true; break; } @@ -3502,6 +3584,11 @@ const class ON_SHA1_Hash ON_DimStyle::TextPositionPropertiesHash() const sha1.AccumulateUnsigned32(static_cast(m_dimradial_text_angle_style)); sha1.AccumulateUnsigned32(static_cast(m_text_underlined)); + sha1.AccumulateUnsigned32(static_cast(m_ArrowFit)); + sha1.AccumulateUnsigned32(static_cast(m_TextFit)); + + sha1.AccumulateUnsigned32(static_cast(m_decimal_separator)); + // Save hash in mutable m_text_position_properties_hash m_text_position_properties_hash = sha1.Hash(); } @@ -3576,7 +3663,13 @@ const class ON_SHA1_Hash& ON_DimStyle::ContentHash() const sha1.AccumulateBool(m_draw_forward); sha1.AccumulateBool(m_signed_ordinate); - + + sha1.AccumulateBool(static_cast(m_bForceDimLine)); + sha1.AccumulateUnsigned32(static_cast(m_ArrowFit)); + sha1.AccumulateUnsigned32(static_cast(m_TextFit)); + + sha1.AccumulateUnsigned32(static_cast(m_decimal_separator)); + // Save hash in mutable m_content_hash m_content_hash = sha1.Hash(); } @@ -4804,6 +4897,62 @@ void ON_DimStyle::SetTextUnderlined(bool underlined) Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::TextUnderlined); } +bool ON_DimStyle::ForceDimLine() const +{ + return m_bForceDimLine; +} +void ON_DimStyle::SetForceDimLine(bool forcedimline) +{ + if (m_bForceDimLine != forcedimline) + { + m_bForceDimLine = forcedimline; + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::ForceDimLine); +} + +void ON_DimStyle::SetArrowFit(ON_DimStyle::arrow_fit arrowfit) +{ + if (m_ArrowFit != arrowfit) + { + m_ArrowFit = arrowfit; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::ArrowFit); +} + +ON_DimStyle::arrow_fit ON_DimStyle::ArrowFit() const +{ + return m_ArrowFit; +} + +void ON_DimStyle::SetTextFit(ON_DimStyle::text_fit textfit) +{ + if (m_TextFit != textfit) + { + m_TextFit = textfit; + Internal_TextPositionPropertiesChange(); + } + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::TextFit); +} + +ON_DimStyle::text_fit ON_DimStyle::TextFit() const +{ + return m_TextFit; +} + +void ON_DimStyle::SetDecimalSeparator(wchar_t separator) +{ + if(separator == ON_wString::DecimalAsComma) + m_decimal_separator = ON_wString::DecimalAsComma; + else + m_decimal_separator = ON_wString::DecimalAsPeriod; +} + +wchar_t ON_DimStyle::DecimalSeparator() const +{ + return m_decimal_separator; +} + ON__UINT32* ON_DimStyle::Internal_GetOverrideParentBit(ON_DimStyle::field field_id, ON__UINT32* mask) const { unsigned int bitdex = 0; @@ -5377,7 +5526,18 @@ void ON_DimStyle::OverrideFields(const ON_DimStyle& source, const ON_DimStyle& p case ON_DimStyle::field::AlternateDimensionLengthDisplay: ON_INTERNAL_UPDATE_PROPERTY(AlternateDimensionLengthDisplay); break; - + case ON_DimStyle::field::ForceDimLine: + ON_INTERNAL_UPDATE_PROPERTY(ForceDimLine); + break; + case ON_DimStyle::field::TextFit: + ON_INTERNAL_UPDATE_PROPERTY(TextFit); + break; + case ON_DimStyle::field::ArrowFit: + ON_INTERNAL_UPDATE_PROPERTY(ArrowFit); + break; + case ON_DimStyle::field::DecimalSeparator: + ON_INTERNAL_UPDATE_PROPERTY(DecimalSeparator); + break; default: ON_ERROR("The switch statement in this function has gaps!"); SetFieldOverride(field_id, false); diff --git a/opennurbs_dimensionstyle.h b/opennurbs_dimensionstyle.h index 6edadb01..ed596bd1 100644 --- a/opennurbs_dimensionstyle.h +++ b/opennurbs_dimensionstyle.h @@ -856,8 +856,28 @@ public: /// AlternateDimensionLengthDisplay = 110, + /// + /// Force dimension line to draw when text is moved outside + /// + ForceDimLine = 111, + + /// + /// Arrow position when arrows won't fit between extensions + /// + ArrowFit = 112, + + /// + /// Text position when text won't fit between extensions + /// + TextFit = 113, + + /// + /// Character to use for decimal separator in dimension text + /// + DecimalSeparator = 114, + /// Every enum UINT value that identifies a valid dimension style property is less than the UINT value of Count. - Count = 111 + Count = 115 }; #pragma endregion @@ -1038,6 +1058,50 @@ public: ); +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::arrow_fit] [Rhino.DocObjects.DimensionStyle.ArrowFit] [nested:byte] + /// + /// Arrow display position inside or outside extension lines + /// + enum class arrow_fit : unsigned char + { + /// Auto - Display when space permits + Auto = 0, + /// Force arrows inside extensions + ArrowsInside = 1, + /// Force arrows outside extensions + ArrowsOutside = 2, + }; +#pragma endregion + + static ON_DimStyle::arrow_fit ArrowFitFromUnsigned( + unsigned int arrow_fit_as_unsigned + ); + +#pragma region RH_C_SHARED_ENUM [ON_DimStyle::text_fit] [Rhino.DocObjects.DimensionStyle.TextFit] [nested:byte] + /// + /// Text display position inside or outside extension lines + /// + enum class text_fit : unsigned char + { + /// Auto - Display inside when space permits + Auto = 0, + /// Force text inside extensions + TextInside = 1, + /// Force text outside to the right of extensions + TextRight = 2, + /// Force text outside to the left of extensions + TextLeft = 3, + /// Move text outside to the right of extensions when it won't fit inside + TextHintRight = 4, + /// Move text outside to the left of extensions when it won't fit inside + TextHintLeft = 5, + }; +#pragma endregion + + static ON_DimStyle::text_fit TextFitFromUnsigned( + unsigned int text_fit_as_unsigned + ); + static ON_DimStyle::LengthDisplay LengthDisplayFromUnitsAndFormat( ON::LengthUnitSystem units, ON_DimStyle::OBSOLETE_length_format lengthformat @@ -1095,6 +1159,7 @@ public: unsigned int model_sn ) const; + private: /* Returns: @@ -1797,13 +1862,13 @@ public: True if zero_suppression is a valid setting when DimensionLengthDiplay = dimension_length_display Remarks: - LengthDisplay: Inch fractional No zero suppression matches - LengthDisplay : FeetAndInches Zero suppress can be + LengthDisplay: Inch fractional – No zero suppression matches + LengthDisplay : FeetAndInches – Zero suppress can be None, Suppress zero feet, Suppress zero inches or Suppress zero feet and zero inches. - LengthDisplay : ModelUnits or any Decimal mode Zero suppress can be + LengthDisplay : ModelUnits or any Decimal mode – Zero suppress can be None, Suppress leading, Suppress trailing or @@ -1932,6 +1997,18 @@ public: bool TextUnderlined() const; void SetTextUnderlined(bool underlined); + bool ForceDimLine() const; + void SetForceDimLine(bool forcedimline); + + void SetArrowFit(ON_DimStyle::arrow_fit arrowfit); + ON_DimStyle::arrow_fit ArrowFit() const; + + void SetTextFit(ON_DimStyle::text_fit textfit); + ON_DimStyle::text_fit TextFit() const; + + void SetDecimalSeparator(wchar_t separator); + wchar_t DecimalSeparator() const; + //double ModelSize() const; //void SetModelSize(double size); //double PaperSize() const; @@ -2051,7 +2128,11 @@ private: bool m_bAlternate = false; // (dimalt) display alternate dimension string (or not) - + + bool m_bForceDimLine = true; // 4/30/2019 + ON_DimStyle::arrow_fit m_ArrowFit = ON_DimStyle::arrow_fit::Auto; // 4/30/2019 + ON_DimStyle::text_fit m_TextFit = ON_DimStyle::text_fit::Auto; // 4/30/2019 + wchar_t m_decimal_separator = ON_wString::DecimalAsPeriod; ON_wString m_prefix; // string preceding dimension value string ON_wString m_suffix; // string following dimension value string @@ -2063,8 +2144,8 @@ private: bool m_bSuppressExtension1 = false; // flag to not draw extension lines bool m_bSuppressExtension2 = false; // flag to not draw extension lines bool m_bReserved1 = false; - bool m_bReserved2 = false; - + bool m_bReserved2 = false; + // m_field_override_count // number of ON_DimStyle::field settings that are independent of the parent dimension style. // (not inherited from) @@ -2153,6 +2234,10 @@ private: double m_fixed_extension_len = 1.0; // Fixed extension line length if m_fixed_extension_len_on is true bool m_fixed_extension_len_on = false; // true: use fixed_extension_len, false: don't use m_fixed_extension_len + + unsigned char m_ReservedChar1 = 0; + unsigned short m_ReservedShort1 = 0; + unsigned int m_ReservedInt1 = 0; double m_text_rotation = 0.0; // Dimension text rotation around text point (radians) int m_alternate_tolerance_resolution = 4; // for decimal, digits past the decimal point, fractions: 1/2^n @@ -2160,6 +2245,9 @@ private: bool m_suppress_arrow1 = false; // false: dont suppress, true: suppress bool m_suppress_arrow2 = false; // false: dont suppress, true: suppress + + unsigned short m_ReservedShort2 = 0; + int m_textmove_leader = 0; // 0: move text anywhere, 1: add leader when moving text int m_arclength_sym = 0; // 0: symbol before dim text, 1: symbol above dim text, no symbol double m_stack_textheight_fraction = 0.7; // fraction of main text height @@ -2174,7 +2262,8 @@ private: ON_DimStyle::suppress_zero m_ang_zero_suppress = ON_DimStyle::suppress_zero::None; bool m_alt_below = false; // true: display alternate text below main text - // false: display alternate text after main text + + // false: display alternate text after main text ON_Arrowhead::arrow_type m_arrow_type_1 = ON_Arrowhead::arrow_type::SolidTriangle; // Arrow types for ON_Dimension derived dimensions ON_Arrowhead::arrow_type m_arrow_type_2 = ON_Arrowhead::arrow_type::SolidTriangle; ON_Arrowhead::arrow_type m_leader_arrow_type = ON_Arrowhead::arrow_type::SolidTriangle; @@ -2194,10 +2283,12 @@ private: ON_DimStyle::leader_curve_type m_leader_curve_type = ON_DimStyle::leader_curve_type::Polyline; double m_leader_content_angle = 0.0; bool m_leader_has_landing = true; + double m_leader_landing_length = 1.0; bool m_draw_forward = true; bool m_signed_ordinate = true; + ON_ScaleValue m_scale_value = ON_ScaleValue::OneToOne; // Unit system for dimension rendering sizes like text height, and arrow head length. diff --git a/opennurbs_dll.cpp b/opennurbs_dll.cpp index 4ac2bed9..d6ab1f29 100644 --- a/opennurbs_dll.cpp +++ b/opennurbs_dll.cpp @@ -40,7 +40,8 @@ int APIENTRY DllMain( HANDLE hModule, bRunning = true; } - switch( ul_reason_for_call ) { + switch( ul_reason_for_call ) + { case DLL_PROCESS_ATTACH: ON_ClassId::IncrementMark(); // make sure each DLL that each process that diff --git a/opennurbs_error.cpp b/opennurbs_error.cpp index 9c546bdd..4ffcc36e 100644 --- a/opennurbs_error.cpp +++ b/opennurbs_error.cpp @@ -124,6 +124,13 @@ static void ON_IncrementWarningCount() ON_WARNING_COUNT++; } +void ON_SubDIncrementErrorCount() +{ + ON_ERROR_COUNT++; + ON_SubD::ErrorCount++; +} + + bool ON_IsNotValid() { return false; diff --git a/opennurbs_extensions.cpp b/opennurbs_extensions.cpp index e3b7dab6..b976689e 100644 --- a/opennurbs_extensions.cpp +++ b/opennurbs_extensions.cpp @@ -2173,20 +2173,10 @@ bool ONX_Model::IncrementalReadBegin( rc = archive.Read3dmBitmap(&bitmap); if ( rc==0 ) break; // end of bitmap table - - for (;;) - { - if ( rc < 0 ) - break; - - if ( AddModelComponentForExperts(bitmap,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) - { - delete bitmap; - continue; - } - + if (rc < 0) break; - } + if ( AddModelComponentForExperts(bitmap,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) + delete bitmap; } } @@ -2209,19 +2199,10 @@ bool ONX_Model::IncrementalReadBegin( rc = archive.Read3dmTextureMapping(&texture_mapping); if ( rc==0 ) break; // end of texture_mapping table - for (;;) - { - if ( rc < 0 ) - break; - - if (AddModelComponentForExperts(texture_mapping, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) - { - delete texture_mapping; - continue; - } - + if ( rc < 0 ) break; - } + if (AddModelComponentForExperts(texture_mapping, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) + delete texture_mapping; } } @@ -2251,36 +2232,22 @@ bool ONX_Model::IncrementalReadBegin( rc = archive.Read3dmMaterial(&material); if ( rc==0 ) break; // end of material table - for (;;) - { - if ( rc < 0 ) - { - break; - } - - bool bSetAsCurrent = - (bSetCurrentById && settings_current_id == material->Id()) - || (bSetCurrentByIndex && settings_current_index == material->Index()); - - if ( AddModelComponentForExperts(material,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) - { - delete material; - continue; - } - - if (bSetAsCurrent) - { - m_settings.SetCurrentMaterialId(material->Id()); - bSetCurrentById = false; - bSetCurrentByIndex = false; - } - - material = nullptr; - + if (rc < 0) break; - } - if ( nullptr != material) + // index or id might be modified by AddModelComponentForExperts() + const bool bSetAsCurrent = + (bSetCurrentById && settings_current_id == material->Id()) + || (bSetCurrentByIndex && settings_current_index == material->Index()); + if ( AddModelComponentForExperts(material,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) + { delete material; + } + else if (bSetAsCurrent) + { + m_settings.SetCurrentMaterialId(material->Id()); + bSetCurrentById = false; + bSetCurrentByIndex = false; + } } } @@ -2310,22 +2277,17 @@ bool ONX_Model::IncrementalReadBegin( rc = archive.Read3dmLinetype(&line_pattern); if ( rc==0 ) break; // end of linetype table - if ( rc < 0 ) - { - continue; - } - - bool bSetAsCurrent = + if (rc < 0) + break; + // index or id might be modified by AddModelComponentForExperts() + const bool bSetAsCurrent = (bSetCurrentById && settings_current_id == line_pattern->Id()) || (bSetCurrentByIndex && settings_current_index == line_pattern->Index()); - if ( AddModelComponentForExperts(line_pattern,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) { delete line_pattern; - continue; } - - if (bSetAsCurrent) + else if (bSetAsCurrent) { m_settings.SetCurrentLinePatternId(line_pattern->Id()); bSetCurrentById = false; @@ -2359,22 +2321,17 @@ bool ONX_Model::IncrementalReadBegin( rc = archive.Read3dmLayer(&layer); if ( rc==0 ) break; // end of layer table - if ( rc < 0 ) - { - continue; - } - - bool bSetAsCurrent = + if (rc < 0) + break; + // index or id might be modified by AddModelComponentForExperts() + const bool bSetAsCurrent = (bSetCurrentById && settings_current_id == layer->Id()) || (bSetCurrentByIndex && settings_current_index == layer->Index()); - if (AddModelComponentForExperts(layer, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) { delete layer; - continue; } - - if (bSetAsCurrent) + else if (bSetAsCurrent) { m_settings.SetCurrentLayerId(layer->Id()); bSetCurrentById = false; @@ -2432,21 +2389,10 @@ bool ONX_Model::IncrementalReadBegin( rc = archive.Read3dmGroup(&group); if ( rc==0 ) break; // end of group table - - for (;;) - { - if ( rc < 0 ) - { - break; - } - - if (AddModelComponentForExperts(group, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) - { - delete group; - continue; - } + if (rc < 0) break; - } + if (AddModelComponentForExperts(group, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) + delete group; } } @@ -2476,21 +2422,16 @@ bool ONX_Model::IncrementalReadBegin( if ( rc==0 ) break; // end of dimstyle table if ( rc < 0 ) - { break; - } - - bool bSetAsCurrent = + // index or id might be modified by AddModelComponentForExperts() + const bool bSetAsCurrent = (bSetCurrentById && settings_current_id == dimension_style->Id()) || (bSetCurrentByIndex && settings_current_index == dimension_style->Index()); - if (AddModelComponentForExperts(dimension_style, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) { delete dimension_style; - continue; } - - if (bSetAsCurrent) + else if (bSetAsCurrent) { m_settings.SetCurrentDimensionStyleId(dimension_style->Id()); bSetCurrentById = false; @@ -2517,24 +2458,11 @@ bool ONX_Model::IncrementalReadBegin( ON_ModelGeometryComponent* model_light = nullptr; rc = archive.Read3dmModelLight(&model_light); if (rc == 0) - { break; // end of light table - } - for (;;) - { - if ( rc < 0 ) - { - break; - } - - if (AddModelComponentForExperts(model_light, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) - { - delete model_light; - continue; - } - + if ( rc < 0 ) break; - } + if (AddModelComponentForExperts(model_light, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) + delete model_light; } } @@ -2565,18 +2493,15 @@ bool ONX_Model::IncrementalReadBegin( break; // end of hatchpattern table if ( rc < 0 ) break; - - bool bSetAsCurrent = + // index or id might be modified by AddModelComponentForExperts() + const bool bSetAsCurrent = (bSetCurrentById && settings_current_id == hatch_pattern->Id()) || (bSetCurrentByIndex && settings_current_index == hatch_pattern->Index()); - if (AddModelComponentForExperts(hatch_pattern, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) { delete hatch_pattern; - continue; } - - if (bSetAsCurrent) + else if (bSetAsCurrent) { m_settings.SetCurrentHatchPatternId(hatch_pattern->Id()); bSetCurrentById = false; @@ -2606,12 +2531,8 @@ bool ONX_Model::IncrementalReadBegin( break; // end of instance definition table if ( rc < 0 ) break; - if (AddModelComponentForExperts(instance_definition, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) - { delete instance_definition; - continue; - } } } @@ -2757,9 +2678,7 @@ bool ONX_Model::IncrementalReadFinish( if ( rc == 0 ) break; // end of history record table if ( rc < 0 ) - { break; - } if ( AddModelComponentForExperts(pHistoryRecord,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) { delete pHistoryRecord; @@ -3020,13 +2939,13 @@ bool ONX_Model::Write( { if ( 0 != version ) { - if ( version < 2 - || version > ON_BinaryArchive::CurrentArchiveVersion() - || (version >= 50 && 0 != (version%10)) - || (version < 50 && version > ON_BinaryArchive::CurrentArchiveVersion()/10) - ) + if ( + version < 2 + || version > ON_BinaryArchive::CurrentArchiveVersion() + || (version < 50 && version > ON_BinaryArchive::CurrentArchiveVersion()/10) + || (version >= 50 && 0 != (version % 10)) + ) { - // version must be 0, 2, 3, 4, 5 or 50 version = 0; if ( error_log) error_log->Print("ONX_Model::Write version parameter = %d; it must be 0, or >= 2 and <= %d, or a multiple of 10 >= 50 and <= %d.\n", version,ON_BinaryArchive::CurrentArchiveVersion()/10,ON_BinaryArchive::CurrentArchiveVersion()); diff --git a/opennurbs_font.cpp b/opennurbs_font.cpp index 6598be9c..8c7902d1 100644 --- a/opennurbs_font.cpp +++ b/opennurbs_font.cpp @@ -1687,6 +1687,114 @@ const ON_Font* ON_FontFaceQuartet::BoldItalicFace() const return m_bold_italic; } +ON_FontFaceQuartet::Member ON_FontFaceQuartet::QuartetMember( + const ON_Font* font +) const +{ + if (nullptr == font || m_quartet_name.IsEmpty()) + return ON_FontFaceQuartet::Member::Unset; + + if ( + false == m_quartet_name.EqualOrdinal(font->QuartetName(ON_Font::NameLocale::Localized), true) + && false == m_quartet_name.EqualOrdinal(font->QuartetName(ON_Font::NameLocale::English), true) + ) + return ON_FontFaceQuartet::Member::Unset; + + ON_FontFaceQuartet::Member m[2] = { ON_FontFaceQuartet::Member ::Unset,ON_FontFaceQuartet::Member::Unset}; + if ( font->IsItalicOrOblique() ) + { + m[0] = ON_FontFaceQuartet::Member::Italic; + m[1] = ON_FontFaceQuartet::Member::BoldItalic; + } + else + { + m[0] = ON_FontFaceQuartet::Member::Regular; + m[1] = ON_FontFaceQuartet::Member::Bold; + } + + const ON_Font* q[2] = { Face(m[0]),Face(m[1]) }; + + if (font == q[0]) + return m[0]; + if (font == q[1]) + return m[1]; + + ON_wString s[4]; + for (int i = 0; i < 2; i++) + { + if (nullptr == q[i]) + continue; + +#if defined(ON_RUNTIME_WIN) + // style, weight, and LOGFONT name is the most reliable way to uniquely identify quartet members on Windows. + // We've already matched style. + if (q[i]->FontWeight() != font->FontWeight()) + continue; + s[0] = q[i]->WindowsLogfontName(ON_Font::NameLocale::Localized); + s[1] = q[i]->WindowsLogfontName(ON_Font::NameLocale::English); + s[2] = font->WindowsLogfontName(ON_Font::NameLocale::Localized); + s[3] = font->WindowsLogfontName(ON_Font::NameLocale::English); +#else + // style and PostScript name is the most reliable way to uniquely identify quartet members on Mac OS. + // We've already matched style. + s[0] = q[i]->PostScriptName(ON_Font::NameLocale::Localized); + s[1] = q[i]->PostScriptName(ON_Font::NameLocale::English); + s[2] = font->PostScriptName(ON_Font::NameLocale::Localized); + s[3] = font->PostScriptName(ON_Font::NameLocale::English); +#endif + if (s[0].IsNotEmpty() && (s[0].EqualOrdinal(s[2], true) || s[0].EqualOrdinal(s[3], true))) + return m[i]; + if (s[1].IsNotEmpty() && (s[1].EqualOrdinal(s[2], true) || s[1].EqualOrdinal(s[3], true))) + return m[i]; + } + return ON_FontFaceQuartet::Member::Unset; +} + +const ON_Font* ON_FontFaceQuartet::Face( + ON_FontFaceQuartet::Member member +) const +{ + switch (member) + { + case ON_FontFaceQuartet::Member::Regular: + return m_regular; + case ON_FontFaceQuartet::Member::Bold: + return m_bold; + case ON_FontFaceQuartet::Member::Italic: + return m_italic; + case ON_FontFaceQuartet::Member::BoldItalic: + return m_bold_italic; + } + return nullptr; +} + +const ON_Font* ON_FontFaceQuartet::ClosestFace( + ON_FontFaceQuartet::Member member +) const +{ + bool bPreferBold = false; + bool bPreferItalic = false; + switch (member) + { + case ON_FontFaceQuartet::Member::Regular: + break; + case ON_FontFaceQuartet::Member::Bold: + bPreferBold = true; + break; + case ON_FontFaceQuartet::Member::Italic: + bPreferItalic = true; + break; + case ON_FontFaceQuartet::Member::BoldItalic: + bPreferItalic = true; + bPreferBold = true; + break; + default: + return nullptr; + } + return ClosestFace(bPreferBold,bPreferItalic); +} + + const ON_Font* ON_FontFaceQuartet::Face( bool bBold, bool bItalic @@ -1820,6 +1928,23 @@ int ON_FontList::CompareFamilyAndFaceName(ON_Font const* const* lhs, ON_Font con return rc; } +int ON_FontList::CompareFamilyAndWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs) +{ + ON_ManagedFonts_CompareFontPointer(lhs, rhs); + int rc = ON_wString::CompareOrdinal( + lhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst), + rhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst), + true + ); + if (0 == rc) + rc = ON_wString::CompareOrdinal( + lhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst), + rhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst), + true + ); + return rc; +} + int ON_FontList::CompareWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); @@ -1925,6 +2050,19 @@ int ON_FontList::CompareWeightStretchStyle( return 0; } + +int ON_FontList::CompareStretch( + ON_Font const* const* lhs, + ON_Font const* const* rhs + ) +{ + ON_ManagedFonts_CompareFontPointer(lhs, rhs); + const int lhs_font_stretch = (int)static_cast(lhs_font->FontStretch()); + const int rhs_font_stretch = (int)static_cast(rhs_font->FontStretch()); + const int rc = (lhs_font_stretch - rhs_font_stretch); + return rc; +} + int ON_FontList::CompareUnderlinedStrikethroughPointSize( ON_Font const* const* lhs, ON_Font const* const* rhs @@ -4171,10 +4309,18 @@ const ON_ClassArray< ON_FontFaceQuartet >& ON_FontList::QuartetList() const m_quartet_list.Reserve(32 + font_count / 4); const ON_Font* f = nullptr; - const ON_Font* upright6[6] = {}; - const ON_Font* italic6[6] = {}; + const unsigned max_stretch_dex = 10; + const unsigned max_weight_dex = 10; + // quartet_fonts[stretch_dex][upright,italic][weight_dex] + // = all fonts in the quartet arranged by stretch, slant, and weight. + const ON_Font* quartet_fonts[11][2][11] = {}; + // count[stretch_dex][upright,italic] = number of weights available for that stretch and slant + unsigned int count[10][2] = {}; - for (unsigned int i = 0; i < font_count; i++) + unsigned stretch_dex_range[2] = { 0U,0U }; + + unsigned next_i = 0U; + for (unsigned i = 0; i < font_count; i = (i < next_i) ? next_i : (i+1)) { f = a[i]; if (nullptr == f) @@ -4184,115 +4330,138 @@ const ON_ClassArray< ON_FontFaceQuartet >& ON_FontList::QuartetList() const if (quartet_name.IsEmpty()) continue; - memset(upright6, 0, sizeof(upright6)); - memset(italic6, 0, sizeof(italic6)); - - const ON_Font** sextet = (f->IsItalic()) ? italic6 : upright6; - if (ON_Font::Weight::Normal == f->FontWeight()) - sextet[2] = f; - else if (ON_Font::Weight::Medium == f->FontWeight()) - sextet[3] = f; - else if (f->IsBold()) - sextet[4] = f; - else if (f->IsLight()) - sextet[1] = f; - else - continue; - - while(i+1 < font_count) + memset(quartet_fonts, 0, sizeof(quartet_fonts)); + memset(count, 0, sizeof(count)); + const unsigned medium_stretch_dex = (unsigned)static_cast(ON_Font::Stretch::Medium); + unsigned stretch_dex = medium_stretch_dex; + unsigned slant_dex; + unsigned weight_dex; + unsigned quartet_count = 0; + for ( next_i = i; next_i < font_count; ++next_i) { - f = a[i + 1]; + f = a[next_i]; if (nullptr == f) break; if (false == quartet_name.EqualOrdinal(f->QuartetName(), true)) break; - i++; - const unsigned int f_weight = static_cast(f->FontWeight()); - sextet = (f->IsItalic()) ? italic6 : upright6; - if (ON_Font::Weight::Normal == f->FontWeight()) - sextet[2] = f; - else if (ON_Font::Weight::Medium == f->FontWeight()) - sextet[3] = f; - else if (f->IsBold()) + stretch_dex = static_cast(f->FontStretch()); + if (stretch_dex < 1 || stretch_dex >= max_stretch_dex) + continue; + weight_dex = static_cast(f->FontWeight()); + if (weight_dex < 1 || weight_dex >= max_weight_dex) + continue; + slant_dex = f->IsItalicOrOblique() ? 1U : 0U; + if (nullptr != quartet_fonts[stretch_dex][slant_dex][weight_dex]) + continue; + if (0 == quartet_count) { - if (nullptr == sextet[4]) - sextet[4] = f; - else if (f_weight < static_cast(sextet[4]->FontWeight())) - { - if (nullptr == sextet[5]) - sextet[5] = sextet[4]; - sextet[4] = f; - } - else if (nullptr != sextet[5] && f_weight > static_cast(sextet[5]->FontWeight())) - sextet[5] = f; - } - else if (f->IsLight()) - { - if (nullptr == sextet[1]) - sextet[1] = f; - else if (f_weight > static_cast(sextet[1]->FontWeight())) - { - if ( nullptr == sextet[0]) - sextet[0] = sextet[1]; - sextet[1] = f; - } - else if (nullptr != sextet[0] && f_weight < static_cast(sextet[0]->FontWeight())) - sextet[0] = f; + stretch_dex_range[0] = stretch_dex; + stretch_dex_range[1] = stretch_dex; } + else if (stretch_dex < stretch_dex_range[0]) + stretch_dex_range[0] = stretch_dex; + else if (stretch_dex > stretch_dex_range[1]) + stretch_dex_range[1] = stretch_dex; + quartet_fonts[stretch_dex][slant_dex][weight_dex] = f; + ++count[stretch_dex][slant_dex]; + ++quartet_count; } + if (0 == quartet_count) + continue; - for (int sex_dex = 0; sex_dex < 2; sex_dex++) + if (stretch_dex_range[0] < stretch_dex_range[1]) { - sextet = (1==sex_dex) ? italic6 : upright6; - - if (nullptr == sextet[2]) + // Need to pick the stretch_dex with the most members. + // This happens on Mac OS (where no reliable "LOGFONT" name exists) and + // with damaged Windows fonts that don't have a "LOFGONT" name set. + // Pick the one closest to ON_Font::Stretch::Medium with the most faces + stretch_dex = medium_stretch_dex; + for (unsigned k = 1; k <= medium_stretch_dex; ++k) { - sextet[2] = sextet[3]; - sextet[3] = nullptr; - } - else if (nullptr == sextet[4]) - { - sextet[4] = sextet[3]; - sextet[3] = nullptr; - } - - if (nullptr != sextet[2]) - { - if (nullptr != sextet[5]) - sextet[4] = sextet[5]; - continue; - } - - if (nullptr != sextet[1] && nullptr != sextet[4]) - { - sextet[2] = sextet[1]; - continue; - } - - if (nullptr != sextet[1]) - { - if (nullptr != sextet[0]) - { - sextet[2] = sextet[0]; - sextet[4] = sextet[1]; - } - else - { - sextet[2] = sextet[1]; - } - continue; - } - - if (nullptr != sextet[4]) - { - sextet[2] = sextet[4]; - sextet[4] = sextet[5]; - continue; + const unsigned k0 = medium_stretch_dex - k; + const unsigned k1 = medium_stretch_dex + k; + if (k0 > 0 && (count[k0][0] + count[k0][1]) > (count[stretch_dex][0] + count[stretch_dex][1])) + stretch_dex = k0; + if (k1 < max_stretch_dex && (count[k1][0] + count[k1][1]) >(count[stretch_dex][0] + count[stretch_dex][1])) + stretch_dex = k1; } } + else + stretch_dex = stretch_dex_range[0]; - ON_FontFaceQuartet q(quartet_name,upright6[2],upright6[4],italic6[2],italic6[4]); + if (count[stretch_dex][0] + count[stretch_dex][1] <= 0) + continue; + + const unsigned normal_weight_dex = (unsigned)static_cast(ON_Font::Weight::Normal); + const unsigned medium_weight_dex = (unsigned)static_cast(ON_Font::Weight::Medium); + const unsigned bold_weight_dex = (unsigned)static_cast(ON_Font::Weight::Bold); + + const ON_Font* pairs[2][2] = {}; + for (slant_dex = 0; slant_dex < 2; slant_dex++) + { + if ( count[stretch_dex][slant_dex] <= 2 ) + { + // 0, 1 or 2 available weights. + // If there is 1 face it must be the "Regular" face. + // If there are 2 faces, the lightest will be "Regular" and the heaviest will be bold. + int pair_dex = 0; + for (int j = 1; j < max_weight_dex && pair_dex < 2; ++j) + { + if (nullptr != quartet_fonts[stretch_dex][slant_dex][j]) + pairs[slant_dex][pair_dex++] = quartet_fonts[stretch_dex][slant_dex][j]; + } + continue; + } + + // 3 or more available weights (Bahnshrift, Helvetica Neue, ...) + unsigned regular_dex + = (nullptr != quartet_fonts[stretch_dex][slant_dex][normal_weight_dex]) + ? normal_weight_dex + : medium_weight_dex; + while (nullptr == quartet_fonts[stretch_dex][slant_dex][regular_dex] && regular_dex > 0) + --regular_dex; + + unsigned bold_dex + = (nullptr != quartet_fonts[stretch_dex][slant_dex][bold_weight_dex]) + ? bold_weight_dex + : regular_dex+1; + while (nullptr == quartet_fonts[stretch_dex][slant_dex][bold_dex] && bold_dex < max_weight_dex) + ++bold_dex; + + if (nullptr != quartet_fonts[stretch_dex][slant_dex][regular_dex] && nullptr == quartet_fonts[stretch_dex][slant_dex][bold_dex] ) + { + if (regular_dex > 0) + { + for (unsigned j = regular_dex - 1; j > 0; --j) + { + if (nullptr == quartet_fonts[stretch_dex][slant_dex][j]) + continue; + bold_dex = regular_dex; + regular_dex = j; + break; + } + } + } + else if (nullptr == quartet_fonts[stretch_dex][slant_dex][regular_dex] && nullptr != quartet_fonts[stretch_dex][slant_dex][bold_dex] ) + { + if (bold_dex > 0) + { + for (unsigned j = bold_dex - 1; j > 0; --j) + { + if (nullptr == quartet_fonts[stretch_dex][slant_dex][j]) + continue; + regular_dex = j; + break; + } + } + } + + pairs[slant_dex][0] = quartet_fonts[stretch_dex][slant_dex][regular_dex]; + pairs[slant_dex][1] = quartet_fonts[stretch_dex][slant_dex][bold_dex]; + } + + ON_FontFaceQuartet q(quartet_name,pairs[0][0],pairs[0][1],pairs[1][0],pairs[1][1]); if (q.IsEmpty()) continue; @@ -6125,11 +6294,367 @@ const ON_wString ON_Font::WindowsLogfontName() const return WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst); } +class Internal_FakeWindowsLogfontName +{ +public: + ~Internal_FakeWindowsLogfontName() = default; + Internal_FakeWindowsLogfontName(const Internal_FakeWindowsLogfontName&) = default; + Internal_FakeWindowsLogfontName() = default; + Internal_FakeWindowsLogfontName& operator=(const Internal_FakeWindowsLogfontName&) = default; + + Internal_FakeWindowsLogfontName( + const wchar_t* family_name, + const wchar_t* postscript_name, + const wchar_t* fake_logfont_name, + ON_FontFaceQuartet::Member quartet_member + ) + : m_fake_logfont_name(fake_logfont_name) + , m_family_and_postcript_name_hash(Internal_FakeWindowsLogfontName::NameHash(family_name, postscript_name)) + , m_quartet_member(quartet_member) + // for debugging + //, m_family_name(family_name) + //, m_postscript_name(postscript_name) + { + m_fake_logfont_name.TrimLeftAndRight(); + if ( + m_fake_logfont_name.EqualOrdinal(family_name, true) + || (ON_FontFaceQuartet::Member::Unset != quartet_member && m_fake_logfont_name.IsEmpty()) + || m_family_and_postcript_name_hash.IsZeroDigentOrEmptyContentHash() + ) + { + ON_ERROR("Invalid input."); + m_fake_logfont_name = ON_wString::EmptyString; + m_family_and_postcript_name_hash = ON_SHA1_Hash::EmptyContentHash; + m_quartet_member = ON_FontFaceQuartet::Member::Unset; + } + } + + static const ON_SHA1_Hash NameHash( + const wchar_t* family_name, + const wchar_t* postscript_name + ) + { + if (nullptr == family_name || 0 == family_name[0]) + return ON_SHA1_Hash::EmptyContentHash; + if (nullptr == postscript_name || 0 == postscript_name[0]) + return ON_SHA1_Hash::EmptyContentHash; + + ON_wString s1(family_name); + s1.Remove(ON_wString::Space); + s1.Remove(ON_wString::HyphenMinus); + s1.TrimLeftAndRight(); + if (s1.IsEmpty()) + return ON_SHA1_Hash::EmptyContentHash; + + ON_wString s2(postscript_name); + s2.Remove(ON_wString::Space); + s2.Remove(ON_wString::HyphenMinus); + s2.TrimLeftAndRight(); + if (s2.IsEmpty()) + return ON_SHA1_Hash::EmptyContentHash; + + // add a hypen between family and postscript name + // to insure hash for Family = A and postscript = BC + // is different from hash for Family = AB and postscript = C + s1 += ON_wString::HyphenMinus; + s1 += s2; + + return s1.ContentHash(ON_StringMapOrdinalType::MinimumOrdinal); + } + + static int CompareFamilyAndPostscriptNameHash( + const Internal_FakeWindowsLogfontName* lhs, + const Internal_FakeWindowsLogfontName* rhs + ) + { + if (lhs == rhs) + return 0; + // nulls sort last + if (nullptr == lhs) + return 1; + if (nullptr == rhs) + return -1; + return ON_SHA1_Hash::Compare(lhs->m_family_and_postcript_name_hash, rhs->m_family_and_postcript_name_hash); + } + + const ON_SHA1_Hash QuartetFamilyAndPostscriptNameHash() const + { + return m_family_and_postcript_name_hash; + } + + ON_FontFaceQuartet::Member QuartetMember() const + { + return m_quartet_member; + } + + const ON_wString FakeWindowsLogfontName() const + { + return m_fake_logfont_name; + } + +private: + ON_SHA1_Hash m_family_and_postcript_name_hash = ON_SHA1_Hash::EmptyContentHash; + ON_wString m_fake_logfont_name = ON_wString::EmptyString; + ON_FontFaceQuartet::Member m_quartet_member = ON_FontFaceQuartet::Member::Unset; + + // for debugging + //const ON_wString m_family_name = ON_wString::EmptyString; + //const ON_wString m_postscript_name = ON_wString::EmptyString; +}; + +const ON_wString ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames( + ON_wString family_name, + ON_wString postscript_name +) +{ + // This function is used to partition Apple fonts into groups with + // at most 4 faces and assign a face windows logfont name to the group. + // + // This is required for archaic font selection interfaces, like ones in Rhino 6 and 7, + // that use quartets of regular/italic/bold/bold-italic to specify fonts. + // + // Because modern fonts can have many faces (Helvetica Neue has 14 as of 2019) + // and those faces dont' fit the 1970's LOGFONT model of regular/italic/bold/bold-italic. + // American Typewriter and Avenir are other example of commonly used fonts that have + // more than 4 faces and don't have fit well into the regular/italic/bold/bold-italic UI. + // + // It fixes bugs like + // https://mcneel.myjetbrains.com/youtrack/issue/RH-37074 + // https://mcneel.myjetbrains.com/youtrack/issue/RH-53129 + // https://mcneel.myjetbrains.com/youtrack/issue/RH-53430 + // + + family_name.TrimLeftAndRight(); + postscript_name.TrimLeftAndRight(); + if (family_name.IsEmpty() || postscript_name.IsEmpty()) + return ON_wString::EmptyString; + + static Internal_FakeWindowsLogfontName fake_names[] = + { + // Quartets that need a fake LOGFONT name different from family_name in order to + // appear in an archaic quartet name + regular/bold/italic/bold-italic UI + // + // Internal_FakeWindowsLogfontName(family_name,postscript_name,fake_logfont_name,member); + + // Cominations that are not explicitly specified are correctly handled by using the family name as the fake logfont name. + // The explicity fake logfont name should always be different from the family name. + Internal_FakeWindowsLogfontName(L"American Typewriter", L"AmericanTypewriter-Light", L"American Typewriter Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"American Typewriter", L"AmericanTypewriter-Semibold", L"American Typewriter Semibold", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"American Typewriter", L"AmericanTypewriter-CondensedLight", L"American Typewriter Condensed Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"American Typewriter", L"AmericanTypewriter-Condensed", L"American Typewriter Condensed", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"American Typewriter", L"AmericanTypewriter-CondensedBold", L"American Typewriter Condensed", ON_FontFaceQuartet::Member::Bold), + + Internal_FakeWindowsLogfontName(L"Apple Braille", L"AppleBraille-Outline6Dot", L"Apple Braille Outline 6 Dot", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Apple Braille", L"AppleBraille-Outline8Dot", L"Apple Braille Outline 8 Dot", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Apple Braille", L"AppleBraille-Pinpoint6Dot", L"Apple Braille Pinpoint 6 Dot", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Apple Braille", L"AppleBraille-Pinpoint8Dot", L"Apple Braille Pinpoint 8 Dot", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-Thin", L"Apple SD Gothic Neo Thin", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-UltraLight", L"Apple SD Gothic Neo Ultralight", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-Light", L"Apple SD Gothic Neo Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-Medium", L"Apple SD Gothic Neo Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-SemiBold", L"Apple SD Gothic Neo Semibold", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-ExtraBold", L"Apple SD Gothic Neo Extra Bold", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-Heavy", L"Apple SD Gothic Neo Heavy", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Arial Hebrew", L"ArialHebrew-Light", L"Arial Hebrew Light", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Arial Hebrew Scholar", L"ArialHebrewScholar-Light", L"Arial Hebrew Scholar Light", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Book", L"Avenir Book", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-BookOblique", L"Avenir Book", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Light", L"Avenir Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-LightOblique", L"Avenir Light", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Medium", L"Avenir Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-MediumOblique", L"Avenir Medium", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Heavy", L"Avenir Heavy", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-HeavyOblique", L"Avenir Heavy", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Black", L"Avenir Black", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-BlackOblique", L"Avenir-Black", ON_FontFaceQuartet::Member::Italic), + + Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-UltraLight", L"Avenir Next Ultralight", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-UltraLightItalic", L"Avenir Next Ultralight", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-Medium", L"Avenir Next Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-MediumItalic", L"Avenir Next Medium", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-DemiBold", L"Avenir Next Demibold", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-DemiBoldItalic", L"Avenir Next Demibold", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-Heavy", L"Avenir Next Heavy", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-HeavyItalic", L"Avenir Next Heavy", ON_FontFaceQuartet::Member::Italic), + + Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-UltraLight", L"Avenir Next Condensed Ultralight", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-UltraLightItalic", L"Avenir Next Condensed Ultralight", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-Medium", L"Avenir Next Condensed Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-MediumItalic", L"Avenir Next Condensed Medium", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-DemiBold", L"Avenir Next Condensed Demibold", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-DemiBoldItalic", L"Avenir Next Condensed Demibold", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-Heavy", L"Avenir Next Condensed Heavy", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-HeavyItalic", L"Avenir Next Condensed Heavy", ON_FontFaceQuartet::Member::Italic), + + Internal_FakeWindowsLogfontName(L"Baskerville", L"Baskerville-SemiBold", L"Baskerville Semibold", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Baskerville", L"Baskerville-SemiBoldItalic", L"Baskerville Semibold", ON_FontFaceQuartet::Member::Italic), + + Internal_FakeWindowsLogfontName(L"Chalkboard SE", L"ChalkboardSE-Light", L"Chalkboard SE Light", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Charter", L"Charter-Black", L"Charter Black", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Charter", L"Charter-BlackItalic", L"Charter Black", ON_FontFaceQuartet::Member::Italic), + + Internal_FakeWindowsLogfontName(L"Copperplate", L"Copperplate-Light", L"Copperplate Light", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Damascus", L"DamascusLight", L"Damascus Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Damascus", L"DamascusMedium", L"Damascus Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Damascus", L"DamascusSemiBold", L"Damascus Semibold", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Futura", L"Futura-CondensedMedium", L"Futura Condensed", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Futura", L"Futura-CondensedExtraBold", L"Futura Condensed", ON_FontFaceQuartet::Member::Bold), + + Internal_FakeWindowsLogfontName(L"Gill Sans", L"GillSans-Light", L"Gill Sans Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Gill Sans", L"GillSans-LightItalic", L"Gill Sans Light", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Gill Sans", L"GillSans-SemiBold", L"Gill Sans Semibold", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Gill Sans", L"GillSans-SemiBoldItalic", L"Gill Sans Semibold", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Gill Sans", L"GillSans-UltraBold", L"Gill Sans Ultrabold", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Helvetica", L"Helvetica-Light", L"Helvetica Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Helvetica", L"Helvetica-LightOblique", L"Helvetica Light", ON_FontFaceQuartet::Member::Italic), + + Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-UltraLight", L"Helvetica Neue Ultralight", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-UltraLightItalic", L"Helvetica Neue Ultralight", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-Thin", L"Helvetica Neue Thin", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-ThinItalic", L"Helvetica Neue Thin", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-Light", L"Helvetica Neue Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-LightItalic", L"Helvetica Neue Light", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-Medium", L"Helvetica Neue Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-MediumItalic", L"Helvetica Neue Medium", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-CondensedBold", L"Helvetica Neue Condensed", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-CondensedBlack", L"Helvetica Neue Condensed", ON_FontFaceQuartet::Member::Bold), + + Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W3", L"Hiragino Sans W3", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W6", L"Hiragino Sans W6", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W0", L"Hiragino Sans W0", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W1", L"Hiragino Sans W1", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W2", L"Hiragino Sans W2", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W4", L"Hiragino Sans W4", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W5", L"Hiragino Sans W5", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W7", L"Hiragino Sans W7", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W8", L"Hiragino Sans W8", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W9", L"Hiragino Sans W9", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Hoefler Text", L"HoeflerText-Ornaments", L"Hoefler Text Ornaments", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"ITF Devanagari", L"ITFDevanagari-Light", L"ITF Devanagari Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"ITF Devanagari", L"ITFDevanagari-Medium", L"ITF Devanagari Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"ITF Devanagari", L"ITFDevanagari-Demi", L"ITF Devanagari Demibold", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"ITF Devanagari Marathi", L"ITFDevanagariMarathi-Light", L"ITF Devanagari Marathi Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"ITF Devanagari Marathi", L"ITFDevanagariMarathi-Medium", L"ITF Devanagari Marathi Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"ITF Devanagari Marathi", L"ITFDevanagariMarathi-Demi", L"ITF Devanagari Marathi Demibold", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Kohinoor Bangla", L"KohinoorBangla-Light", L"Kohinoor Bangla Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Kohinoor Bangla", L"KohinoorBangla-Medium", L"Kohinoor Bangla Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Kohinoor Bangla", L"KohinoorBangla-Semibold", L"Kohinoor Bangla Semibold", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Kohinoor Devanagari", L"KohinoorDevanagari-Light", L"Kohinoor Devanagari Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Kohinoor Devanagari", L"KohinoorDevanagari-Medium", L"Kohinoor Devanagari Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Kohinoor Devanagari", L"KohinoorDevanagari-Semibold", L"Kohinoor Devanagari Semibold", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Kohinoor Telugu", L"KohinoorTelugu-Light", L"Kohinoor Telugu Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Kohinoor Telugu", L"KohinoorTelugu-Medium", L"Kohinoor Telugu Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Kohinoor Telugu", L"KohinoorTelugu-Semibold", L"Kohinoor Telugu Semibold", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Muna", L"MunaBlack", L"Muna Black", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Optima", L"Optima-ExtraBlack", L"Optima Black", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Papyrus", L"Papyrus-Condensed", L"Papyrus Condensed", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Phosphate", L"Phosphate-Inline", L"Phosphate Inline", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Phosphate", L"Phosphate-Solid", L"Phosphate Solid", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"PingFang HK", L"PingFangHK-Ultralight", L"PingFang HK Ultralight", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"PingFang HK", L"PingFangHK-Thin", L"PingFang HK Thin", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"PingFang HK", L"PingFangHK-Light", L"PingFang HK Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"PingFang HK", L"PingFangHK-Medium", L"PingFang HK Medium", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"PingFang SC", L"PingFangSC-Ultralight", L"PingFang SC Ultralight", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"PingFang SC", L"PingFangSC-Thin", L"PingFang SC Thin", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"PingFang SC", L"PingFangSC-Light", L"PingFang SC Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"PingFang SC", L"PingFangSC-Medium", L"PingFang SC Medium", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"PingFang TC", L"PingFangTC-Ultralight", L"PingFang TC Ultralight", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"PingFang TC", L"PingFangTC-Thin", L"PingFang TC Thin", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"PingFang TC", L"PingFangTC-Light", L"PingFang TC Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"PingFang TC", L"PingFangTC-Medium", L"PingFang TC Medium", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Light", L"Skia Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Black", L"Skia Black", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Condensed", L"Skia Condensed", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Black-Condensed", L"Skia Condensed Black", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Light-Condensed", L"Skia Condensed Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Extended", L"Skia Extended", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Black-Extended", L"Skia Extended Black", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Light-Extended", L"Skia Extended Light", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Songti SC", L"STSongti-SC-Light", L"Songti SC Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Songti SC", L"STSongti-SC-Black", L"Songti SC Black", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Songti TC", L"STSongti-TC-Light", L"Songti TC Light", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Sukhumvit Set", L"SukhumvitSet-Light", L"Sukhumvit Set Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Sukhumvit Set", L"SukhumvitSet-Medium", L"Sukhumvit Set Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Sukhumvit Set", L"SukhumvitSet-SemiBold", L"Sukhumvit Set Semibold", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Sukhumvit Set", L"SukhumvitSet-Thin", L"Sukhumvit Set Thin", ON_FontFaceQuartet::Member::Regular), + + Internal_FakeWindowsLogfontName(L"Thonburi", L"Thonburi-Light", L"Thonburi Light", ON_FontFaceQuartet::Member::Regular), + }; + + static bool bFakeNamesAreSorted = false; + if (false == bFakeNamesAreSorted) + { + bFakeNamesAreSorted = true; + qsort(fake_names, sizeof(fake_names) / sizeof(fake_names[0]), sizeof(fake_names[0]), (int(*)(const void*,const void*))Internal_FakeWindowsLogfontName::CompareFamilyAndPostscriptNameHash); + } + + const Internal_FakeWindowsLogfontName key(family_name, postscript_name, L"", ON_FontFaceQuartet::Member::Unset); + const Internal_FakeWindowsLogfontName* fake_name + = (const Internal_FakeWindowsLogfontName*)bsearch((const void*)&key, (const void*)fake_names, sizeof(fake_names) / sizeof(fake_names[0]), sizeof(fake_names[0]), (int(*)(const void*, const void*))Internal_FakeWindowsLogfontName::CompareFamilyAndPostscriptNameHash); + if ( + fake_name + && ON_FontFaceQuartet::Member::Unset != fake_name->QuartetMember() + && false == fake_name->QuartetFamilyAndPostscriptNameHash().IsZeroDigentOrEmptyContentHash() + && fake_name->FakeWindowsLogfontName().IsNotEmpty() + ) + { + // this family is complicated enough that it + // has to be partitioned into multiple fake Windows LOGFONT quartets. + return fake_name->FakeWindowsLogfontName(); + } + + return family_name; // use family_name as the fake LOGFONT name +} + const ON_wString ON_Font::QuartetName( ON_Font::NameLocale name_locale ) const { - // This may need adjustment for MacOS. + // The Windows OS partitions a font family (which can have many face) into + // subsets with at most 4 faces that can be tagged regular/italic/bold/bold-italic + // and assigns each subset a unique "LOGFONT" name. Note that some subsets may + // have fewer than 4 faces because there isn't an italic or bold member. + // There are Windows OS font picking tools that use + // LOGFONT name + regular/italic/bold/bold-italic to select a particular face + // and these work ok because Windows OS generates the quartets and all applications + // choosing to use the archaic regular/italic/bold/bold-italic font UI have consistent + // quartets. + // + // Modern Mac OS does not have native font selection UI that uses the + // archaic regular/italic/bold/bold-italic approach. + // + // On Mac OS, opennurbs generates a fake LOGFONT name because Rhino 6 and 7 use + // the archaic regular/italic/bold/bold-italic font UI on oth Windows and Mac. + // Helvetica Neue, American Typewriter, and Avenir are examples of common + // Mac OS font families that do not fit well with the + // archaic regular/italic/bold/bold-italic font UI. Code in + // opennurbs_apple_nsfont.cpp is used to assign fake LOGFONT names for fonts + // like these. return WindowsLogfontName(name_locale); } @@ -6163,7 +6688,12 @@ bool ON_Font::IsBoldInQuartet() const if (nullptr == regular) return true; // no regular in this quartet. - + + if (this == bold) + return true; + if (this == regular) + return false; + const unsigned int font_weight = static_cast(FontWeight()); const unsigned int regular_weight = static_cast(regular->FontWeight()); const unsigned int bold_weight = static_cast(bold->FontWeight()); @@ -7569,43 +8099,28 @@ void ON_Font::Dump(ON_TextLog& dump) const dump.Print(L"PointSize = annotation default\n"); } - bool bInQuartet = false; - for(;;) + const ON_FontFaceQuartet q = this->InstalledFontQuartet(); + const ON_FontFaceQuartet::Member m = q.QuartetMember(this); + switch (m) { - const ON_FontFaceQuartet q = this->InstalledFontQuartet(); - if (q.QuartetName().IsEmpty()) - break; - - const bool bQuartetItalic = (ON_Font::Style::Italic == m_font_style || ON_Font::Style::Oblique == m_font_style); - const ON_Font* regular = bQuartetItalic ? q.ItalicFace() : q.RegularFace(); - const ON_Font* bold = bQuartetItalic ? q.BoldItalicFace() : q.BoldFace(); - const ON_Font* quartet_face; - if (nullptr != regular && FontWeight() == regular->FontWeight()) - quartet_face = regular; - else if (nullptr != bold && FontWeight() == bold->FontWeight()) - quartet_face = bold; - else - quartet_face = nullptr; - if (nullptr == quartet_face) - break; - - bInQuartet = true; - if (quartet_face == q.RegularFace()) - dump.Print("Quartet: %ls (Regular member)\n", static_cast(q.QuartetName())); - else if (quartet_face == q.BoldFace()) - dump.Print("Quartet: %ls (Bold member)\n", static_cast(q.QuartetName())); - else if (quartet_face == q.ItalicFace()) - dump.Print("Quartet: %ls (Italic member)\n", static_cast(q.QuartetName())); - else if (quartet_face == q.BoldItalicFace()) - dump.Print("Quartet: %ls (Bold-Italic member)\n", static_cast(q.QuartetName())); - else - bInQuartet = false; - + case ON_FontFaceQuartet::Member::Regular: + dump.Print("Quartet: %ls (Regular member)\n", static_cast(q.QuartetName())); + break; + case ON_FontFaceQuartet::Member::Bold: + dump.Print("Quartet: %ls (Bold member)\n", static_cast(q.QuartetName())); + break; + case ON_FontFaceQuartet::Member::Italic: + dump.Print("Quartet: %ls (Italic member)\n", static_cast(q.QuartetName())); + break; + case ON_FontFaceQuartet::Member::BoldItalic: + dump.Print("Quartet: %ls (Bold-Italic member)\n", static_cast(q.QuartetName())); + break; + case ON_FontFaceQuartet::Member::Unset: + default: + dump.Print("Quartet: None\n"); break; } - if (false == bInQuartet) - dump.Print("Quartet: None\n"); s = ON_Font::WeightToWideString(FontWeight()); if( s.IsEmpty()) @@ -7717,6 +8232,13 @@ void ON_Font::Dump(ON_TextLog& dump) const dump.PushIndent(); ON_Font::DumpCTFont(apple_font, dump); dump.PopIndent(); + if (nullptr != apple_font && ON_Font::Origin::AppleFont == m_font_origin) + { + if (m_apple_font_weight_trait >= -1.0 && m_apple_font_weight_trait <= 1.0) + dump.Print(L"Apple font weight trait = %g\n", m_apple_font_weight_trait); + if (m_apple_font_width_trait >= -1.0 && m_apple_font_width_trait <= 1.0) + dump.Print(L"Apple font width trait = %g\n", m_apple_font_width_trait); + } } #elif defined(OPENNURBS_FREETYPE_SUPPORT) // Look in opennurbs_system_rumtime.h for the correct place to define OPENNURBS_FREETYPE_SUPPORT. @@ -9363,6 +9885,11 @@ bool ON_Font::IsUpright() const return (ON_Font::Style::Upright == m_font_style); } +bool ON_Font::IsItalicOrOblique() const +{ + return (ON_Font::Style::Italic == m_font_style || ON_Font::Style::Oblique == m_font_style); +} + bool ON_Font::IsOblique() { return (ON_Font::Style::Oblique == m_font_style); @@ -9550,12 +10077,33 @@ bool ON_Font::SetAppleFontWeightTrait( double ON_Font::AppleFontWeightTrait() const { - return + // old function that cannot be changed because it would break the SDK. + return (m_apple_font_weight_trait >= -1.0 && m_apple_font_weight_trait <= 1.0) ? m_apple_font_weight_trait : ON_Font::AppleFontWeightTraitFromWeight(this->FontWeight()); } +double ON_Font::AppleFontWeightTraitEx() const +{ + return + ON_Font::Origin::AppleFont == FontOrigin() + && m_apple_font_weight_trait >= -1.0 + && m_apple_font_weight_trait <= 1.0 + ? m_apple_font_weight_trait + : ON_UNSET_VALUE; +} + +double ON_Font::AppleFontWidthTrait() const +{ + return + ON_Font::Origin::AppleFont == FontOrigin() + && m_apple_font_width_trait >= -1.0 + && m_apple_font_width_trait <= 1.0 + ? m_apple_font_width_trait + : ON_UNSET_VALUE; +} + bool ON_Font::IsValidPointSize( double point_size ) @@ -11345,6 +11893,153 @@ bool ON_ManagedFonts::GetFontMetricsInFontDesignUnits( return false; } +void ON_ManagedFonts::Internal_SetFakeWindowsLogfontName( + const ON_Font * font, + const ON_wString fake_loc_logfont_name, + const ON_wString fake_en_logfont_name +) +{ + if (nullptr == font) + return; + const_cast(font)->m_loc_windows_logfont_name = fake_loc_logfont_name; + const_cast(font)->m_en_windows_logfont_name = fake_en_logfont_name; +} + +void ON_ManagedFonts::Internal_SetFakeWindowsLogfontNames( + ON_SimpleArray& device_list +) +{ + // Windows divides font families (which can have many faces) + // into LOGFONT subsets identified by a logfont name. + // A LOGFONT subset can have at most 4 faces and typically + // they are regular/bold/italic/bold-italic variations of + // a face and all have matching stretch. + // + // This all goes back to the 1970's and early user interfaces + // that selected fonts based on a name, bold=true/false, italic=true/false + // style inteface. This type of archaic font selection interface is way + // too simple to accomodate the rich font families are currently in use. + // Newer font families can have dozens of faces with severeal weights, + // several widths, and an upright and italic version of each of these. + // However, that does not prevent applications like Rhino 6/7 + // from attempting to use the inadequate and archic bold/italic font + // selection interface to select a font from a rich list of modern fonts. + // + // This function is used on non Windows platforms to partition + // the installed font families into subsets with the same + // width and set a unique fake LOGFONT name. Later sorting will + // choose up to four faces from each subset to use as the + // regular/bold/italic/bold-italic representatives. + + // Assign a fake logfont name. + const unsigned int font_count = device_list.UnsignedCount(); + for (unsigned int i = 0; i < font_count; ++i) + { + const ON_Font* f0 = device_list[i]; + if (nullptr == f0) + continue; + + ON_wString fake_loc_logfont_name = ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames( + f0->FamilyName(ON_Font::NameLocale::LocalizedFirst), + f0->PostScriptName(ON_Font::NameLocale::LocalizedFirst) + ); + if (fake_loc_logfont_name.IsEmpty()) + continue; + ON_wString fake_en_logfont_name = ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames( + f0->FamilyName(ON_Font::NameLocale::English), + f0->PostScriptName(ON_Font::NameLocale::English) + ); + if (fake_en_logfont_name.IsEmpty()) + fake_en_logfont_name = fake_loc_logfont_name; + else if (false == fake_loc_logfont_name.EqualOrdinal(fake_en_logfont_name, true)) + { + const bool bLocalFaceIsFamilyName = fake_loc_logfont_name.EqualOrdinal(f0->FamilyName(ON_Font::NameLocale::LocalizedFirst), true); + const bool bEnglishFaceIsFamilyName = fake_en_logfont_name.EqualOrdinal(f0->FamilyName(ON_Font::NameLocale::English), true); + if (bLocalFaceIsFamilyName && false == bEnglishFaceIsFamilyName) + fake_loc_logfont_name = fake_en_logfont_name; + else if (bEnglishFaceIsFamilyName && false == bLocalFaceIsFamilyName) + fake_en_logfont_name = fake_loc_logfont_name; + } + + ON_ManagedFonts::Internal_SetFakeWindowsLogfontName( + f0, + fake_loc_logfont_name, + fake_en_logfont_name + ); + } + + // Sort device_list by font family and current fake LOGFONT name + device_list.QuickSort(ON_FontList::CompareFamilyAndWindowsLogfontName); + ON_SimpleArray quartet_candidates(128); + bool bSortAgain = false; + for (unsigned int i = 0; i < font_count; ++i) + { + const ON_Font* f0 = device_list[i]; + if (nullptr == f0) + continue; + + quartet_candidates.SetCount(0); + quartet_candidates.Append(f0); + while( (i + 1) < font_count ) + { + const ON_Font* f = device_list[i + 1]; + if (nullptr == f || 0 != ON_FontList::CompareFamilyAndWindowsLogfontName(&f0, &f)) + break; + quartet_candidates.Append(f); + ++i; + } + const unsigned int quartet_candidate_count = quartet_candidates.UnsignedCount(); + if (quartet_candidate_count < 2) + continue; + + // members in a fake LOGFONT quartet should have the same stretch + quartet_candidates.QuickSort(ON_FontList::CompareStretch); + + // See which subset of family gets to use the family name as + // the fake logfont name. + const int medium_stretch = (int)((unsigned)(ON_Font::Stretch::Medium)); + ON_Font::Stretch stretch0 = quartet_candidates[0]->FontStretch(); + int delta_stretch = abs(medium_stretch - ((int)((unsigned)(stretch0)))); + bool bNeedToModifyFakeName = false; + for (unsigned int j = 0; j < quartet_candidate_count; ++j) + { + const ON_Font::Stretch stretch = quartet_candidates[j]->FontStretch(); + int ds = abs(medium_stretch - ((int)((unsigned)(stretch)))); + if (ds < delta_stretch) + { + stretch0 = stretch; + delta_stretch = ds; + bNeedToModifyFakeName = true; + } + } + + if (false == bNeedToModifyFakeName) + continue; // we cannot use stretch to distinguish between family members + + // If a family member's stretch is not stretch0, modify its names. + for (unsigned int j = 0; j < quartet_candidate_count; ++j) + { + const ON_Font* f = quartet_candidates[j]; + const ON_Font::Stretch stretch = f->FontStretch(); + if (stretch == stretch0) + continue; + ON_wString suffix = L" ("; + suffix += ON_Font::StretchToWideString(stretch); + if (suffix.Length() < 3) + continue; + suffix += L")"; + const ON_wString loc_name = f->m_loc_windows_logfont_name+suffix; + const ON_wString en_name = f->m_en_windows_logfont_name+suffix; + ON_ManagedFonts::Internal_SetFakeWindowsLogfontName(f,loc_name,en_name); + bSortAgain = true; + } + } + + if (bSortAgain) + device_list.QuickSort(ON_FontList::CompareFamilyAndWindowsLogfontName); + +} + const ON_FontList& ON_ManagedFonts::InstalledFonts() { if (0 == List.m_installed_fonts.Count()) @@ -11355,6 +12050,7 @@ const ON_FontList& ON_ManagedFonts::InstalledFonts() ON_ManagedFonts::Internal_GetWindowsInstalledFonts(device_list); #elif defined (ON_RUNTIME_APPLE_CORE_TEXT_AVAILABLE) ON_ManagedFonts::Internal_GetAppleInstalledCTFonts(device_list); + ON_ManagedFonts::Internal_SetFakeWindowsLogfontNames(device_list); #endif if (device_list.Count() > 0) { @@ -11364,3 +12060,5 @@ const ON_FontList& ON_ManagedFonts::InstalledFonts() } return List.m_installed_fonts; } + + diff --git a/opennurbs_font.h b/opennurbs_font.h index 18fb953d..4b47a4a5 100644 --- a/opennurbs_font.h +++ b/opennurbs_font.h @@ -2152,6 +2152,16 @@ public: class ON_CLASS ON_FontFaceQuartet { public: + + enum class Member : unsigned char + { + Unset = 0, + Regular = 1, + Bold = 2, + Italic = 3, + BoldItalic = 4 + }; + ON_FontFaceQuartet() = default; ~ON_FontFaceQuartet() = default; ON_FontFaceQuartet(const ON_FontFaceQuartet&) = default; @@ -2193,6 +2203,38 @@ public: const class ON_Font* ItalicFace() const; const class ON_Font* BoldItalicFace() const; + /* + Parameters: + font - [in] + Font to test + Returns: + If font exactly matches a quartet member, that member is identified. + Otherwise, ON_FontFaceQuartet::Member::Unset is returned. + */ + ON_FontFaceQuartet::Member QuartetMember( + const ON_Font* font + ) const; + + /* + Parameters: + member - [in] + Returns: + Specified quartet member. + */ + const ON_Font* Face( + ON_FontFaceQuartet::Member member + ) const; + + /* + Parameters: + member - [in] + Returns: + Closest quartet member. + */ + const ON_Font* ClosestFace( + ON_FontFaceQuartet::Member member + ) const; + const ON_Font* Face( bool bBold, bool bItalic @@ -3589,6 +3631,24 @@ public: */ const ON_wString WindowsLogfontName() const; + /* + Description: + On non-WIndows platforms like Mac OS, iOS, and Android, this function can + be used to generate fake windows logfont names. + For fonts that have at most 4 faces with the same stretch and variations + in weight and slant, the family_name is a good choice. + For fonts that have many faces, like Helvetica Neue on Mac OS, + this funciton will generate names that act like a Windows LOGFONT name + for use in archaic name + regular/bold/italic/bold-italic font selction + user interfaces. + Returns: + A fake windows logfont name. + */ + static const ON_wString FakeWindowsLogfontNameFromFamilyAndPostScriptNames( + ON_wString family_name, + ON_wString postscript_name + ); + /* Returns: Name of the quartet for this font. See ON_FontFaceQuartet for more details. @@ -4754,6 +4814,13 @@ public: int AppleWeightOfFont() const; double AppleFontWeightTrait() const; + /* + Returns: + If the font is created from a CTFont, the weight trait, + otherwise ON_UNSET_VALUE; + */ + double AppleFontWeightTraitEx() const; + /* Description: Don't use this old function. If you have a font and want a face in @@ -4943,20 +5010,35 @@ public: */ bool IsUpright() const; + /* + Returns: + true if FontStyle() is ON_Font::Style::Italic or is ON_Font::Style::Oblique. + Otherwise false. + Remarks: + The face is sloped so the top is to the left of the base. This is extremely rare. + NOTE WELL: + When the term "oblique" appears in a face names or descriptions, + it generally means the face is an italic face. + */ + bool IsItalicOrOblique() const; + /* Returns: true if FontStyle() is ON_Font::Style::Oblique. false if FontStyle() is ON_Font::Style::Upright or .ON_Font::Style::Italic. Remarks: The face is sloped so the top is to the left of the base. This is extremely rare. - NOTE WELL: + NOTE WELL: When the term "oblique" appears in a face names or descriptions, it generally means the face is an italic face. */ bool IsOblique(); // ERROR - missing const - + + ON_Font::Stretch FontStretch() const; + double AppleFontWidthTrait() const; + /* Description: Don't use this old function. If you have a font and want a face in @@ -5330,7 +5412,7 @@ private: mutable ON_SHA1_Hash m_font_characteristics_hash; private: - double m_reserved2 = 0.0; + double m_apple_font_width_trait = ON_UNSET_VALUE; double m_reserved3 = 0.0; double m_reserved4 = 0.0; @@ -5766,6 +5848,7 @@ public: static int CompareFamilyName(ON_Font const* const* lhs, ON_Font const* const* rhs); static int CompareFamilyAndFaceName(ON_Font const* const* lhs, ON_Font const* const* rhs); static int CompareWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs); + static int CompareFamilyAndWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs); static int CompareEnglishPostScriptName(ON_Font const* const* lhs, ON_Font const* const* rhs); static int CompareEnglishFamilyName(ON_Font const* const* lhs, ON_Font const* const* rhs); @@ -5775,6 +5858,7 @@ public: static int CompareQuartetName(ON_Font const* const* lhs, ON_Font const* const* rhs); static int CompareWeightStretchStyle(ON_Font const* const* lhs, ON_Font const* const* rhs); + static int CompareStretch(ON_Font const* const* lhs, ON_Font const* const* rhs); static int CompareUnderlinedStrikethroughPointSize(ON_Font const* const* lhs, ON_Font const* const* rhs); unsigned int AddFont( diff --git a/opennurbs_fpoint.h b/opennurbs_fpoint.h index 8517f0b7..ab9159d6 100644 --- a/opennurbs_fpoint.h +++ b/opennurbs_fpoint.h @@ -176,7 +176,7 @@ public: /* Returns: - true if at least one coordinate is not zero and no coordinates are nans. + true if at lease one coordinate is not zero and no coordinates are unset or nans. */ bool IsNotZero() const; @@ -353,7 +353,7 @@ public: /* Returns: - true if at lease one coordinate is not zero and no coordinates are nans. + true if at lease one coordinate is not zero and no coordinates are unset or nans. */ bool IsNotZero() const; @@ -774,7 +774,7 @@ public: /* Returns: - true if at lease one coordinate is not zero and no coordinates are nans. + true if at lease one coordinate is not zero and no coordinates are unset or nans. */ bool IsNotZero() const; @@ -1036,7 +1036,7 @@ public: /* Returns: - true if at lease one coordinate is not zero and no coordinates are nans. + true if at lease one coordinate is not zero and no coordinates are unset or nans. */ bool IsNotZero() const; diff --git a/opennurbs_freetype.cpp b/opennurbs_freetype.cpp index c61144c1..89d1e1be 100644 --- a/opennurbs_freetype.cpp +++ b/opennurbs_freetype.cpp @@ -7,7 +7,7 @@ // 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 . // //////////////////////////////////////////////////////////////// @@ -18,7 +18,7 @@ #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 +// 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 @@ -33,18 +33,18 @@ // FreeType Licensing: // -//// Retrieved March 22, 2017 +//// Retrieved March 22, 2017 //// https://www.freetype.org/freetype2/docs/index.html ////What is FreeType? //// -////FreeType is a software font engine that is designed to be small, efficient, -////highly customizable, and portable while capable of producing high-quality +////FreeType is a software font engine that is designed to be small, efficient, +////highly customizable, and portable while capable of producing high-quality ////output (glyph images). It can be used in graphics libraries, display servers, ////font conversion tools, text image generation tools, and many other products as well. //// -////Note that FreeType is a font service and doesn't provide APIs to perform -////higher-level features like text layout or graphics processing -////(e.g., colored text rendering, hollowing, etc.). However, it greatly +////Note that FreeType is a font service and doesn't provide APIs to perform +////higher-level features like text layout or graphics processing +////(e.g., colored text rendering, ‘hollowing’, etc.). However, it greatly ////simplifies these tasks by providing a simple, easy to use, and uniform ////interface to access the content of font files. //// @@ -52,12 +52,12 @@ ////FreeType License and the GNU Public License, Version 2. It can thus ////be used by any kind of projects, be they proprietary or not. //// -////Please note that FreeType is also called FreeType 2, to -////distinguish it from the old, deprecated FreeType 1 library, +////Please note that ‘FreeType’ is also called ‘FreeType 2’, to +////distinguish it from the old, deprecated ‘FreeType 1’ library, ////a predecessor no longer maintained and supported. //// //// http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT -//// +//// //// The FreeType Project LICENSE //// ---------------------------- //// @@ -108,7 +108,7 @@ //// encourage you to use the following text: //// //// """ -//// Portions of this software are copyright The FreeType +//// Portions of this software are copyright © The FreeType //// Project (www.freetype.org). All rights reserved. //// """ //// @@ -266,7 +266,7 @@ public: ON_FontFileBuffer(const ON_FontFileBuffer& src) { Internal_CopyFrom(src); - } + } ON_FontFileBuffer& operator=(const ON_FontFileBuffer& src) { @@ -499,9 +499,9 @@ bool ON_FreeType::IsDamagedCharMap( { if (nullptr == cmap || nullptr == cmap->face) return true; - + bool rc = false; - + switch (cmap->encoding) { case FT_ENCODING_APPLE_ROMAN: @@ -555,7 +555,7 @@ bool ON_FreeType::UseUnicodeAsAppleRomanCharCode(FT_Face face) // // It appears that for these fonts, passing a Unicode code point value // to the cmap[] idenfied as "FT_ENCODING_APPLE_ROMAN" will get the correct - // glyph id. + // glyph id. // // I have verified that the way opennurbs handles FT_ENCODING_APPLE_ROMAN charmaps // and the funciton ON_MapUnicodeToAppleRoman() works correctly with all the @@ -583,7 +583,7 @@ bool ON_FreeType::UseUnicodeAsAppleRomanCharCode(FT_Face face) // Linguist's Software Romantic 2.0 generated with Altsys Fontographer 4.1 9/17/96 // Linguist's Software SansSerif 2.0 generated with Altsys Fontographer 4.1 9/17/96 // Linguist's Software Technic 2.0 generated with Altsys Fontographer 4.1 9/17/96 - // + // // So far, all styles with these face names have the buggy cmap[]. if (ON_String::EqualOrdinal(face->family_name, "CityBlueprint", false)) return true; @@ -602,7 +602,7 @@ bool ON_FreeType::UseUnicodeAsAppleRomanCharCode(FT_Face face) const ON_wString ON_FreeType::EncodingTypeToString( FT_Encoding charmap_encoding ) { ON_wString e; - + switch (charmap_encoding) { case FT_ENCODING_NONE: e = L"FT_ENCODING_NONE"; break; @@ -619,14 +619,14 @@ const ON_wString ON_FreeType::EncodingTypeToString( FT_Encoding charmap_encoding case FT_ENCODING_ADOBE_EXPERT: e = L"FT_ENCODING_ADOBE_EXPERT"; break; case FT_ENCODING_ADOBE_CUSTOM: e = L"FT_ENCODING_ADOBE_CUSTOM"; break; case FT_ENCODING_APPLE_ROMAN: e = L"FT_ENCODING_APPLE_ROMAN"; break; - default: + default: e = ON_wString::FormatToString( L"((FT_Encoding)%u)", static_cast(charmap_encoding) ); break; } - + return e; } @@ -639,8 +639,8 @@ const ON_wString ON_FreeType::CharmapPlatformEncodingDescription( const FT_CharM ON_wString encoding; switch (cmap->platform_id) { - case 0: - platform = L"Unicode"; + case 0: + platform = L"Unicode"; switch (cmap->encoding_id) { case 0: encoding = L"Unicode 1.0 semantics [deprecated]"; break; @@ -653,7 +653,7 @@ const ON_wString ON_FreeType::CharmapPlatformEncodingDescription( const FT_CharM } break; - case 1: + case 1: platform = L"Apple Script Manager"; switch (cmap->encoding_id) { @@ -704,7 +704,7 @@ const ON_wString ON_FreeType::CharmapPlatformEncodingDescription( const FT_CharM break; case 3: - platform = L"Windows"; + platform = L"Windows"; switch (cmap->encoding_id) { case 0: encoding = L"Symbol"; break; @@ -747,7 +747,7 @@ bool ON_FontGlyph::TestFreeTypeFaceCharMaps( ON_TextLog* text_log ) const { - // In order for false to be returned, charmaps[] have to exist and + // In order for false to be returned, charmaps[] have to exist and // an explicit error has to be detected. Otherwise, true is returned. const ON_Font* font = Font(); if (nullptr == font) @@ -847,7 +847,7 @@ bool ON_FontGlyph::TestFreeTypeFaceCharMaps( if (bHaveCharCode) { - bHaveCharMap + bHaveCharMap = FT_Err_Ok == FT_Set_Charmap(face, charmap) && charmap == face->charmap; if (bHaveCharMap) @@ -896,7 +896,7 @@ bool ON_FontGlyph::TestFreeTypeFaceCharMaps( if (glyph_index != gid) { s += ON_wString::FormatToString(L"ERROR(expected glyph index %u)",glyph_index); - } + } } text_log->Print(L"%ls\n", static_cast(s)); } @@ -904,7 +904,7 @@ bool ON_FontGlyph::TestFreeTypeFaceCharMaps( // restore face charmap state FT_Set_Charmap(face, charmap0); - + return rc; } @@ -944,15 +944,15 @@ unsigned int ON_FreeType::GlyphId( // and they can map different unicode code points. These typically are // Windows UCS-2 and Windows UCS-4 cmaps. UCS-2 and UCS-4 values subsets of Unicode. // - // In fonts like CityBlueprint and CountryBlueprint (which many customers use), there + // In fonts like CityBlueprint and CountryBlueprint (which many customers use), there // is no FT_ENCODING_UNICODE charmap but there is a viable FT_ENCODING_APPLE_ROMAN charmap. // // As we discover fonts that customers use, we will add support for their charmaps. // // TrueType platform_id and encoding_id. The encoding id is platform specific. - // platform_id-encoding_id + // platform_id-encoding_id // - // 0-* "Apple *code" - encoding id varies + // 0-* "Apple *code" - encoding id varies // // 1-0 Apple Roman (256 codes - see ON_MapAppleRomanToUnicode()) // 1-* Apple (encoding id = script manager) @@ -971,7 +971,7 @@ unsigned int ON_FreeType::GlyphId( // 4-* Custom // // http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-chapter08 - // + // const FT_Encoding encoding_pass[] { FT_ENCODING_UNICODE, @@ -1100,7 +1100,7 @@ static bool Internal_CreateFontBufferFromDirectWrite( ON_WARNING("Multiple font files."); numfiles = 1; } - + Microsoft::WRL::ComPtr dwriteFontFile = nullptr; hr = dwriteFontFace->GetFiles(&numfiles, &dwriteFontFile); if (FAILED(hr)) @@ -1193,11 +1193,11 @@ static bool Internal_CreateFontBufferFromDirectWrite( cleanLogFont.lfOrientation = 0; if (cleanLogFont.lfWeight < 1 || cleanLogFont.lfWeight > 999) cleanLogFont.lfWeight = 400; - if (0 != cleanLogFont.lfItalic) + if (0 != cleanLogFont.lfItalic) cleanLogFont.lfItalic = 1; - if (0 != cleanLogFont.lfUnderline) + if (0 != cleanLogFont.lfUnderline) cleanLogFont.lfUnderline = 1; - if (0 != cleanLogFont.lfStrikeOut) + if (0 != cleanLogFont.lfStrikeOut) cleanLogFont.lfStrikeOut = 1; if (ON_Font::WindowsConstants::logfont_symbol_charset != cleanLogFont.lfCharSet) cleanLogFont.lfCharSet = ON_Font::WindowsConstants::logfont_default_charset; @@ -1265,7 +1265,7 @@ static bool Internal_CreateFontBufferFromGDI( { ON_ERROR("onmalloc(buffer_capacity) failed."); break; - } + } memset(buffer, 0, buffer_capacity); const DWORD buffer_size = ::GetFontData(font_hdc, dwTable, dwOffset, buffer, buffer_capacity); @@ -1317,7 +1317,7 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromWindowsFont( // The DirectWrite approach yields better results in some cases. // For example, the Yu Gothic in Windows 10 // The font_buffer created by DirectWrite results in an FT_Face - // with 3 charmaps (2 Uniocde) and the font_buffer created by + // with 3 charmaps (2 Uniocde) and the font_buffer created by // GDI has 0 charmaps which means getting a glyph from a code point // is not possible. const bool bHaveBuffer @@ -1331,7 +1331,7 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromWindowsFont( continue; if (nullptr == font_buffer.Buffer()) continue; - + int font_face_index = 0; FT_Face face = nullptr; FT_Error rc = FT_New_Memory_Face( @@ -1371,19 +1371,19 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromAppleFont (CTFontRef fontRe { if (nullptr == fontRef) return nullptr; - + // determine file path for CTFont CFURLRef fontURLRef = (CFURLRef) CTFontCopyAttribute (fontRef, kCTFontURLAttribute); - + NSURL* fontURL = (NSURL*) CFBridgingRelease (fontURLRef); const char* path = fontURL.path.UTF8String; - + // Search all the faces in this font file for a face that matches the NSFont family and style FT_Face ftFace; FT_Error err = FT_New_Face (ON_FreeType::Library(), path, 0, &ftFace); // get first face if (err) return nullptr; // that didn't work - + const int numFaces = (int)ftFace->num_faces; // determine number of faces in the font file ON_FreeTypeFace* rc = new ON_FreeTypeFace(); @@ -1392,7 +1392,7 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromAppleFont (CTFontRef fontRe rc->m_face = ftFace; return rc; // only one face, so this must be the right one } - + int faceIndex = 0; for (;;) { @@ -1409,10 +1409,10 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromAppleFont (CTFontRef fontRe rc->m_face = ftFace; return rc; } - + // No match. Step to next face. FT_Done_Face (ftFace); - + FT_Error err = FT_New_Face (ON_FreeType::Library(), path, ++faceIndex, &ftFace); if (ftFace == nullptr || err || faceIndex >= numFaces) { // Ran out of faces to inspect or FT_New_Face returned an error. @@ -1420,7 +1420,7 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromAppleFont (CTFontRef fontRe break; } } - + // When no match found, use first face in font file as the default face. FT_New_Face (ON_FreeType::Library(), path, 0, &ftFace); // get first face @@ -1513,20 +1513,20 @@ unsigned int ON_FreeTypeGetFontUnitsPerM( font = font->ManagedFont(); if (nullptr == font) break; - + const ON__UINT_PTR ft_face_as_uint = ON_Font::FreeTypeFace(font); if (0 == ft_face_as_uint) break; - + FT_Face ft_face = (FT_Face)ft_face_as_uint; - + unsigned int freetypeUPM = (unsigned int)ft_face->units_per_EM; if (freetypeUPM > 0 && freetypeUPM < 0xFFFFFFF) return freetypeUPM; - + break; } - + return 0; } @@ -1564,15 +1564,15 @@ void ON_FreeTypeGetFontMetrics( return 0; - - const unsigned int glyph_id + + const unsigned int glyph_id = glyph->FontGlyphIdIsSet() ? (unsigned int)glyph->FontGlyphId() : ON_FreeType::GlyphId(face, glyph->CodePoint()); if (0 == glyph_id) return 0; */ - + // Turns out that checking H and I doesn't work very well for some // fonts designed for Asian languages, symbol fonts, and emoji fonts. const ON_FontGlyph Iglyph(font, 'I'); @@ -1625,8 +1625,8 @@ public: ~ON_FreeTypeOutlineAccumlator() = default; bool AddFreeTypeFiguresToOutline( - FT_Face ft_face, - FT_UInt font_glyph_id, + FT_Face ft_face, + FT_UInt font_glyph_id, ON_Outline& outline ); @@ -1641,7 +1641,7 @@ private: // ON_Outline* destination_outline, // FT_Face ft_face, FT_UInt font_glyph_id, FT_Orientation* ft_orientation // ); -// +// // bool BeginGlyphOutline( // ON__UINT32 font_units_per_em, // bool bSingleStrokeFont, @@ -1714,8 +1714,8 @@ const ON_TextBox ON_TextBox_CreateFromFreeTypeGlyphMetrics( } bool ON_FreeTypeOutlineAccumlator::AddFreeTypeFiguresToOutline( - FT_Face ft_face, - FT_UInt font_glyph_id, + FT_Face ft_face, + FT_UInt font_glyph_id, ON_Outline& outline ) { @@ -1742,7 +1742,7 @@ bool ON_FreeTypeOutlineAccumlator::AddFreeTypeFiguresToOutline( if (FT_Err_Ok != FT_Get_Glyph(ft_face->glyph, &ft_glyph)) break; if (nullptr == ft_glyph) - break; + break; if (FT_GLYPH_FORMAT_OUTLINE != ft_glyph->format) break; @@ -1758,7 +1758,7 @@ bool ON_FreeTypeOutlineAccumlator::AddFreeTypeFiguresToOutline( } m_ft_orientation = FT_Outline_Get_Orientation(&ft_outline); - m_end_figure_point_type + m_end_figure_point_type = (ON_OutlineFigure::Type::SingleStroke == outline.FigureType()) ? ON_OutlineFigurePoint::Type::EndFigureOpen : ON_OutlineFigurePoint::Type::EndFigureClosed; @@ -1786,9 +1786,9 @@ bool ON_FreeTypeOutlineAccumlator::AddFreeTypeFiguresToOutline( return rc; } -int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineMoveToFunc( - const FT_Vector* to, - void* user +int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineMoveToFunc( + const FT_Vector* to, + void* user ) { ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user; @@ -1806,8 +1806,8 @@ int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineMoveToFunc( } int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToFunc( - const FT_Vector* to, - void* user + const FT_Vector* to, + void* user ) { ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user; @@ -1821,8 +1821,8 @@ int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToFunc( } int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToCloseContourFunc( - const FT_Vector* to, - void* user + const FT_Vector* to, + void* user ) { ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user; @@ -1831,19 +1831,19 @@ int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToCloseContourFunc a->EndFigure(a->m_end_figure_point_type); a->m_prev_point = *to; - + return FT_Err_Ok; } int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineConicToFunc( const FT_Vector* control, - const FT_Vector* to, - void* user + const FT_Vector* to, + void* user ) { ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user; if (nullptr == a) - return FT_Err_Invalid_Argument; + return FT_Err_Invalid_Argument; a->AppendQuadraticBezier(Internal_To2fPoint(control),Internal_To2fPoint(to)); a->m_prev_point = *to; @@ -1854,8 +1854,8 @@ int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineConicToFunc( int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineCubicToFunc( const FT_Vector* control1, const FT_Vector* control2, - const FT_Vector* to, - void* user + const FT_Vector* to, + void* user ) { ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user; @@ -1890,7 +1890,7 @@ bool ON_FreeTypeLoadGlyph( #endif for (int pass = bLoadRenderBitmap ? 1 : pass0; pass < 2; pass++) { - FT_Int32 ft_face_load_no_scale_flag + FT_Int32 ft_face_load_no_scale_flag = (0 == pass) ? 0 : FT_LOAD_NO_SCALE; @@ -1905,10 +1905,10 @@ bool ON_FreeTypeLoadGlyph( /* Avoid use of FT_LOAD_NO_SCALE https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_LOAD_NO_SCALE - If the font is tricky (see FT_FACE_FLAG_TRICKY for more), using FT_LOAD_NO_SCALE - usually yields meaningless outlines because the subglyphs must be scaled and - positioned with hinting instructions. This can be solved by loading the font - without FT_LOAD_NO_SCALE and setting the character size to font->units_per_EM. + If the font is ‘tricky’ (see FT_FACE_FLAG_TRICKY for more), using FT_LOAD_NO_SCALE + usually yields meaningless outlines because the subglyphs must be scaled and + positioned with hinting instructions. This can be solved by loading the font + without FT_LOAD_NO_SCALE and setting the character size to ‘font->units_per_EM’. */ #if defined(ON_RUNTIME_WIN) // Unit Systems: @@ -1974,7 +1974,7 @@ bool ON_FreeTypeLoadGlyph( unsigned int ON_FreeTypeGetGlyphMetrics( const ON_FontGlyph* glyph, class ON_TextBox& glyph_metrics_in_font_design_units -) +) { glyph_metrics_in_font_design_units = ON_TextBox::Unset; @@ -1994,7 +1994,7 @@ unsigned int ON_FreeTypeGetGlyphMetrics( FT_Face face = (FT_Face)ft_face_as_uint; - const unsigned int glyph_index + const unsigned int glyph_index = glyph->FontGlyphIndexIsSet() ? glyph->FontGlyphIndex() : ON_FreeType::GlyphId(face, glyph->CodePoint()); @@ -2015,7 +2015,7 @@ unsigned int ON_FreeTypeGetGlyphMetrics( //// } ////#endif - + const bool bLoadRenderBitmap = false; // bLoadRenderBitmap = false means we load using FT_LOAD_NO_SCALE // This won't work for "tricky" font faces that render glyphs using composites. @@ -2024,7 +2024,7 @@ unsigned int ON_FreeTypeGetGlyphMetrics( if ( nullptr == face->glyph) return 0; - + // Because ft_load_flags includes FT_LOAD_NO_SCALE, the // face->glyph->metrics units are expressed in font design units. glyph_metrics_in_font_design_units = ON_TextBox_CreateFromFreeTypeGlyphMetrics(&face->glyph->metrics); @@ -2079,7 +2079,7 @@ bool ON_FreeTypeGetGlyphOutline( if (glyph_index <= 0) return false; } - + FT_Face ft_face = (FT_Face)(ON_Font::FreeTypeFace(font)); if (nullptr == ft_face) return false; @@ -2133,10 +2133,10 @@ void ON_Font::DumpFreeTypeFace( s = face->family_name; text_log.Print("Family name = %ls\n", static_cast(s)); - + s = face->style_name; text_log.Print("Style name = %ls\n", static_cast(s)); - + FT_Long style_mask = 0xFFFF; s = ON_FreeType::StyleFlagsToString(face->style_flags); if ( 0 != (style_mask&face->style_flags) || s.IsNotEmpty() ) @@ -2171,7 +2171,7 @@ void ON_Font::DumpFreeTypeFace( } } text_log.PopIndent(); - + text_log.PopIndent(); return; diff --git a/opennurbs_freetype_include.h b/opennurbs_freetype_include.h index 78bb8f95..b976a2ab 100644 --- a/opennurbs_freetype_include.h +++ b/opennurbs_freetype_include.h @@ -7,7 +7,7 @@ // 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 . // //////////////////////////////////////////////////////////////// @@ -17,18 +17,18 @@ // FreeType Licensing: // -//// Retrieved March 22, 2017 +//// Retrieved March 22, 2017 //// https://www.freetype.org/freetype2/docs/index.html ////What is FreeType? //// -////FreeType is a software font engine that is designed to be small, efficient, -////highly customizable, and portable while capable of producing high-quality +////FreeType is a software font engine that is designed to be small, efficient, +////highly customizable, and portable while capable of producing high-quality ////output (glyph images). It can be used in graphics libraries, display servers, ////font conversion tools, text image generation tools, and many other products as well. //// -////Note that FreeType is a font service and doesn't provide APIs to perform -////higher-level features like text layout or graphics processing -////(e.g., colored text rendering, hollowing, etc.). However, it greatly +////Note that FreeType is a font service and doesn't provide APIs to perform +////higher-level features like text layout or graphics processing +////(e.g., colored text rendering, ‘hollowing’, etc.). However, it greatly ////simplifies these tasks by providing a simple, easy to use, and uniform ////interface to access the content of font files. //// @@ -36,12 +36,12 @@ ////FreeType License and the GNU Public License, Version 2. It can thus ////be used by any kind of projects, be they proprietary or not. //// -////Please note that FreeType is also called FreeType 2, to -////distinguish it from the old, deprecated FreeType 1 library, +////Please note that ‘FreeType’ is also called ‘FreeType 2’, to +////distinguish it from the old, deprecated ‘FreeType 1’ library, ////a predecessor no longer maintained and supported. //// //// http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT -//// +//// //// The FreeType Project LICENSE //// ---------------------------- //// @@ -92,7 +92,7 @@ //// encourage you to use the following text: //// //// """ -//// Portions of this software are copyright The FreeType +//// Portions of this software are copyright © The FreeType //// Project (www.freetype.org). All rights reserved. //// """ //// @@ -219,8 +219,8 @@ // This header file is not included in opennurbs.h because // FreeType 2.6.3 has deeply nested includes and uses angle brackets // in its include files (instead of double quotes and relative paths like opennurbs), -// the directory ./freetype263/include must be in the "system" includes path. -// It is not feasable or reasonable for all projects that include opennurbs.h to have the +// the directory ./freetype263/include must be in the "system" includes path. +// It is not feasable or reasonable for all projects that include opennurbs.h to have the // freetype includes directory in the system includes path. #if defined(OPENNURBS_FREETYPE_SUPPORT) diff --git a/opennurbs_fsp.cpp b/opennurbs_fsp.cpp index 3c31831b..30650dc4 100644 --- a/opennurbs_fsp.cpp +++ b/opennurbs_fsp.cpp @@ -3,7 +3,7 @@ #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 +// 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 @@ -46,7 +46,7 @@ ON_FixedSizePool& ON_FixedSizePool::operator=(ON_FixedSizePool&& src) { if (this != &src) { - Destroy(); + Destroy(); m_first_block = src.m_first_block; m_al_element_stack = src.m_al_element_stack; m_al_block = src.m_al_block; @@ -69,8 +69,8 @@ size_t ON_FixedSizePool::SizeofElement() const return m_sizeof_element; } -bool ON_FixedSizePool::Create( - size_t sizeof_element, +bool ON_FixedSizePool::Create( + size_t sizeof_element, size_t element_count_estimate, size_t block_element_capacity ) @@ -121,14 +121,14 @@ bool ON_FixedSizePool::Create( // Set m_al_count = capacity of the first block. - // If the estimated number of elements is not too big, + // If the estimated number of elements is not too big, // then make the first block that size. if ( element_count_estimate > 0 ) { // this is the first block and it has a custom size if ( 8*m_block_element_count >= element_count_estimate ) m_al_count = element_count_estimate; - else + else m_al_count = 8*m_block_element_count; // first block will be large } else @@ -149,7 +149,7 @@ void ON_FixedSizePool::ReturnAll() //////m_qwerty_it_element = 0; m_al_block = m_first_block; m_al_element_array = (void*)(((char*)m_al_block) + 2*sizeof(void*)); - m_al_count = BlockElementCapacity(m_first_block); + m_al_count = BlockElementCapacity(m_first_block); m_active_element_count = 0; m_total_element_count = 0; } @@ -180,7 +180,7 @@ size_t ON_FixedSizePool::TotalElementCount() const void* ON_FixedSizePool::AllocateDirtyElement() { - void* p; + void* p; if ( 0 != m_al_element_stack ) { @@ -226,7 +226,7 @@ void* ON_FixedSizePool::AllocateDirtyElement() m_first_block = p; // If the call to Create() specified a positive element_count_estimate, // then m_sizeof_block needs to be reset for any future block allocations. - + } else { @@ -279,7 +279,7 @@ bool ON_FixedSizePool::IsValid() const const bool bBlockIsAlBlock = (block == m_al_block); capacity = BlockElementCapacity(block); - count + count = bSkipCcountCheck ? 0xFFFFFFFF : BlockElementCount(block); @@ -336,7 +336,7 @@ bool ON_FixedSizePool::IsValid() const sizeof_block_allocated = sizeof_block_total; block_element_count = block_element_capacity; } - + total_element_count += block_element_count; if (total_element_count > (size_t)m_total_element_count) { @@ -382,7 +382,7 @@ void ON_FixedSizePool::ReturnElement(void* p) if ( m_active_element_count <= 0 ) { // If you get this error, something is seriously wrong. - // You may be returning the same element multiple times or + // You may be returning the same element multiple times or // you may be returning pointers that are not from this pool. // In any case, you're probably going to be crashing sometime soon. ON_ERROR("ON_FixedSizePool::ReturnElement - no active elements exist."); @@ -571,7 +571,7 @@ size_t ON_FixedSizePool::BlockElementCount( const void* block ) const if ( 0 == block || m_sizeof_element <= 0 ) return 0; - const char* block_end + const char* block_end = (block == m_al_block && m_al_count > 0) ? ((const char*)m_al_element_array) : *((const char**)(((const char*)block)+sizeof(void*))); @@ -602,7 +602,7 @@ void* ON_FixedSizePoolIterator::FirstBlock( size_t* block_element_count ) void* ON_FixedSizePoolIterator::NextBlock( size_t* block_element_count ) { - if ( 0 != m_it_block + if ( 0 != m_it_block && m_it_block != m_fsp->m_al_block && m_it_element == (void*)(((char*)m_it_block)+2*sizeof(void*)) ) { @@ -970,4 +970,3 @@ unsigned int ON_FixedSizePool::ResetElementId( return id; } - diff --git a/opennurbs_fsp.h b/opennurbs_fsp.h index 65cce0f2..a9200681 100644 --- a/opennurbs_fsp.h +++ b/opennurbs_fsp.h @@ -16,6 +16,20 @@ #if !defined(OPENNURBS_FSP_INC_) #define OPENNURBS_FSP_INC_ +class ON_CLASS ON_FixedSizePoolElement +{ +private: + // ON_FixedSizePoolElement is never instantiated + ON_FixedSizePoolElement() = delete; + ~ON_FixedSizePoolElement() = delete; + ON_FixedSizePoolElement(const ON_FixedSizePoolElement&) = delete; + ON_FixedSizePoolElement operator=(const ON_FixedSizePoolElement&) = delete; + +public: + // next element - intentionally not initialized because instantiation is not permitted. + ON_FixedSizePoolElement* m_next; +}; + class ON_CLASS ON_FixedSizePool { public: @@ -288,7 +302,18 @@ private: unsigned int m_active_element_count = 0; // number of active elements unsigned int m_total_element_count = 0; // total number of elements (active + returned) + +private: + // Used by The ThreadSafe...() functions and for expert users + // to use when managing memory controlled by this pool. Best + // to ingnore this unless you have a very clear idea of what + // you are doing, why you are doing it, and when you are doing it. + // Otherwise, you'll find yourself waiting forever on a nested + // access request. + friend class ON_SleepLockGuard; ON_SleepLock m_sleep_lock; + +private: unsigned int m_reserved0 = 0; diff --git a/opennurbs_hatch.cpp b/opennurbs_hatch.cpp index 145107a0..8b2fcf79 100644 --- a/opennurbs_hatch.cpp +++ b/opennurbs_hatch.cpp @@ -605,7 +605,7 @@ ON_HatchPattern::HatchFillType ON_HatchPattern::HatchFillTypeFromUnsigned( { ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Solid); ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Lines); - ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Gradient); + //ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Gradient); } ON_ERROR("Invalid hatch_fill_type_as_unsigned value."); return ON_HatchPattern::HatchFillType::Solid; @@ -721,9 +721,9 @@ void ON_HatchPattern::Dump( ON_TextLog& dump) const case ON_HatchPattern::HatchFillType::Lines: dump.Print( "fill type: Lines"); break; - case ON_HatchPattern::HatchFillType::Gradient: - dump.Print( "fill type: Gradient"); - break; + //case ON_HatchPattern::HatchFillType::Gradient: + // dump.Print( "fill type: Gradient"); + // break; } dump.Print( "\n"); @@ -1484,6 +1484,7 @@ void ON_Hatch::Dump( ON_TextLog& dump) const dump.Print("Pattern scale: %g\n", PatternScale()); ON_3dPoint p = this->BasePoint(); dump.Print("Base point: %g, %g, %g\n", p.x, p.y, p.z); + dump.Print("2d base point: %g, %g\n", m_basepoint.x, m_basepoint.y); dump.Print("Plane origin: %g, %g, %g\n", m_plane.origin.x, m_plane.origin.y, m_plane.origin.z); dump.Print("Plane x axis: %g, %g, %g\n", m_plane.xaxis.x, m_plane.xaxis.y, m_plane.xaxis.z); dump.Print("Plane y axis: %g, %g, %g\n", m_plane.yaxis.x, m_plane.yaxis.y, m_plane.yaxis.z); @@ -1711,15 +1712,20 @@ double arbaxisRotation(const ON_Plane& plane) static void UnrotateHatch(ON_Hatch* hatch) { double a = arbaxisRotation(hatch->Plane()); + ON_Plane& plane = *(ON_Plane*)(&hatch->Plane()); if(fabs(a) > ON_ZERO_TOLERANCE) { + ON_2dPoint base2 = hatch->BasePoint2d(); plane.Rotate(-a, plane.zaxis); for(int i = 0; i < hatch->LoopCount(); i++) { ON_Curve* pC = (ON_Curve*)hatch->Loop(i)->Curve(); pC->Rotate(a, ON_3dVector::ZAxis, ON_3dPoint::Origin); } + base2.Rotate(a, ON_2dPoint::Origin); + hatch->SetBasePoint(base2); + //hatch->SetPatternRotation(hatch->PatternRotation()+a); } // Project world origin to hatch plane and set hatch plane origin to the result @@ -1735,11 +1741,16 @@ static void UnrotateHatch(ON_Hatch* hatch) ON_Curve* pC = (ON_Curve*)hatch->Loop(i)->Curve(); pC->Translate(V); } + + ON_2dPoint base2 = hatch->BasePoint2d(); + base2 = base2 + ON_2dVector(-P.x, -P.y); + hatch->SetBasePoint(base2); + P = plane.PointAt(P.x, P.y); plane.origin = P; } } - + bool ON_Hatch::Transform( const ON_Xform& xform) { if( fabs( fabs( xform.Determinant()) - 1.0) > 1.0e-4) @@ -1761,7 +1772,11 @@ bool ON_Hatch::Transform( const ON_Xform& xform) for( int i = 0; i < LoopCount(); i++) m_loops[i]->m_p2dCurve->Transform( T); } - int rc = m_plane.Transform( xform); + + ON_3dPoint base = m_plane.PointAt(m_basepoint.x, m_basepoint.y); + base.Transform(xform); + int rc = m_plane.Transform(xform); + SetBasePoint(base); //ON_3dVector x = m_plane.xaxis; //x.Transform(xform); @@ -2043,3 +2058,357 @@ ON_CurveRegionBoundaryElement& ON_CurveRegionBoundaryElement::operator=(const ON } +#if defined(OPENNURBS_GRADIENT_WIP) + + +class ON_CLASS ON_GradientColorData : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_GradientColorData); +public: + ON_GradientColorData(); + ~ON_GradientColorData() = default; + + ON_GradientColorData(const ON_GradientColorData&) = default; + ON_GradientColorData& operator=(const ON_GradientColorData&) = default; + + //ON_GradientColorData(const ON_GradientColorData&); + //ON_GradientColorData& operator=(const ON_GradientColorData&); + + static ON_GradientColorData* FromObject( const ON_Object* ); + static ON_GradientColorData* FromObject(ON_Object* obj, bool createAndAttachIfMissing); + + // override virtual ON_Object::Dump function + void Dump(ON_TextLog& text_log) const override; + + // override virtual ON_Object::SizeOf function + unsigned int SizeOf() const override; + + // override virtual ON_Object::DataCRC function + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + // override virtual ON_Object::Write function + bool Write(ON_BinaryArchive& binary_archive) const override; + + // override virtual ON_Object::Read function + bool Read(ON_BinaryArchive& binary_archive) override; + + // override virtual ON_UserData::GetDescription function + bool GetDescription(ON_wString& description) override; + + // override virtual ON_UserData::Archive function + bool WriteToArchive( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + + // override virtual ON_UserData::Transform function + bool Transform(const ON_Xform&) override; + + ON_GradientType m_gradient_type = ON_GradientType::Linear; + ON_3dPoint m_start = ON_3dPoint::UnsetPoint; + ON_3dPoint m_end = ON_3dPoint::UnsetPoint; + double m_repeat = 0; + ON_SimpleArray m_colors; + +//private: + //void Internal_CopyFrom(const ON_GradientColorData&); +}; + +ON_OBJECT_IMPLEMENT(ON_GradientColorData, ON_UserData, "0C1AD613-4EFA-4F47-A147-4D79D77FCB0C"); + +ON_GradientColorData::ON_GradientColorData() +{ + m_userdata_uuid = ON_CLASS_ID(ON_GradientColorData); + m_application_uuid = ON_opennurbs6_id; + m_userdata_copycount = 1; +} + +//void ON_GradientColorData::Internal_CopyFrom(const ON_GradientColorData & src) +//{ +// m_gradient_type = src.m_gradient_type; +// m_start = src.m_start; +// m_end = src.m_end; +// m_repeat = src.m_repeat; +// m_colors = src.m_colors; +//} +// +//ON_GradientColorData::ON_GradientColorData(const ON_GradientColorData &src) +// : ON_UserData(src) +//{ +// Internal_CopyFrom(src); +//} + +ON_GradientColorData* ON_GradientColorData::FromObject(const ON_Object* p) +{ + return p + ? ON_GradientColorData::Cast(p->GetUserData(ON_CLASS_ID(ON_GradientColorData))) + : nullptr; +} + +//ON_GradientColorData & ON_GradientColorData::operator=(const ON_GradientColorData & src) +//{ +// if (this != &src) +// { +// ON_UserData::operator=(src); +// Internal_CopyFrom(src); +// } +// return *this; +//} + +ON_GradientColorData* ON_GradientColorData::FromObject(ON_Object* obj, bool createAndAttachIfMissing) +{ + if (nullptr == obj) + return nullptr; + ON_GradientColorData* rc = ON_GradientColorData::Cast(obj->GetUserData(ON_CLASS_ID(ON_GradientColorData))); + if (nullptr == rc && createAndAttachIfMissing) + { + rc = new ON_GradientColorData(); + if (!obj->AttachUserData(rc)) + { + delete rc; + return nullptr; + } + } + return rc; +} + +bool ON_GradientColorData::GetDescription(ON_wString& description) +{ + description = L"Color Gradient UserData"; + return true; +} + +bool ON_GradientColorData::WriteToArchive(const ON_BinaryArchive & archive, const ON_Object * parent_object) const +{ + return (archive.Archive3dmVersion() >= 60); +} + +unsigned int ON_GradientColorData::SizeOf() const +{ + unsigned int sz = ON_UserData::SizeOf(); + sz += sizeof(*this) - sizeof(ON_UserData); + sz += m_colors.SizeOfArray(); + return sz; +} + +ON__UINT32 ON_GradientColorData::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder, sizeof(m_gradient_type), &m_gradient_type); + current_remainder = m_start.DataCRC(current_remainder); + current_remainder = m_end.DataCRC(current_remainder); + current_remainder = ON_CRC32(current_remainder, sizeof(m_repeat), &m_repeat); + current_remainder = m_colors.DataCRC(current_remainder); + return current_remainder; +} + +void ON_GradientColorData::Dump(ON_TextLog& text_log) const +{ + switch (m_gradient_type) + { + case ON_GradientType::None: + text_log.Print("None gradient\n"); + break; + case ON_GradientType::Linear: + text_log.Print("Linear gradient\n"); + break; + case ON_GradientType::Radial: + text_log.Print("Radial gradient\n"); + break; + case ON_GradientType::LinearDisabled: + text_log.Print("Linear(disabled) gradient\n"); + break; + case ON_GradientType::RadialDisabled: + text_log.Print(L"Radial(disabled) gradient\n"); + break; + default: + break; + } + + text_log.PushIndent(); + text_log.Print("points "); + text_log.Print(m_start); + text_log.Print("-"); + text_log.Print(m_end); + text_log.Print("\nrepeat %d\n", m_repeat); + text_log.PopIndent(); +} + +bool ON_GradientColorData::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0); + if (!rc) + return false; + + for (;;) + { + rc = archive.WriteInt((int)m_gradient_type); + if (!rc) break; + rc = archive.WritePoint(m_start); + if (!rc) break; + rc = archive.WritePoint(m_end); + if (!rc) break; + rc = archive.WriteDouble(m_repeat); + if (!rc) break; + + int count = m_colors.Count(); + rc = archive.WriteInt(count); + if (!rc) break; + + for (int i = 0; i < count && rc; i++) + { + rc = m_colors[i].Write(archive); + if (!rc) break; + } + if (!rc) break; + + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_GradientColorData::Read(ON_BinaryArchive& archive) +{ + m_colors.SetCount(0); + + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version); + if (!rc) + return false; + + for (;;) + { + rc = (1 == major_version); + if (!rc) break; + + int gt = (int)ON_GradientType::None; + rc = archive.ReadInt(>); + if (gt < (int)ON_GradientType::None || gt >(int)ON_GradientType::RadialDisabled) + rc = false; + if (!rc) break; + m_gradient_type = (ON_GradientType)gt; + + rc = archive.ReadPoint(m_start); + if (!rc) break; + rc = archive.ReadPoint(m_end); + if (!rc) break; + rc = archive.ReadDouble(&m_repeat); + if (!rc) break; + int count = 0; + rc = archive.ReadInt(&count); + if (!rc) break; + + m_colors.Reserve(count); + for (int i = 0; i < count && rc; i++) + { + ON_ColorStop cs; + rc = cs.Read(archive); + if (!rc) break; + + m_colors.Append(cs); + } + if (!rc) break; + + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +bool ON_GradientColorData::Transform(const ON_Xform& xform) +{ + m_start.Transform(xform); + m_end.Transform(xform); + return ON_UserData::Transform(xform); +} + +///////////////////////////////////////////////////////////////////////// + +ON_GradientType ON_Hatch::GetGradientType() const +{ + ON_GradientColorData* data = ON_GradientColorData::FromObject(this); + if (data) + return data->m_gradient_type; + return ON_GradientType::None; +} + +void ON_Hatch::SetGradientType(ON_GradientType gt) +{ + ON_GradientColorData* data = ON_GradientColorData::FromObject(this); + if (nullptr == data && ON_GradientType::None == gt) + return; + + data = ON_GradientColorData::FromObject(this, true); + if (data) + data->m_gradient_type = gt; +} + +void ON_Hatch::GetGradientColors(ON_SimpleArray& colors) const +{ + colors.Empty(); + ON_GradientColorData* data = ON_GradientColorData::FromObject(this); + if (data) + colors = data->m_colors; +} +bool ON_Hatch::SetGradientColors(const ON_SimpleArray& colors) +{ + ON_GradientColorData* data = ON_GradientColorData::FromObject(this, true); + if (nullptr == data) + return false; + data->m_colors = colors; + return true; +} + +double ON_Hatch::GetGradientRepeat() const +{ + ON_GradientColorData* data = ON_GradientColorData::FromObject(this); + if (data) + return data->m_repeat; + return 0; +} +bool ON_Hatch::SetGradientRepeat(double repeat) +{ + ON_GradientColorData* data = ON_GradientColorData::FromObject(this); + if (nullptr == data && 0 == repeat) + return true; + + data = ON_GradientColorData::FromObject(this, true); + if (nullptr == data) + return false; + + data->m_repeat = repeat; + return true; +} + +void ON_Hatch::GetGradientEndPoints(ON_3dPoint& startPoint, ON_3dPoint& endPoint) const +{ + ON_GradientColorData* data = ON_GradientColorData::FromObject(this); + if (nullptr == data) + { + startPoint = ON_3dPoint::UnsetPoint; + endPoint = ON_3dPoint::UnsetPoint; + return; + } + + startPoint = data->m_start; + endPoint = data->m_end; +} +bool ON_Hatch::SetGradientEndPoints(ON_3dPoint startpoint, ON_3dPoint endpoint) +{ + ON_GradientColorData* data = ON_GradientColorData::FromObject(this, true); + if (nullptr == data) + return false; + + data->m_start = startpoint; + data->m_end = endpoint; + return true; +} + +#endif diff --git a/opennurbs_hatch.h b/opennurbs_hatch.h index 76065fbe..bbf34f59 100644 --- a/opennurbs_hatch.h +++ b/opennurbs_hatch.h @@ -397,7 +397,7 @@ public: { Solid = 0, // uses entity color Lines = 1, // pat file definition - Gradient = 2, // uses a fill color function + //Gradient = 2, // uses a fill color function }; static ON_HatchPattern::HatchFillType HatchFillTypeFromUnsigned( @@ -883,6 +883,64 @@ public: */ bool ReplaceLoops(ON_SimpleArray& loops); +#if defined(OPENNURBS_GRADIENT_WIP) + /* + Description: + Returns gradient fill type for this hatch + */ + ON_GradientType GetGradientType() const; + + /* + Description: + Set the gradient fill type for this hatch + */ + void SetGradientType(ON_GradientType gt); + + /* + Description: + Get list of color stops used for gradient drawing. + */ + void GetGradientColors(ON_SimpleArray& colors) const; + + /* + Description: + Set list of color stops used for gradient drawing. + */ + bool SetGradientColors(const ON_SimpleArray& colors); + + /* + Description: + Get gradient repeat factor for gradient drawing. + > 1 repeat reflected number of times between start and end point + < -1 repeat wrap number of times between start and end point + any other value does not affect repeat on a gradient + */ + double GetGradientRepeat() const; + + /* + Description: + Set gradient repeat factor for gradient drawing + > 1 repeat reflected number of times between start and end point + < -1 repeat wrap number of times between start and end point + any other value does not affect repeat on a gradient + Returns: + True if the repeat factor was successfully set + */ + bool SetGradientRepeat(double repeat); + + /* + Description: + Get the start and end points for gradient drawing in 3d + */ + void GetGradientEndPoints(ON_3dPoint& startPoint, ON_3dPoint& endPoint) const; + + /* + Description: + Set the start and end points for gradient drawing + */ + bool SetGradientEndPoints(ON_3dPoint startpoint, ON_3dPoint endPoint); +#endif + private: ON_Plane m_plane; double m_pattern_scale = 1.0; diff --git a/opennurbs_internal_V2_annotation.cpp b/opennurbs_internal_V2_annotation.cpp index 22ea36c7..ec309d78 100644 --- a/opennurbs_internal_V2_annotation.cpp +++ b/opennurbs_internal_V2_annotation.cpp @@ -5890,7 +5890,8 @@ const wchar_t* ON_TextDot::FontFace() const void ON_TextDot::SetFontFace( const wchar_t* font_face ) { - SetDotText(font_face, true, m_font_face); + m_font_face = font_face; + m_font_face.TrimLeftAndRight(); } static void SetDisplayBitsFromBool( diff --git a/opennurbs_internal_V5_annotation.cpp b/opennurbs_internal_V5_annotation.cpp index 06d68d52..fbb20f37 100644 --- a/opennurbs_internal_V5_annotation.cpp +++ b/opennurbs_internal_V5_annotation.cpp @@ -897,7 +897,6 @@ ON_Object* ON_BinaryArchive::Internal_ConvertObject( return nullptr; } -#if defined(OPENNURBS_SUBD_WIP) if (ON::object_type::mesh_object == archive_object->ObjectType()) { const ON_Mesh* mesh = ON_Mesh::Cast(archive_object); @@ -931,7 +930,6 @@ ON_Object* ON_BinaryArchive::Internal_ConvertObject( return mesh; } } -#endif // defined(OPENNURBS_SUBD_WIP) // no conversion required. return nullptr; diff --git a/opennurbs_internal_Vx_annotation.cpp b/opennurbs_internal_Vx_annotation.cpp index 2c7a18bd..03a999ee 100644 --- a/opennurbs_internal_Vx_annotation.cpp +++ b/opennurbs_internal_Vx_annotation.cpp @@ -814,17 +814,21 @@ ON_Leader* ON_Leader::CreateFromV5Leader( V6_leader->Internal_SetDimStyleFromV5Annotation(V5_leader,annotation_context); const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle(); - ON_2dVector textdir; - if (V5_leader.GetTextDirection(textdir)) - { - ON_3dVector dir; - dir.Set(textdir.x, textdir.y, 0.0); - if (0.0 > dir * plane.xaxis) - { - // override - V6_leader->SetLeaderTextHorizontalAlignment(&parent_dim_style, ON::TextHorizontalAlignment::Right); - } - } + //ON_2dVector textdir; + //if (V5_leader.GetTextDirection(textdir)) + //{ + // ON_3dVector dir; + // dir.Set(textdir.x, textdir.y, 0.0); + // if (0.0 > dir * plane.xaxis) + // { + // // override + // V6_leader->SetLeaderTextHorizontalAlignment(&parent_dim_style, ON::TextHorizontalAlignment::Right); + // } + //} + + // ON::TextHorizontalAlignment::Auto behaves like all V5 leaders did + V6_leader->SetLeaderTextHorizontalAlignment(&parent_dim_style, ON::TextHorizontalAlignment::Auto); + // updates any m_overrides to be current and updates content hash. parent_dim_style.ContentHash(); diff --git a/opennurbs_internal_glyph.h b/opennurbs_internal_glyph.h index e2cc4bb6..107e9911 100644 --- a/opennurbs_internal_glyph.h +++ b/opennurbs_internal_glyph.h @@ -57,6 +57,17 @@ public: static void Internal_GetAppleInstalledCTFonts(ON_SimpleArray& platform_font_list); #endif +private: + static void Internal_SetFakeWindowsLogfontNames( + ON_SimpleArray& device_list + ); + static void Internal_SetFakeWindowsLogfontName( + const ON_Font* font, + const ON_wString fake_loc_logfont_name, + const ON_wString fake_en_logfont_name + ); +public: + // sorts nulls to end of lists static int CompareFontPointer(ON_Font const* const* lhs, ON_Font const* const* rhs); diff --git a/opennurbs_knot.cpp b/opennurbs_knot.cpp index 039ae202..8f56088a 100644 --- a/opennurbs_knot.cpp +++ b/opennurbs_knot.cpp @@ -435,24 +435,19 @@ bool ON_IsKnotVectorUniform( if (rc) { const double delta = knot[order-1] - knot[order-2]; - const double delta_tol = ON_SQRT_EPSILON*delta; - int i0, i1; - double d; - if ( ON_IsKnotVectorClamped(order,cv_count,knot) ) + rc = (0.0 != delta && delta > ON_UNSET_VALUE && delta < ON_UNSET_POSITIVE_VALUE); + if (rc) { - i0 = order; - i1 = cv_count; - } - else - { - i0 = 1; - i1 = ON_KnotCount(order,cv_count); - } - for (/*empty*/; i0 < i1 && rc; i0++ ) - { - d = knot[i0] - knot[i0-1]; - if ( fabs(d - delta) > delta_tol ) - rc = false; + const int i0 = ON_IsKnotVectorClamped(order, cv_count, knot, 0) ? order : 1; + const int i1 = ON_IsKnotVectorClamped(order, cv_count, knot, 1) ? cv_count : ON_KnotCount(order, cv_count); + double k0 = knot[i0 - 1]; + const double delta_tol = fabs(ON_SQRT_EPSILON*delta); + for (int i = i0; i < i1 && rc; ++i) + { + const double d = knot[i] - k0; + rc = fabs(d - delta) <= delta_tol; + k0 = knot[i]; + } } } return rc; @@ -984,32 +979,37 @@ bool ON_GetGrevilleAbcissae( // get Greville abcissa from knots ) { // Grevielle abscissae for a given knot vector - double x, t0; - int gi, periodic_check; - if ( order < 2 || cv_count < order || !knot || !g ) return false; const int g_count = (bPeriodic) ? cv_count-order+1 : cv_count; - if (order == 2) { + if (order == 2) + { // g[i] = knot[i] in degree 1 case - memcpy( g, knot, g_count*sizeof(*g) ); + for (int i = 0; i < g_count; i++) + g[i] = knot[i]; } - else { + else + { // g = (knot[i]+...+knot[i+degree-1])/degree - t0 = knot[order-2]; - gi = 0; - periodic_check = (bPeriodic) ? order-2 : 0; - while (gi < g_count) { - x = ON_GrevilleAbcissa( order, knot++ ); - if ( periodic_check ) { - periodic_check--; - if ( x < t0 ) - continue; + const double t0 = knot[order-2]; + if (bPeriodic) + { + for (int i = 0; i < order - 1; ++i) + { + g[i] = ON_GrevilleAbcissa(order, knot + i); + if (g[i] >= t0) + { + knot += ((i > 0 && (t0 - g[i - 1]) < (g[i] - t0)) ? (i - 1) : i); + break; + } } - g[gi++] = x; } + for ( int i = 0; i < g_count; ++i) + g[i] = ON_GrevilleAbcissa( order, knot+i ); + if (bPeriodic && g[0] < t0) + g[0] = t0; } return true; diff --git a/opennurbs_leader.cpp b/opennurbs_leader.cpp index 22d74980..3d3074d3 100644 --- a/opennurbs_leader.cpp +++ b/opennurbs_leader.cpp @@ -159,7 +159,6 @@ bool ON_Leader::GetBBox( // returns true if successful //return GetBBox(&ON_DimStyle::Default, 1.0, ON_3dVector::XAxis, ON_3dVector::YAxis, bbox_min, bbox_max, grow); } - bool ON_Leader::GetTextXform( const ON_Viewport* vp, const ON_DimStyle* dimstyle, @@ -185,21 +184,39 @@ bool ON_Leader::GetTextXform( if (nullptr == text) return true; - if ( DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash() ) + ON_2dVector tail_dir = TailDirection(dimstyle); + ON_3dVector view_x = nullptr == vp ? ON_3dVector::XAxis : vp->CameraX(); + ON_3dVector view_y = nullptr == vp ? ON_3dVector::YAxis : vp->CameraY(); + ON_Plane objectplane = Plane(); + ON::TextHorizontalAlignment halign = dimstyle->LeaderTextHorizontalAlignment(); + ON::TextVerticalAlignment valign = dimstyle->LeaderTextVerticalAlignment(); + if (ON::TextHorizontalAlignment::Auto == halign) + { + double xdotx = objectplane.xaxis * view_x; + if (tail_dir.x < -0.00001) + xdotx = -xdotx; + if (xdotx > -0.00001) + halign = ON::TextHorizontalAlignment::Left; + else + halign = ON::TextHorizontalAlignment::Right; + } + ON::TextHorizontalAlignment last_halign = text->RuntimeHorizontalAlignment(); + text->SetRuntimeHorizontalAlignment(halign); + + if (last_halign != halign) + { + const_cast(text)->SetAlignment(halign, valign); + } + if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash() ) { ON_wString rtfstr = text->RtfText(); - ON_Plane objectplane = Plane(); const_cast(text)->Create( rtfstr, ON::AnnotationType::Leader, dimstyle, text->TextIsWrapped(), text->FormattingRectangleWidth(), text->TextRotationRadians()); - ON::TextHorizontalAlignment halign = dimstyle->LeaderTextHorizontalAlignment(); - ON::TextVerticalAlignment valign = dimstyle->LeaderTextVerticalAlignment(); const_cast(text)->SetAlignment(halign, valign); } - ON_2dVector tail_dir = TailDirection(dimstyle); - // Find center of scaled text double textblock_width = 0.0; double textblock_height = 0.0; @@ -295,9 +312,6 @@ bool ON_Leader::GetTextXform( textcenter_xf.m_xform[0][3] = -text_center.x; textcenter_xf.m_xform[1][3] = -text_center.y + text_shift.y; - ON_3dVector view_x = nullptr == vp ? ON_3dVector::XAxis : vp->CameraX(); - ON_3dVector view_y = nullptr == vp ? ON_3dVector::YAxis : vp->CameraY(); - if(ON::TextOrientation::InView == dimstyle->LeaderTextOrientation()) { const ON_Plane& ldrplane = Plane(); diff --git a/opennurbs_line.cpp b/opennurbs_line.cpp index dbec7c70..5e3d776b 100644 --- a/opennurbs_line.cpp +++ b/opennurbs_line.cpp @@ -133,7 +133,7 @@ bool ON_Line::ClosestPointTo( const ON_3dPoint& point, double *t ) const const ON_3dVector D = Direction(); const double DoD = D.LengthSquared(); if ( DoD > 0.0 ) { - if ( point.DistanceTo(from) <= point.DistanceTo(to) ) { + if ((point - from).LengthSquared() <= (point - to).LengthSquared()) { *t = ((point - from)*D)/DoD; } else { @@ -726,6 +726,11 @@ ON_3dPoint ON_Triangle::PointAt(double s1, double s2) const return (1 - s1 - s2)* m_V[0] + s1*m_V[1] + s2*m_V[2]; } +ON_3dPoint ON_Triangle::Centroid() const +{ + return PointAt(1.0/3.0, 1.0/3.0); +} + bool ON_Triangle::ClosestPointTo(const ON_3dPoint & P, double * s1, double * s2) const { bool rc = false; @@ -876,4 +881,3 @@ bool operator!=(const ON_Triangle & a, const ON_Triangle & b) a.m_V[2] != b.m_V[2]); } - diff --git a/opennurbs_line.h b/opennurbs_line.h index f485f1f0..84b0cb77 100644 --- a/opennurbs_line.h +++ b/opennurbs_line.h @@ -41,7 +41,7 @@ public: /* Returns: - True if from != to. + True if from != to and both from and to are valid. */ bool IsValid() const; @@ -440,7 +440,6 @@ public: // Ensure !IsDegenerate() to gaurentee meaningful result ON_PlaneEquation PlaneEquation() const; - /* Description: Evaluate point on triangle. @@ -456,6 +455,10 @@ public: double s1, double s2 ) const; + // Returns: + // Evaluation of PointAt(1/3.0, 1/3.0); + ON_3dPoint Centroid() const; + /* Description: Find the point on the triangle that is @@ -549,7 +552,4 @@ the result will be false. ON_DECL bool operator!=(const ON_Triangle& a, const ON_Triangle& b); - - - #endif diff --git a/opennurbs_material.cpp b/opennurbs_material.cpp index 02d67b98..89cec8d1 100644 --- a/opennurbs_material.cpp +++ b/opennurbs_material.cpp @@ -8,7 +8,7 @@ // 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 . // //////////////////////////////////////////////////////////////// @@ -19,7 +19,7 @@ #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 +// 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 @@ -90,7 +90,7 @@ void ON_Material::Dump( ON_TextLog& dump ) const { ON_ModelComponent::Dump(dump); - + dump.Print("ambient rgb = "); dump.PrintRGB( m_ambient ); dump.Print("\n"); dump.Print("diffuse rgb = "); dump.PrintRGB( m_diffuse ); dump.Print("\n"); dump.Print("emmisive rgb = "); dump.PrintRGB( m_emission ); dump.Print("\n"); @@ -139,7 +139,7 @@ bool ON_Material::Write( ON_BinaryArchive& archive ) const const int minor_version = 0; if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version)) return false; - + bool rc = false; for (;;) { @@ -204,7 +204,7 @@ bool ON_Material::Write( ON_BinaryArchive& archive ) const break; if ( !archive.WriteDouble(m_refraction_glossiness)) break; - if ( !archive.WriteDouble(m_fresnel_index_of_refraction)) + if ( !archive.WriteDouble(m_fresnel_index_of_refraction)) break; if ( !archive.WriteUuid(m_rdk_material_instance_id)) break; @@ -303,14 +303,14 @@ bool ON_Material::Read( ON_BinaryArchive& archive ) break; if ( !archive.ReadDouble(&m_refraction_glossiness)) break; - if ( !archive.ReadDouble(&m_fresnel_index_of_refraction)) + if ( !archive.ReadDouble(&m_fresnel_index_of_refraction)) break; if ( !archive.ReadUuid(m_rdk_material_instance_id)) break; if ( !archive.ReadBool(&m_bUseDiffuseTextureAlphaForObjectTransparencyTexture)) break; - rc = true; + rc = true; break; } if (!archive.EndRead3dmChunk()) @@ -326,7 +326,7 @@ bool ON_Material::Internal_WriteV5( ON_BinaryArchive& file ) const // V2 or V3 file format rc = Internal_WriteV3(file); } - else + else { // V4 file format @@ -485,16 +485,16 @@ bool ON_Material::Internal_ReadV5( ON_BinaryArchive& file ) if ( rc ) rc = file.ReadColor( m_reflection ); if ( rc ) rc = file.ReadColor( m_transparent ); - if ( rc - && file.ArchiveOpenNURBSVersion() < 200912010 - && 128 == m_transparent.Red() + if ( rc + && file.ArchiveOpenNURBSVersion() < 200912010 + && 128 == m_transparent.Red() && 128 == m_transparent.Green() && 128 == m_transparent.Blue() ) { // Prior to 1 Dec 2009 the ON_Material::Defaults() set // m_transparent to 128,128,128. This was the wrong - // value for the default. This "hack" is here to + // value for the default. This "hack" is here to // make it appear that the default was always white. m_transparent = m_diffuse; } @@ -851,7 +851,7 @@ ON::object_type ON_Material::ObjectType() const return ON::material_object; } -int ON_Material::FindTexture( const wchar_t* filename, +int ON_Material::FindTexture( const wchar_t* filename, ON_Texture::TYPE type, int i0 ) const @@ -859,7 +859,7 @@ int ON_Material::FindTexture( const wchar_t* filename, int i, count = m_textures.Count(); for (i = ((i0 < 0) ? 0 : (i0+1)); i < count; i++ ) { - if ( type != m_textures[i].m_type + if ( type != m_textures[i].m_type && type != ON_Texture::TYPE::no_texture_type ) { continue; @@ -912,7 +912,7 @@ double ON_Material::FresnelReflectionCoefficient( y = x + 1.0; // y = c*(g+c) + 1.0 if ( !(y != 0.0) ) break; // y is NAN or zero - + y = (x - 1.0)/y; // y = (c*(g+c) - 1.0)/(c*(g+c) + 1.0) x = 0.5*z*z*(1.0 + y*y); @@ -922,7 +922,7 @@ double ON_Material::FresnelReflectionCoefficient( if ( !ON_IS_FINITE(x) ) break; // x is infinity - return x; // x is + return x; // x is } return 1.0; // error occured @@ -1186,6 +1186,9 @@ int ON_Texture::Compare(const ON_Texture& a, const ON_Texture& b) rc = CompareDouble(a.m_blend_RGB3, b.m_blend_RGB3); if (rc) break; + rc = ((int)(a.m_bTreatAsLinear ? 1 : 0)) - ((int)(b.m_bTreatAsLinear ? 1 : 0)); + if (rc) break; + break; } @@ -1263,6 +1266,9 @@ int ON_Texture::CompareAppearance(const ON_Texture& a, const ON_Texture& b) rc = CompareDouble(a.m_blend_RGB3, b.m_blend_RGB3); if (rc) break; + rc = ((int)(a.m_bTreatAsLinear ? 1 : 0)) - ((int)(b.m_bTreatAsLinear ? 1 : 0)); + if (rc) break; + break; } @@ -1299,12 +1305,82 @@ int ON_Material::CompareNameAndIds( const ON_Material& a, const ON_Material& b ) rc = ON_UuidCompare(a.m_textures[i].m_texture_id, b.m_textures[i].m_texture_id); } - return rc; + return rc; } int ON_Material::CompareColorAttributes( const ON_Material& a, const ON_Material& b ) { + const auto a_pbr = a.PhysicallyBased(); + const auto b_pbr = b.PhysicallyBased(); + if (a_pbr.Supported() && !b_pbr.Supported()) + return -1; + if (!a_pbr.Supported() && b_pbr.Supported()) + return 1; + + if (a_pbr.Supported() && b_pbr.Supported()) + { + int rc = a_pbr.BaseColor().Compare(a_pbr.BaseColor()); + if (rc) return rc; + + rc = ((int)a_pbr.BRDF()) - ((int)b_pbr.BRDF()); + if (rc) return rc; + + rc = CompareDouble(a_pbr.Subsurface(), b_pbr.Subsurface()); + if (0 != rc) return rc; + + rc = a_pbr.SubsurfaceScatteringColor().Compare(b_pbr.SubsurfaceScatteringColor()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.SubsurfaceScatteringRadius(), b_pbr.SubsurfaceScatteringRadius()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.Metallic(), b_pbr.Metallic()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.Specular(), b_pbr.Specular()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.SpecularTint(), b_pbr.SpecularTint()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.Roughness(), b_pbr.Roughness()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.Sheen(), b_pbr.Sheen()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.SheenTint(), b_pbr.SheenTint()); + if (0 != rc) return rc; + + //rc = CompareDouble(a_pbr.ReflectiveIOR(), b_pbr.ReflectiveIOR()); + //if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.Anisotropic(), b_pbr.Anisotropic()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.AnisotropicRotation(), b_pbr.AnisotropicRotation()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.Clearcoat(), b_pbr.Clearcoat()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.ClearcoatRoughness(), b_pbr.ClearcoatRoughness()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.Opacity(), b_pbr.Opacity()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.OpacityIOR(), b_pbr.OpacityIOR()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.OpacityRoughness(), b_pbr.OpacityRoughness()); + if (0 != rc) return rc; + + rc = a_pbr.Emission().Compare(b_pbr.Emission()); + return rc; + } + int rc = a.m_ambient.Compare(b.m_ambient); if (rc) return rc; @@ -1328,39 +1404,76 @@ int ON_Material::CompareColorAttributes( const ON_Material& a, const ON_Material rc = ((int)a.m_bDisableLighting) - ((int)b.m_bDisableLighting); - return rc; + return rc; } -int ON_Material::CompareReflectionAttributes( const ON_Material& a, const ON_Material& b ) +int ON_Material::CompareReflectionAttributes(const ON_Material& a, const ON_Material& b) { - int rc = a.m_reflection.Compare( b.m_reflection ); - if (0 != rc) return rc; + const auto a_pbr = a.PhysicallyBased(); + const auto b_pbr = b.PhysicallyBased(); + if (a_pbr.Supported() && !b_pbr.Supported()) + return -1; + if (!a_pbr.Supported() && b_pbr.Supported()) + return 1; - rc = CompareDouble(a.m_index_of_refraction,b.m_index_of_refraction); - if (0 != rc) return rc; - - rc = CompareDouble(a.m_reflectivity,b.m_reflectivity); - if (0 != rc) return rc; - - rc = CompareDouble(a.m_shine,b.m_shine); - if (0 != rc) return rc; - - rc = (a.m_bFresnelReflections?1:0) - (b.m_bFresnelReflections?1:0); - if (0 != rc) return rc; - if ( a.m_bFresnelReflections ) - { - rc = CompareDouble(a.m_fresnel_index_of_refraction,b.m_fresnel_index_of_refraction); + if (a_pbr.Supported() && b_pbr.Supported()) + { + int rc = CompareDouble(a_pbr.Metallic(), b_pbr.Metallic()); if (0 != rc) return rc; - } - rc = CompareDouble(a.m_reflection_glossiness,b.m_reflection_glossiness); - if (0 != rc) return rc; + rc = CompareDouble(a_pbr.Specular(), b_pbr.Specular()); + if (0 != rc) return rc; - rc = CompareDouble(a.m_refraction_glossiness,b.m_refraction_glossiness); + rc = CompareDouble(a_pbr.SpecularTint(), b_pbr.SpecularTint()); + if (0 != rc) return rc; + rc = CompareDouble(a_pbr.Roughness(), b_pbr.Roughness()); + if (0 != rc) return rc; - return rc; + //rc = CompareDouble(a_pbr.ReflectiveIOR(), b_pbr.ReflectiveIOR()); + //if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.Anisotropic(), b_pbr.Anisotropic()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.AnisotropicRotation(), b_pbr.AnisotropicRotation()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.Clearcoat(), b_pbr.Clearcoat()); + if (0 != rc) return rc; + + rc = CompareDouble(a_pbr.ClearcoatRoughness(), b_pbr.ClearcoatRoughness()); + + return rc; + } + + int rc = a.m_reflection.Compare(b.m_reflection); + if (0 != rc) return rc; + + rc = CompareDouble(a.m_index_of_refraction, b.m_index_of_refraction); + if (0 != rc) return rc; + + rc = CompareDouble(a.m_reflectivity, b.m_reflectivity); + if (0 != rc) return rc; + + rc = CompareDouble(a.m_shine, b.m_shine); + if (0 != rc) return rc; + + rc = (a.m_bFresnelReflections ? 1 : 0) - (b.m_bFresnelReflections ? 1 : 0); + if (0 != rc) return rc; + if (a.m_bFresnelReflections) + { + rc = CompareDouble(a.m_fresnel_index_of_refraction, b.m_fresnel_index_of_refraction); + if (0 != rc) return rc; + } + + rc = CompareDouble(a.m_reflection_glossiness, b.m_reflection_glossiness); + if (0 != rc) return rc; + + rc = CompareDouble(a.m_refraction_glossiness, b.m_refraction_glossiness); + + return rc; } int ON_Material::CompareTextureAttributes(const ON_Material& a, const ON_Material& b) @@ -1473,11 +1586,11 @@ const ON_UUID ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_class_id_ctor const ON_UUID ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_userdata_uuid_ctor = { 0xb63ed079, 0xcf67, 0x416c, { 0x80, 0xd, 0x22, 0x2, 0x3a, 0xe1, 0xbe, 0x21 } }; -// CRhRdkUserData::m_application_uuid value +// CRhRdkUserData::m_application_uuid value // = CRhRdkRhinoPlugIn::RhinoPlugInUuid() // = CRhRdkRhinoPlugIn::m_uuidRhinoPlugIn // = 16592D58-4A2F-401D-BF5E-3B87741C1B1B -// From +// From // file: http://subversion.mcneel.com/rhino/6/trunk/src4/rhino4/Plug-ins/RDK/RDK/RhRcmRhinoPlugIn.cpp // revision: 102101 // @@ -1561,7 +1674,7 @@ bool ON_RdkMaterialInstanceIdObsoleteUserData::DeleteAfterRead( ON_Material* mat = ON_Material::Cast(parent_object); if (mat && mat->RdkMaterialInstanceIdIsNil()) mat->SetRdkMaterialInstanceId(m_rdk_material_instance_id); - + // Returning true will cause the ON_BinaryArchive::ReadObject() // plumbing to delete this user data. return true; @@ -1606,11 +1719,11 @@ static const char* ParsePast(const char* token, const char* s) { if (0 == token || token[0] <= 32) return 0; - + s = ParsePastWhiteSpace(s); if (0 == s || s[0] <= 32) return 0; - + for (/*empty init*/; ToUpper(*s) == ToUpper(*token); s++, token++) { if (0 == *s) @@ -1644,7 +1757,7 @@ bool ON_RdkMaterialInstanceIdObsoleteUserData::Read( rc = true; break; } - + ON_String str((char)0, (int)s_length); if (str.Length() < s_length) break; @@ -1696,7 +1809,7 @@ bool ON_RdkMaterialInstanceIdObsoleteUserData::Write( "\n" "\n" "\n" ""; @@ -1820,6 +1933,10 @@ ON_Color ON_Material::PreviewColor(void) const bool ON_Material::UseDiffuseTextureAlphaForObjectTransparencyTexture() const { + //Physically based materials do not support alpha transparency (at the moment). + if (PhysicallyBased().Supported()) + return false; + return m_bUseDiffuseTextureAlphaForObjectTransparencyTexture; } @@ -1833,7 +1950,7 @@ void ON_Material::SetUseDiffuseTextureAlphaForObjectTransparencyTexture( IncrementContentVersionNumber(); } } - + bool ON_Material::FresnelReflections() const { return m_bFresnelReflections; @@ -1892,9 +2009,9 @@ bool ON_Texture::Write( ) const { const int major_version = 1; - const int minor_version + const int minor_version = binary_archive.Archive3dmVersion() >= 60 - ? 1 + ? (binary_archive.Archive3dmVersion() >= 70 ? 2 : 1) : 0; bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version); @@ -1964,11 +2081,18 @@ bool ON_Texture::Write( if ( minor_version <= 0 ) break; - + // version 1.1 added m_image_file_reference rc = m_image_file_reference.Write(true,binary_archive); if (!rc) break; + if ( minor_version <= 1 ) + break; + + // version 1.2 added m_bTreatAsLinear + rc = binary_archive.WriteBool(m_bTreatAsLinear); + if (!rc) break; + break; } @@ -2050,6 +2174,7 @@ const ON_SHA1_Hash ON_Texture::ContentHash() const sha1.AccumulateDouble(m_blend_RGB2); sha1.AccumulateDouble(m_blend_RGB3); sha1.AccumulateInteger32(m_blend_order); + sha1.AccumulateBool(m_bTreatAsLinear); return sha1.Hash(); } @@ -2076,15 +2201,34 @@ void ON_Texture::SetMappingChannel( m_mapping_channel_id = mapping_channel_id; } -ON_Texture::TYPE ON_Texture::TypeFromUnsigned( unsigned int type_as_unsigned ) +ON_Texture::TYPE ON_Texture::TypeFromUnsigned(unsigned int type_as_unsigned) { - switch(type_as_unsigned) + switch (type_as_unsigned) { - ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::no_texture_type ); - ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::bitmap_texture); - ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::bump_texture); - ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::emap_texture); - ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::transparency_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::no_texture_type); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::diffuse_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::bump_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::emap_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::opacity_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_subsurface_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_subsurface_scattering_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_subsurface_scattering_radius_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_metallic_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_specular_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_specular_tint_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_roughness_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_anisotropic_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_anisotropic_rotation_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_sheen_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_sheen_tint_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_roughness_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_bump_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_opacity_ior_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_opacity_roughness_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_emission_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_ambient_occlusion_texture); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_displacement_texture); } ON_ERROR("Invalid type_as_unsigned value."); @@ -2242,6 +2386,12 @@ bool ON_Texture::Read( rc = m_image_file_reference.Read(binary_archive); if (!rc) break; + if (minor_version <= 1) + break; + + rc = binary_archive.ReadBool(&m_bTreatAsLinear); + if (!rc) break; + break; } } @@ -2343,7 +2493,7 @@ ON_TextureMapping::TEXTURE_SPACE ON_TextureMapping::TextureSpaceFromUnsigned( ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TEXTURE_SPACE::single); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TEXTURE_SPACE::divided); } - + ON_ERROR("Invalid texture_space_as_unsigned value."); return ON_TextureMapping::TEXTURE_SPACE::single; } @@ -2355,7 +2505,7 @@ const ON_wString ON_TextureMapping::SpaceToString(ON_TextureMapping::TEXTURE_SPA ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TEXTURE_SPACE::single); ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TEXTURE_SPACE::divided); } - + ON_ERROR("Invalid texture_mapping_space value."); return ON_wString::EmptyString; } @@ -2484,11 +2634,11 @@ ON_Xform ON_Texture::GetPictureShrinkSurfaceTransformation( const ON_Xform* error_return ) { - const class ON_Surface* original_srf + const class ON_Surface* original_srf = (nullptr != original && 1 == original->m_F.Count()) ? original->m_F[0].SurfaceOf() : nullptr; - const class ON_Surface* shrunk_srf + const class ON_Surface* shrunk_srf = (nullptr != shrunk && 1 == shrunk->m_F.Count()) ? shrunk->m_F[0].SurfaceOf() : nullptr; @@ -2506,7 +2656,7 @@ ON_Xform ON_Texture::GetPictureShrinkSurfaceTransformation( ) { ON_Interval original_udomain; - ON_Interval original_vdomain; + ON_Interval original_vdomain; ON_Interval shrunk_udomain; ON_Interval shrunk_vdomain; if (nullptr != original) @@ -2750,7 +2900,7 @@ int IntersectBoxRayHelper(const ON_3dPoint& rst, const ON_3dVector& n, int dir, || Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON) ) { - // The ray's intersection with the plane missed the + // The ray's intersection with the plane missed the // (-1,+1)x(-1,+1) square that is the side of the box. t0 = ON_UNSET_VALUE; } @@ -2768,7 +2918,7 @@ int IntersectBoxRayHelper(const ON_3dPoint& rst, const ON_3dVector& n, int dir, || Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON) ) { - // The ray's intersection with the plane missed the + // The ray's intersection with the plane missed the // (-1,+1)x(-1,+1) square that is the side of the box. t1 = ON_UNSET_VALUE; if ( ON_UNSET_VALUE == t0 ) @@ -2793,7 +2943,7 @@ int IntersectBoxRayHelper(const ON_3dPoint& rst, const ON_3dVector& n, int dir, } -int ON_TextureMapping::EvaluatePlaneMapping( +int ON_TextureMapping::EvaluatePlaneMapping( const ON_3dPoint& P, const ON_3dVector& N, ON_3dPoint* T @@ -2821,7 +2971,7 @@ int ON_TextureMapping::EvaluatePlaneMapping( rst.x = 0.5*rst.x + 0.5; rst.y = 0.5*rst.y + 0.5; - // Apply texture coordinate transformation + // Apply texture coordinate transformation *T = m_uvw*rst; //See docs - if m_bCapped is false, then planar is truely flat. @@ -2831,7 +2981,7 @@ int ON_TextureMapping::EvaluatePlaneMapping( return 1; } -int ON_TextureMapping::EvaluateSphereMapping( +int ON_TextureMapping::EvaluateSphereMapping( const ON_3dPoint& P, const ON_3dVector& N, ON_3dPoint* T @@ -2844,15 +2994,15 @@ int ON_TextureMapping::EvaluateSphereMapping( ON_3dPoint rst(m_Pxyz*P); const double r = ((const ON_3dVector*)(&rst.x))->Length(); double t0, t1; - + if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection ) { ON_3dVector n(m_Nxyz*N); - // Shoot a ray from P in the direction N and see if it + // Shoot a ray from P in the direction N and see if it // hits the sphere. - int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y+n.z*n.z), - 2.0*(rst.x*n.x+rst.y*n.y+rst.z*n.z), - (rst.x*rst.x+rst.y*rst.y+rst.z*rst.z) - 1.0, + int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y+n.z*n.z), + 2.0*(rst.x*n.x+rst.y*n.y+rst.z*n.z), + (rst.x*rst.x+rst.y*rst.y+rst.z*rst.z) - 1.0, &t0, &t1 ); if (rc >= 0 ) { @@ -2863,17 +3013,17 @@ int ON_TextureMapping::EvaluateSphereMapping( rst = rst + t0*n; } } - + // convert sphere 3d location to longitude, latitude, radius - double longitude = (0.0 != rst.y || 0.0 != rst.x) - ? atan2(rst.y,rst.x) + double longitude = (0.0 != rst.y || 0.0 != rst.x) + ? atan2(rst.y,rst.x) : 0.0; - double latitude = (0.0 != rst.z) - ? atan2(rst.z,((const ON_2dVector*)(&rst.x))->Length()) + double latitude = (0.0 != rst.z) + ? atan2(rst.z,((const ON_2dVector*)(&rst.x))->Length()) : 0.0; if ( latitude > ON_PI ) latitude -= 2.0*ON_PI; - + // convert longitude to normalized texture coordinate rst.x = 0.5*longitude/ON_PI; if ( rst.x < -ON_EPSILON ) @@ -2889,17 +3039,17 @@ int ON_TextureMapping::EvaluateSphereMapping( rst.y = 0.0; else if ( rst.y > 1.0 ) rst.y = 1.0; - + // radius is already normalized rst.z = r; - + // apply texture coordinate transformation *T = m_uvw*rst; return 1; } -int ON_TextureMapping::EvaluateCylinderMapping( +int ON_TextureMapping::EvaluateCylinderMapping( const ON_3dPoint& P, const ON_3dVector& N, ON_3dPoint* T @@ -2917,17 +3067,17 @@ int ON_TextureMapping::EvaluateCylinderMapping( double t, t0, t1; int side0, side1; PROJECTION mapping_proj = m_projection; - + side0 = 0; if ( ON_TextureMapping::PROJECTION::ray_projection == mapping_proj ) { ON_3dVector n(m_Nxyz*N); t = 0.0; - + if ( m_bCapped ) { // shoot at caps - // The < t check prevents overflow when the + // The < t check prevents overflow when the // ray is nearly parallel to the cap. t = fabs(n.z)*on__overflow_tol; if ( fabs(1.0+rst.z) < t && fabs(1.0-rst.z) < t ) @@ -2940,7 +3090,7 @@ int ON_TextureMapping::EvaluateCylinderMapping( if ( fabs(1.0+Q.z) > ON_SQRT_EPSILON || (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON ) { - // The ray's intersection with the bottom plane missed the + // The ray's intersection with the bottom plane missed the // radius 1 disk that is the bottom of the cylinder. side0 = 0; } @@ -2950,7 +3100,7 @@ int ON_TextureMapping::EvaluateCylinderMapping( if ( fabs(1.0-Q.z) > ON_SQRT_EPSILON || (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON ) { - // The ray's intersection with the top plane missed the + // The ray's intersection with the top plane missed the // radius 1 disk that is the top of the cylinder. side1 = 0; } @@ -2965,11 +3115,11 @@ int ON_TextureMapping::EvaluateCylinderMapping( } } } - + // shoot ray at the cylinder wall - int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y), - 2.0*(rst.x*n.x+rst.y*n.y), - (rst.x*rst.x+rst.y*rst.y) - 1.0, + int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y), + 2.0*(rst.x*n.x+rst.y*n.y), + (rst.x*rst.x+rst.y*rst.y) - 1.0, &t0, &t1 ); if (rc >= 0 ) { @@ -2986,9 +3136,9 @@ int ON_TextureMapping::EvaluateCylinderMapping( } else if ( 1 != BestHitHelper(t0,t) ) { - // The cylinder is capped and the ray hit the cap, - // hit the infinite cylinder wall, and the wall - // hit is "first". If the ray hits the finite + // The cylinder is capped and the ray hit the cap, + // hit the infinite cylinder wall, and the wall + // hit is "first". If the ray hits the finite // cylinder wall, the I will use the wall hit. t1 = rst.z + t0*n.z; if ( t1 >= -(1.0+ON_SQRT_EPSILON) && t1 <= 1.0+ON_SQRT_EPSILON ) @@ -3001,14 +3151,14 @@ int ON_TextureMapping::EvaluateCylinderMapping( } } } - + if ( side0 > 1 ) { // best hit is on a cap rst = rst + t*n; } } - + if ( m_bCapped && 0 == side0 ) { if ( fabs(rst.z) > 1.0+ON_SQRT_EPSILON ) @@ -3030,19 +3180,19 @@ int ON_TextureMapping::EvaluateCylinderMapping( } } } - + if ( 2 == side0 || 3 == side0 ) { - // The cylinder is capped and P maps to + // The cylinder is capped and P maps to // the top (1 == side0) or bottom (2 == side0) if ( 2 == side0 ) { // This is the same convention as box mapping. - // Put another way, if you change the mapping + // Put another way, if you change the mapping // between box and cylinder, you get the same // picture on the top and bottom. - rst.x = -rst.x; + rst.x = -rst.x; } if ( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space ) @@ -3059,7 +3209,7 @@ int ON_TextureMapping::EvaluateCylinderMapping( rst.y /= r; } - + // convert to normalized texture coordinates rst.x = 0.5*rst.x + 0.5; if ( rst.x < 0.0) rst.x = 0.0; else if (rst.x > 1.0) rst.x = 1.0; @@ -3071,7 +3221,7 @@ int ON_TextureMapping::EvaluateCylinderMapping( // bottom uses 4/6 <= x <= 5/6 region of the texture map. // top uses 5/6 <= x <= 1 region of the texture map. rst.x = (2.0 + side0 + rst.x)/6.0; - } + } } else { @@ -3103,14 +3253,14 @@ int ON_TextureMapping::EvaluateCylinderMapping( } side0 = 1; } - rst.z = r; - + rst.z = r; + *T = m_uvw*rst; return side0; } -int ON_TextureMapping::EvaluateBoxMapping( +int ON_TextureMapping::EvaluateBoxMapping( const ON_3dPoint& P, const ON_3dVector& N, ON_3dPoint* T @@ -3128,7 +3278,7 @@ int ON_TextureMapping::EvaluateBoxMapping( int side0, side1; double t0, t1; - + side0 = 0; t0 = 0.0; @@ -3139,10 +3289,10 @@ int ON_TextureMapping::EvaluateBoxMapping( // 4 = front side (y=+1) // 5 = bottom side (z=-1) // 6 = top side (z=+1) - + if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection ) { - + if ( m_bCapped ) { // intersect ray with top and bottom @@ -3167,7 +3317,7 @@ int ON_TextureMapping::EvaluateBoxMapping( // ray hit the box rst = rst + t0*n; } - } + } if ( 0 == side0 ) { @@ -3180,14 +3330,14 @@ int ON_TextureMapping::EvaluateBoxMapping( { side0 = 2*side1 + 1; } - else + else { side0 = 2*side1 + 2; } - + //if ( fabs(t1) <= 1.0+ON_SQRT_EPSILON )... //// The point is inside the box. If the normal - //// is not zero, then use it to choose the side + //// is not zero, then use it to choose the side //// used for the closest point projection. side1 = ( fabs(n.x) >= fabs(n.y) ) ? 0 : 1; @@ -3206,7 +3356,7 @@ int ON_TextureMapping::EvaluateBoxMapping( } double shift = 0.0; - + // side flag // 1 = left side (x=-1) // 2 = right side (x=+1) @@ -3217,27 +3367,27 @@ int ON_TextureMapping::EvaluateBoxMapping( switch(side0) { - case 1: // x = -1 - rst.x = -rst.y; - rst.y = rst.z; + case 1: // x = -1 + rst.x = -rst.y; + rst.y = rst.z; shift = 3.0; break; case 2: // x = +1 - rst.x = rst.y; - rst.y = rst.z; + rst.x = rst.y; + rst.y = rst.z; shift = 1.0; break; case 3: // y = -1 - rst.y = rst.z; + rst.y = rst.z; shift = 0.0; break; case 4: // y = +1 - rst.x = -rst.x; - rst.y = rst.z; + rst.x = -rst.x; + rst.y = rst.z; shift = 2.0; break; case 5: // z = -1 - rst.x = -rst.x; + rst.x = -rst.x; shift = 4.0; break; case 6: // z = +1 @@ -3249,14 +3399,14 @@ int ON_TextureMapping::EvaluateBoxMapping( rst.x = 0.5*rst.x + 0.5; rst.y = 0.5*rst.y + 0.5; rst.z = 0.0; - + if( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space) { rst.x = (shift + rst.x)/(m_bCapped ? 6.0 : 4.0); } *T = m_uvw*rst; - + return side0; } @@ -3325,7 +3475,7 @@ int ON_TextureMapping::Evaluate( default: rc = EvaluatePlaneMapping(P,N,T); break; - } + } return rc; } @@ -3407,8 +3557,8 @@ ON__UINT32 ON_TextureMapping::MappingCRC() const // Should brep's render meshes be included in the crc? // The texture that is being mapped is actually // being applied to the brep by the render mesh's - // m_T[] values and some users will want to see - // the "picture" on the brep mapped to the + // m_T[] values and some users will want to see + // the "picture" on the brep mapped to the // "picture" on the // target. } @@ -3445,12 +3595,12 @@ bool ON_TextureMapping::RequiresVertexNormals() const if ( ON_TextureMapping::TYPE::srfp_mapping == m_type ) return false; - if(m_projection == ON_TextureMapping::PROJECTION::ray_projection) + if(m_projection == ON_TextureMapping::PROJECTION::ray_projection) return true; - if(m_type == ON_TextureMapping::TYPE::box_mapping) + if(m_type == ON_TextureMapping::TYPE::box_mapping) return true; - if(m_type == ON_TextureMapping::TYPE::cylinder_mapping && m_bCapped) + if(m_type == ON_TextureMapping::TYPE::cylinder_mapping && m_bCapped) return true; return false; @@ -3461,7 +3611,7 @@ bool ON_TextureMapping::IsPeriodic(void) const return (m_type == ON_TextureMapping::TYPE::sphere_mapping || m_type == ON_TextureMapping::TYPE::cylinder_mapping); } -bool ON_TextureMapping::HasMatchingTextureCoordinates( +bool ON_TextureMapping::HasMatchingTextureCoordinates( const ON_Mesh& mesh, const ON_Xform* mesh_xform ) const @@ -3473,7 +3623,7 @@ bool ON_TextureMapping::HasMatchingTextureCoordinates( return rc; } -bool ON_TextureMapping::HasMatchingTextureCoordinates( +bool ON_TextureMapping::HasMatchingTextureCoordinates( const ON_MappingTag& tag, const ON_Xform* mesh_xform ) const @@ -3495,9 +3645,9 @@ bool ON_TextureMapping::HasMatchingTextureCoordinates( // alwasy independent of 3d location but // the transformations are often set. if ( ON_TextureMapping::TYPE::srfp_mapping != m_type - && mesh_xform + && mesh_xform && mesh_xform->IsValid() - && !mesh_xform->IsZero() + && !mesh_xform->IsZero() && !tag.m_mesh_xform.IsZero() ) { @@ -3550,26 +3700,26 @@ bool GetSPTCHelper( for ( i = 1; i < vcnt; i++ ) { u = S[i].x; - if (u < srf_udom.m_t[0]) srf_udom.m_t[0] = u; - else if (u > srf_udom.m_t[1]) srf_udom.m_t[1] = u; + if (u < srf_udom.m_t[0]) srf_udom.m_t[0] = u; + else if (u > srf_udom.m_t[1]) srf_udom.m_t[1] = u; v = S[i].y; - if (v < srf_vdom.m_t[0]) srf_vdom.m_t[0] = v; - else if (v > srf_vdom.m_t[1]) srf_vdom.m_t[1] = v; + if (v < srf_vdom.m_t[0]) srf_vdom.m_t[0] = v; + else if (v > srf_vdom.m_t[1]) srf_vdom.m_t[1] = v; } - if ( !srf_udom.IsIncreasing() + if ( !srf_udom.IsIncreasing() || !srf_vdom.IsIncreasing() ) { return false; } } - bool bHaveUVWXform = mapping.m_uvw.IsValid() - && !mapping.m_uvw.IsIdentity() + bool bHaveUVWXform = mapping.m_uvw.IsValid() + && !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero(); if ( mesh.HasPackedTextureRegion() ) { - // Packed textures are not compatible with the use + // Packed textures are not compatible with the use // of m_uvw. m_uvw is ignored in this block // of code on purpose. //SEE BELOW const ON_Interval tex_udom = mesh.m_packed_tex_domain[0]; @@ -3593,13 +3743,13 @@ bool GetSPTCHelper( } // (u, v) = known surface parameter - if ( mesh.m_packed_tex_rotate ) + if ( mesh.m_packed_tex_rotate ) { // verify this by checking with mesher a = 1.0 - srf_vdom.NormalizedParameterAt( v ); b = srf_udom.NormalizedParameterAt( u ); } - else + else { a = srf_udom.NormalizedParameterAt( u ); b = srf_vdom.NormalizedParameterAt( v ); @@ -3655,7 +3805,7 @@ bool GetSPTCHelper( bool ON_TextureMapping::GetTextureCoordinates( - const ON_Mesh& mesh, + const ON_Mesh& mesh, ON_SimpleArray& T, const ON_Xform* mesh_xform, bool bLazy, @@ -3758,15 +3908,15 @@ bool ON_TextureMapping::GetTextureCoordinates( double w; int sd; - if (ON_TextureMapping::PROJECTION::clspt_projection == m_projection + if (ON_TextureMapping::PROJECTION::clspt_projection == m_projection && (ON_TextureMapping::TYPE::mesh_mapping_primitive == m_type || ON_TextureMapping::TYPE::brep_mapping_primitive == m_type) && nullptr != m_mapping_primitive) { rc = false; } else if ( mesh_N && - ( ON_TextureMapping::PROJECTION::ray_projection == m_projection - || ON_TextureMapping::TYPE::box_mapping == m_type + ( ON_TextureMapping::PROJECTION::ray_projection == m_projection + || ON_TextureMapping::TYPE::box_mapping == m_type || ON_TextureMapping::TYPE::cylinder_mapping == m_type || ON_TextureMapping::TYPE::mesh_mapping_primitive == m_type ) @@ -3845,8 +3995,8 @@ bool ON_TextureMapping::GetTextureCoordinates( return rc; } -static -void ThreeToTwoHelper( +static +void ThreeToTwoHelper( const ON_SimpleArray& T3, ON_SimpleArray& T2 ) @@ -3867,8 +4017,8 @@ void ThreeToTwoHelper( } bool ON_TextureMapping::GetTextureCoordinates( - const ON_Mesh& mesh, - ON_SimpleArray& T, + const ON_Mesh& mesh, + ON_SimpleArray& T, const ON_Xform* mesh_xform, bool bLazy, ON_SimpleArray* Tside @@ -3938,8 +4088,8 @@ bool ON_TextureMapping::GetTextureCoordinates( } -//bool ON_Mesh::GetSurfaceParameterTextureXform( -// class ON_Xform& StoT +//bool ON_Mesh::GetSurfaceParameterTextureXform( +// class ON_Xform& StoT // ) const // //{ @@ -3952,7 +4102,7 @@ bool ON_TextureMapping::GetTextureCoordinates( // const ON_Interval texture_u_domain(m_tex_domain[0]); // const ON_Interval texture_v_domain(m_tex_domain[1]); // bool bRotateTexture = m_srf_tex_rotate; -// if ( surface_u_domain.IsInterval() +// if ( surface_u_domain.IsInterval() // && surface_v_domain.IsInterval() // && texture_u_domain.IsInterval() // && texture_v_domain.IsInterval() @@ -4012,11 +4162,11 @@ public: bool m_bHasPrincipalCurvatures; bool m_bHasHiddenVertices; - bool m_bHasCachedTextures; + bool m_bHasCachedTextures; ON_SimpleArray< ON_TextureCoordinates* > m_TC; - // m_vuse[] is an array of length = original number of - // vertices in m_mesh and m_vuse[vi] = number of faces + // m_vuse[] is an array of length = original number of + // vertices in m_mesh and m_vuse[vi] = number of faces // that reference vertex vi. If this vertex needs to be // split, vuse[vi] is decremented. The ultimate goal // is to split a few times as needed so we don't @@ -4057,10 +4207,10 @@ void ON__CChangeTextureCoordinateHelper::ChangeTextureCoordinate(int* Fvi, int f } -ON__CChangeTextureCoordinateHelper::ON__CChangeTextureCoordinateHelper( +ON__CChangeTextureCoordinateHelper::ON__CChangeTextureCoordinateHelper( ON_Mesh& mesh, int newvcnt, - float*& mesh_T ) + float*& mesh_T ) : m_mesh(mesh) , m_mesh_dV(0) , m_vuse_count(0) @@ -4093,7 +4243,7 @@ ON__CChangeTextureCoordinateHelper::ON__CChangeTextureCoordinateHelper( } m_bHasVertexNormals = m_mesh.HasVertexNormals(); - if ( m_bHasVertexNormals ) + if ( m_bHasVertexNormals ) m_mesh.m_N.Reserve(vcnt+newvcnt); m_bHasVertexTextures = m_mesh.HasTextureCoordinates(); @@ -4154,8 +4304,8 @@ int ON__CChangeTextureCoordinateHelper::DupVertex(int vi) { if ( 0 == m_vuse_count ) { - // m_vuse[] is an array of length = original number of - // vertices in m_mesh and m_vuse[vi] = number of faces + // m_vuse[] is an array of length = original number of + // vertices in m_mesh and m_vuse[vi] = number of faces // that reference vertex vi. If this vertex needs to be // split, vuse[vi] is decremented. The ultimate goal // is to split a few times as needed so we don't @@ -4343,7 +4493,7 @@ int IntersectBoxSideRayHelper(int side, const ON_3dPoint& rst, const ON_3dVector } static -bool EvBoxSideTextureCoordinateHelper2( +bool EvBoxSideTextureCoordinateHelper2( int side, const ON_TextureMapping& box_mapping, const ON_3dPoint& P, @@ -4375,7 +4525,7 @@ bool EvBoxSideTextureCoordinateHelper2( // 4 = front side (y=+1) // 5 = bottom side (z=-1) // 6 = top side (z=+1) - + if ( ON_TextureMapping::PROJECTION::ray_projection == box_mapping.m_projection ) { double s; @@ -4384,10 +4534,10 @@ bool EvBoxSideTextureCoordinateHelper2( // ray hit the box side rst = rst + s*n; } - } + } double shift = 0.0; - + // side flag // 1 = left side (x=-1) // 2 = right side (x=+1) @@ -4398,27 +4548,27 @@ bool EvBoxSideTextureCoordinateHelper2( switch(side) { - case 1: // x = -1 - rst.x = -rst.y; - rst.y = rst.z; + case 1: // x = -1 + rst.x = -rst.y; + rst.y = rst.z; shift = 3.0; break; case 2: // x = +1 - rst.x = rst.y; - rst.y = rst.z; + rst.x = rst.y; + rst.y = rst.z; shift = 1.0; break; case 3: // y = -1 - rst.y = rst.z; + rst.y = rst.z; shift = 0.0; break; case 4: // y = +1 - rst.x = -rst.x; - rst.y = rst.z; + rst.x = -rst.x; + rst.y = rst.z; shift = 2.0; break; case 5: // z = -1 - rst.x = -rst.x; + rst.x = -rst.x; shift = 4.0; break; case 6: // z = +1 @@ -4433,20 +4583,20 @@ bool EvBoxSideTextureCoordinateHelper2( rst.x = 0.5*rst.x + 0.5; rst.y = 0.5*rst.y + 0.5; rst.z = 0.0; - + if( ON_TextureMapping::TEXTURE_SPACE::divided == box_mapping.m_texture_space) { rst.x = (shift + rst.x)/(box_mapping.m_bCapped ? 6.0 : 4.0); } *T = box_mapping.m_uvw*rst; - + return true; } static bool EvBoxSideTextureCoordinateHelper1( - const ON_Mesh& mesh, + const ON_Mesh& mesh, const ON_Xform* mesh_xform, int vi, int side, @@ -4560,17 +4710,17 @@ static float TcDistanceHelper(const ON_2fPoint& tc) { float dx = (tc.x > 0.5f) ? (1.0f-tc.x) : tc.x; - if ( dx < 0.0f) + if ( dx < 0.0f) return 0.0f; float dy = (tc.y > 0.5f) ? (1.0f-tc.y) : tc.y; - if ( dy < 0.0f) + if ( dy < 0.0f) return 0.0f; return (dx < dy) ? dx : dy; } static -void AdjustSingleBoxTextureCoordinatesHelper( - ON_Mesh& mesh, +void AdjustSingleBoxTextureCoordinatesHelper( + ON_Mesh& mesh, const ON_Xform* mesh_xform, float* mesh_T, int mesh_T_stride, @@ -4664,7 +4814,7 @@ void AdjustSingleBoxTextureCoordinatesHelper( return; ON__CChangeTextureCoordinateHelper helper(mesh,vcnt+newvcnt,mesh_T); - + const int mflist_count = mflist.Count(); for ( k = 0; k < mflist_count; k++ ) @@ -4678,13 +4828,13 @@ void AdjustSingleBoxTextureCoordinatesHelper( { helper.ChangeTextureCoordinate(fvi,j,mf.tc[j].x,mf.tc[j].y,mesh_T,mesh_T_stride); } - } + } } } -static -void AdjustMeshPeriodicTextureCoordinatesHelper( - ON_Mesh& mesh, +static +void AdjustMeshPeriodicTextureCoordinatesHelper( + ON_Mesh& mesh, const ON_Xform* mesh_xform, float* mesh_T, int mesh_T_stride, @@ -4759,7 +4909,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper( t = Tx[vi]; // t = "u" texture coordinate if ( t < ang0 ) - { + { quad[vi] = 1; q |= 1; // longitude < pi/2 ftc_count++; } @@ -4804,7 +4954,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper( } // ftc.fi will be set to fi if a texture coordinate needs to be adjusted - ftc.fi = -1; + ftc.fi = -1; ftc.Tx[0] = Tx[Fvi[0]]; ftc.Tx[1] = Tx[Fvi[1]]; @@ -4814,7 +4964,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper( if ( 0 != (8&q) ) { // see if check for north/south sphere mapping poles and fix them - if ( 8 == ftc.quad[0] ) + if ( 8 == ftc.quad[0] ) { t0 = (8 == ftc.quad[3]) ? ON_UNSET_FLOAT : ftc.Tx[3]; t1 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1]; @@ -4826,7 +4976,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper( ftc.fi = fi; } } - if ( 8 == ftc.quad[1] ) + if ( 8 == ftc.quad[1] ) { t0 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0]; t1 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2]; @@ -4838,7 +4988,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper( ftc.fi = fi; } } - if ( 8 == ftc.quad[2] ) + if ( 8 == ftc.quad[2] ) { int k = (Fvi[2] == Fvi[3]) ? 0 : 3; t0 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1]; @@ -4856,7 +5006,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper( ftc.fi = fi; } } - if ( 8 == ftc.quad[3] && Fvi[2] != Fvi[3] ) + if ( 8 == ftc.quad[3] && Fvi[2] != Fvi[3] ) { t0 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2]; t1 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0]; @@ -4900,7 +5050,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper( } if (f0 >= f1 ) { - // "most" of the face is on the left side of the texture + // "most" of the face is on the left side of the texture // If a vertex is on the right side, clamp its tc to 0. if ( 4 == ftc.quad[0] ) {ftc.Tx[0] = 0.0f; ftc.fi = fi;} if ( 4 == ftc.quad[1] ) {ftc.Tx[1] = 0.0f; ftc.fi = fi;} @@ -4909,7 +5059,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper( } else { - // "most" of the face is on the right side of the texture + // "most" of the face is on the right side of the texture // If a vertex is on the left side, clamp its tc to two_pi_tc. if ( 1 == ftc.quad[0] ) {ftc.Tx[0] = twopitc; ftc.fi = fi;} if ( 1 == ftc.quad[1] ) {ftc.Tx[1] = twopitc; ftc.fi = fi;} @@ -4922,7 +5072,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper( if ( ftc.fi >= 0 ) { // face will require special handling - ftc_list.Append(ftc); + ftc_list.Append(ftc); } } @@ -4998,8 +5148,8 @@ void AdjustMeshPeriodicTextureCoordinatesHelper( } static -bool SeamCheckHelper( const ON_TextureMapping& mp, - double& two_pi_tc, +bool SeamCheckHelper( const ON_TextureMapping& mp, + double& two_pi_tc, ON_SimpleArray& Tside, ON_SimpleArray*& Tsd ) { @@ -5049,7 +5199,7 @@ static inline bool HasSharedVertices(const ON_Mesh& mesh) } -const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates( +const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates( const class ON_TextureMapping& mapping, const class ON_Xform* mesh_xform, bool bLazy @@ -5097,7 +5247,7 @@ const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates( TC->m_tag.Set(mapping); if ( mesh_xform && mesh_xform->IsValid() - && !mesh_xform->IsIdentity() + && !mesh_xform->IsIdentity() && !mesh_xform->IsZero() ) { @@ -5134,7 +5284,7 @@ const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates( } bool ON_Mesh::SetTextureCoordinates( - const class ON_TextureMapping& mapping, + const class ON_TextureMapping& mapping, const class ON_Xform* mesh_xform, bool bLazy ) @@ -5147,7 +5297,7 @@ bool ON_Mesh::SetTextureCoordinates( ON_SimpleArray Tside; ON_SimpleArray* Tsd = 0; ON_TextureMapping mp = mapping; - + double two_pi_tc = 1.0; bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this); @@ -5165,15 +5315,15 @@ bool ON_Mesh::SetTextureCoordinates( // were already once seam checked. So seam check can be skipped now. bSeamCheck = false; } - + if (rc) { // update the texture coordinate tag m_Ttag.Set(mapping); - if ( mesh_xform - && mesh_xform->IsValid() - && !mesh_xform->IsIdentity() - && !mesh_xform->IsZero() + if ( mesh_xform + && mesh_xform->IsValid() + && !mesh_xform->IsIdentity() + && !mesh_xform->IsZero() ) { m_Ttag.m_mesh_xform = *mesh_xform; @@ -5264,8 +5414,8 @@ bool ON_MappingChannel::Read( ON_BinaryArchive& archive ) { // 1.1 field added 6 June 2006 if (rc) rc = archive.ReadXform(m_object_xform); - if (rc - && archive.ArchiveOpenNURBSVersion() < 200610030 + if (rc + && archive.ArchiveOpenNURBSVersion() < 200610030 && m_object_xform.IsZero() ) { @@ -5357,7 +5507,7 @@ bool ON_MaterialRef::Write( ON_BinaryArchive& archive ) const //if (rc) rc = archive.WriteArray( m_mapping_channels ); if (rc) rc = archive.WriteInt(0); - // 23 May 2006 added + // 23 May 2006 added if (rc) rc = archive.WriteUuid( m_material_backface_id ); if (rc) rc = archive.WriteInt( m_material_source ); @@ -5421,7 +5571,7 @@ bool ON_MappingRef::Write( ON_BinaryArchive& archive ) const { if (rc) rc = archive.WriteUuid( m_plugin_id ); if (rc) rc = archive.WriteArray( m_mapping_channels ); - + if ( !archive.EndWrite3dmChunk() ) rc = false; } @@ -5491,7 +5641,7 @@ void ON_ObjectRenderingAttributes::EnableAdvancedTexturePreview(bool b) { if ( b ) m_bits |= 1; // set bit 1 - else + else m_bits &= 0xFE; // clear bit 1 } @@ -5523,7 +5673,7 @@ bool ON_RenderingAttributes::IsValid( ON_TextLog* text_log ) const return false; } } - } + } } return true; } @@ -5554,7 +5704,7 @@ bool ON_ObjectRenderingAttributes::IsValid( ON_TextLog* text_log ) const return false; } } - } + } } return true; @@ -5638,12 +5788,12 @@ const ON_MappingRef* ON_ObjectRenderingAttributes::MappingRef( { if ( plugin_id == mr->m_plugin_id ) return mr; - } + } } //ALB 2013.12.03 //Fixes http://mcneel.myjetbrains.com/youtrack/issue/RH-5730 - //I'm sick of this bug being considered irrelavent, and since I've decided to go out of my way to + //I'm sick of this bug being considered irrelavent, and since I've decided to go out of my way to //Sort out as many mapping problems as I can, I'm fixing this one like this. if (m_mappings.Count() > 0) { @@ -5653,8 +5803,8 @@ const ON_MappingRef* ON_ObjectRenderingAttributes::MappingRef( return 0; } -ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef( - const ON_UUID& plugin_id +ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef( + const ON_UUID& plugin_id ) { ON_MappingRef* mr = 0; @@ -5665,7 +5815,7 @@ ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef( { if ( plugin_id == mr->m_plugin_id ) break; - } + } } if ( !mr ) @@ -5677,18 +5827,18 @@ ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef( return mr; } -bool ON_ObjectRenderingAttributes::DeleteMappingRef( - const ON_UUID& plugin_id +bool ON_ObjectRenderingAttributes::DeleteMappingRef( + const ON_UUID& plugin_id ) { const ON_MappingRef* mr = MappingRef(plugin_id); - if ( mr ) + if ( mr ) m_mappings.Remove( (int)(mr - m_mappings.Array()) ); // safe ptr to in conversion - return (0 != mr); + return (0 != mr); } -const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel( - const ON_UUID& plugin_id, +const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel( + const ON_UUID& plugin_id, const ON_UUID& mapping_id ) const { @@ -5708,8 +5858,8 @@ const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel( return 0; } -const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel( - const ON_UUID& plugin_id, +const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel( + const ON_UUID& plugin_id, int mapping_channel_id ) const { @@ -5732,7 +5882,7 @@ const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel( bool ON_ObjectRenderingAttributes::AddMappingChannel( - const ON_UUID& plugin_id, + const ON_UUID& plugin_id, int mapping_channel_id, const ON_UUID& mapping_id ) @@ -5754,7 +5904,7 @@ bool ON_ObjectRenderingAttributes::AddMappingChannel( } bool ON_ObjectRenderingAttributes::DeleteMappingChannel( - const ON_UUID& plugin_id, + const ON_UUID& plugin_id, int mapping_channel_id ) { @@ -5763,7 +5913,7 @@ bool ON_ObjectRenderingAttributes::DeleteMappingChannel( } bool ON_ObjectRenderingAttributes::DeleteMappingChannel( - const ON_UUID& plugin_id, + const ON_UUID& plugin_id, const ON_UUID& mapping_id ) { @@ -5772,7 +5922,7 @@ bool ON_ObjectRenderingAttributes::DeleteMappingChannel( } bool ON_ObjectRenderingAttributes::ChangeMappingChannel( - const ON_UUID& plugin_id, + const ON_UUID& plugin_id, int old_mapping_channel_id, int new_mapping_channel_id ) @@ -5781,7 +5931,7 @@ bool ON_ObjectRenderingAttributes::ChangeMappingChannel( return mr ? mr->ChangeMappingChannel(old_mapping_channel_id,new_mapping_channel_id) : false; } -const ON_MappingChannel* ON_MappingRef::MappingChannel( +const ON_MappingChannel* ON_MappingRef::MappingChannel( const ON_UUID& mapping_id ) const { @@ -5797,7 +5947,7 @@ const ON_MappingChannel* ON_MappingRef::MappingChannel( return 0; } -const ON_MappingChannel* ON_MappingRef::MappingChannel( +const ON_MappingChannel* ON_MappingRef::MappingChannel( int mapping_channel_id ) const { @@ -5899,7 +6049,7 @@ bool ON_RenderingAttributes::Read( ON_BinaryArchive& archive ) int major_version = 0; int minor_version = 0; bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); - if (!rc) + if (!rc) return false; for(;;) { @@ -5922,7 +6072,7 @@ bool ON_ObjectRenderingAttributes::Write( ON_BinaryArchive& archive ) const return false; for(;;) { - // DO NOT CALL ON_RenderingAttributes::Write + // DO NOT CALL ON_RenderingAttributes::Write rc = archive.WriteArray(m_materials); if ( !rc ) break; rc = archive.WriteArray(m_mappings); @@ -5952,14 +6102,14 @@ bool ON_ObjectRenderingAttributes::Read( ON_BinaryArchive& archive ) int major_version = 0; int minor_version = 0; bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version ); - if (!rc) + if (!rc) return false; for(;;) { rc = ( 1 == major_version && minor_version >= 1 ); if (!rc) break; - // DO NOT CALL ON_RenderingAttributes::Read + // DO NOT CALL ON_RenderingAttributes::Read if (rc) rc = archive.ReadArray(m_materials); if (!rc) break; if (rc) rc = archive.ReadArray(m_mappings); @@ -6035,9 +6185,9 @@ bool ON_TextureMapping::SetPlaneMapping( ON_3dVector yaxis = plane.yaxis; ON_3dVector zaxis = plane.zaxis; - // Any "cleanup" needs to be done here + // Any "cleanup" needs to be done here // to xaxis, yaxis, zaxis. - + double sx,sy,sz; if ( 0.0 == (sx = dx.Length())) sx = 2.0; if ( 0.0 == (sy = dy.Length())) sy = 2.0; @@ -6045,9 +6195,9 @@ bool ON_TextureMapping::SetPlaneMapping( // The plane mapping matrix m_Pxyz transforms the // world coordinate rectangle to a (-1<=r<=1, - // on plane to a + // on plane to a // 1 X 1 square in the xy plane centered at the - // origin. + // origin. // m_Pxyz = surface point transformation ON_3dVector X = (2.0/sx)*xaxis; @@ -6139,7 +6289,7 @@ bool ON_TextureMapping::SetBoxMapping(const ON_Plane& plane, ON_Interval dx, ON_Interval dy, ON_Interval dz, - bool bCapped + bool bCapped ) { bool rc = SetPlaneMapping(plane,dx,dy,dz); @@ -6221,10 +6371,10 @@ bool ON_TextureMapping::GetMappingPlane(ON_Plane& plane, S.z = 1.0/S.z; xform.m_xform[0][0] *= S.x; xform.m_xform[0][1] *= S.x; xform.m_xform[0][2] *= S.x; - xform.m_xform[0][3] *= S.x; - + xform.m_xform[0][3] *= S.x; + xform.m_xform[1][0] *= S.y; xform.m_xform[1][1] *= S.y; xform.m_xform[1][2] *= S.y; - xform.m_xform[1][3] *= S.y; + xform.m_xform[1][3] *= S.y; xform.m_xform[2][0] *= S.z; xform.m_xform[2][1] *= S.z; xform.m_xform[2][2] *= S.z; xform.m_xform[2][3] *= S.z; @@ -6239,7 +6389,7 @@ bool ON_TextureMapping::GetMappingPlane(ON_Plane& plane, return false; plane.origin.Set(inv.m_xform[0][3],inv.m_xform[1][3],inv.m_xform[2][3]); - xform.m_xform[0][3] = 0.0; + xform.m_xform[0][3] = 0.0; xform.m_xform[1][3] = 0.0; xform.m_xform[2][3] = 0.0; plane.xaxis = &xform.m_xform[0][0]; @@ -6297,6 +6447,616 @@ bool ON_TextureMapping::GetMappingSphere(ON_Sphere& sphere) const + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_PBRMaterial +// + +#define SUPPORT_PBR_USERDATA_SERIALIZATION + +class ON_PhysicallyBasedMaterialUserData : public ON_UserData +{ +private: + ON_OBJECT_DECLARE(ON_PhysicallyBasedMaterialUserData); + + + bool GetDescription(ON_wString & description) + { + description = L"ON_PhysicallyBasedMaterialUserData"; + return true; + } + +public: + ON_PhysicallyBasedMaterialUserData() + { + m_userdata_uuid = ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData); + m_application_uuid = ON_opennurbs6_id; + m_userdata_copycount = 1; + } + + ~ON_PhysicallyBasedMaterialUserData() {} + + + ON_PhysicallyBasedMaterialUserData(const ON_PhysicallyBasedMaterialUserData& src) + : ON_UserData(src) + { + m_userdata_copycount = src.m_userdata_copycount; + m_parameters = src.m_parameters; + } + + ON_PhysicallyBasedMaterialUserData& operator=(const ON_PhysicallyBasedMaterialUserData& src) + { + if (this != &src) + { + ON_UserData::operator = (src); + m_parameters = src.m_parameters; + } + return *this; + } + +private: + // ON_Object overrides +#if defined SUPPORT_PBR_USERDATA_SERIALIZATION + bool Write(ON_BinaryArchive& archive) const override + { + ON_ASSERT(IsValid()); + + const int chunk_version = 1; + if (false == archive.BeginWrite3dmAnonymousChunk(chunk_version)) + return false; + + bool rc = m_parameters.Write(archive); + + if (!archive.EndWrite3dmChunk()) + { + rc = false; + } + + return rc; + } + + bool Read(ON_BinaryArchive& archive) override + { + int chunk_version = 0; + if (false == archive.BeginRead3dmAnonymousChunk(&chunk_version)) + return false; + + bool rc = false; + + if (chunk_version == 1) + { + rc = m_parameters.Read(archive); + } + + if (!archive.EndRead3dmChunk()) + { + rc = false; + } + + ON_ASSERT(IsValid()); + return rc; + } + + bool Archive() const override + { + return true; + } +#else + bool Archive() const override + { + return false; + } +#endif + + bool IsValid(class ON_TextLog* text_log = nullptr) const override + { + if (!m_parameters.IsValid(text_log)) + return false; + + return ON_UserData::IsValid(text_log); + } + +public: + struct Parameters + { +#if defined SUPPORT_PBR_USERDATA_SERIALIZATION + bool Write(ON_BinaryArchive& binary_archive) const + { + if (!binary_archive.WriteColor(base_color)) return false; + if (!binary_archive.WriteInt((int)brdf)) return false; + if (!binary_archive.WriteDouble(subsurface)) return false; + if (!binary_archive.WriteColor(subsurface_scattering_color)) return false; + if (!binary_archive.WriteDouble(subsurface_scattering_radius)) return false; + if (!binary_archive.WriteDouble(metallic)) return false; + if (!binary_archive.WriteDouble(specular)) return false; + if (!binary_archive.WriteDouble(specular_tint)) return false; + if (!binary_archive.WriteDouble(roughness)) return false; + if (!binary_archive.WriteDouble(anisotropic)) return false; + if (!binary_archive.WriteDouble(anisotropic_rotation)) return false; + if (!binary_archive.WriteDouble(sheen)) return false; + if (!binary_archive.WriteDouble(sheen_tint)) return false; + if (!binary_archive.WriteDouble(clearcoat)) return false; + if (!binary_archive.WriteDouble(clearcoat_roughness)) return false; + if (!binary_archive.WriteDouble(opacity_IOR)) return false; + if (!binary_archive.WriteDouble(opacity)) return false; + if (!binary_archive.WriteDouble(opacity_roughness)) return false; + if (!binary_archive.WriteColor(emission)) return false; + + return true; + } + + bool Read(ON_BinaryArchive& binary_archive) + { + if (!binary_archive.ReadColor(base_color)) return false; + if (!binary_archive.ReadInt((int*)&brdf)) return false; + if (!binary_archive.ReadDouble(&subsurface)) return false; + if (!binary_archive.ReadColor(subsurface_scattering_color)) return false; + if (!binary_archive.ReadDouble(&subsurface_scattering_radius)) return false; + if (!binary_archive.ReadDouble(&metallic)) return false; + if (!binary_archive.ReadDouble(&specular)) return false; + if (!binary_archive.ReadDouble(&specular_tint)) return false; + if (!binary_archive.ReadDouble(&roughness)) return false; + if (!binary_archive.ReadDouble(&anisotropic)) return false; + if (!binary_archive.ReadDouble(&anisotropic_rotation)) return false; + if (!binary_archive.ReadDouble(&sheen)) return false; + if (!binary_archive.ReadDouble(&sheen_tint)) return false; + if (!binary_archive.ReadDouble(&clearcoat)) return false; + if (!binary_archive.ReadDouble(&clearcoat_roughness)) return false; + if (!binary_archive.ReadDouble(&opacity_IOR)) return false; + if (!binary_archive.ReadDouble(&opacity)) return false; + if (!binary_archive.ReadDouble(&opacity_roughness)) return false; + if (!binary_archive.ReadColor(emission)) return false; + + return true; + } +#endif + + bool IsValid(class ON_TextLog* text_log = nullptr) const + { + //!base_color.IsValid() means that PBR is not supported. + //if (!base_color.IsValid()) return false; + if (ON_UNSET_VALUE == subsurface) return false; + if (!subsurface_scattering_color.IsValid()) return false; + if (ON_IS_UNSET_DOUBLE(subsurface_scattering_radius)) return false; + if (ON_IS_UNSET_DOUBLE(metallic)) return false; + if (ON_IS_UNSET_DOUBLE(specular)) return false; + if (ON_IS_UNSET_DOUBLE(specular_tint)) return false; + if (ON_IS_UNSET_DOUBLE(roughness)) return false; + if (ON_IS_UNSET_DOUBLE(anisotropic)) return false; + if (ON_IS_UNSET_DOUBLE(anisotropic_rotation)) return false; + if (ON_IS_UNSET_DOUBLE(sheen)) return false; + if (ON_IS_UNSET_DOUBLE(sheen_tint)) return false; + if (ON_IS_UNSET_DOUBLE(clearcoat)) return false; + if (ON_IS_UNSET_DOUBLE(clearcoat_roughness)) return false; + if (ON_IS_UNSET_DOUBLE(opacity_IOR)) return false; + if (ON_IS_UNSET_DOUBLE(opacity)) return false; + if (ON_IS_UNSET_DOUBLE(opacity_roughness)) return false; + if (!emission.IsValid()) return false; + + return true; + } + + ON_4fColor base_color = ON_4fColor::Unset; + double subsurface = 0.0; + ON_4fColor subsurface_scattering_color = ON_Color::White; + double subsurface_scattering_radius = 0.0; + double metallic = 0.0; + double specular = 0.5; + double specular_tint = 0.0; + double roughness = 1.0; + double anisotropic = 0.0; + double anisotropic_rotation = 0.0; + double sheen = 0.0; + double sheen_tint = 0.0; + double clearcoat = 0.0; + double clearcoat_roughness = 0.0; + double opacity_IOR = 1.52; + double opacity = 1.0; + double opacity_roughness = 0.0; + ON_4fColor emission = ON_Color::Black; + ON_PhysicallyBasedMaterial::BRDFs brdf = ON_PhysicallyBasedMaterial::BRDFs::GGX; + } m_parameters; +}; + +ON_UUID ON_Material::PhysicallyBasedUserdataId(void) +{ + // {5694E1AC-40E6-44F4-9CA9-3B6D0E8C4440} + static const ON_UUID id = { 0x5694e1ac, 0x40e6, 0x44f4,{ 0x9c, 0xa9, 0x3b, 0x6d, 0xe, 0x8c, 0x44, 0x40 } }; + + return id; +} + +ON_OBJECT_IMPLEMENT(ON_PhysicallyBasedMaterialUserData, ON_UserData, "5694E1AC-40E6-44F4-9CA9-3B6D0E8C4440"); + +class ON_PhysicallyBasedMaterial::Impl +{ +public: + Impl(ON_Material& mat) + : material(&mat), + m_pDummy(nullptr) + { + } + + ~Impl() + { + delete m_pDummy; + } + + const ON_PhysicallyBasedMaterialUserData& UserData() const + { + const auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData)); + if (pUD) + { + return static_cast(*pUD); + } + + if (m_pDummy) + { + return *m_pDummy; + } + + //This is the const version + m_pDummy = new ON_PhysicallyBasedMaterialUserData(); + return *m_pDummy; + } + + + ON_PhysicallyBasedMaterialUserData& UserData() + { + auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData)); + if (pUD) + { + return static_cast(*pUD); + } + + ON_PhysicallyBasedMaterialUserData* p = nullptr; + + if (nullptr == m_pDummy) + { + p = new ON_PhysicallyBasedMaterialUserData(); + } + else + { + p = m_pDummy; + m_pDummy = nullptr; + } + + material->AttachUserData(p); + + return *p; + } + + ON_Material* material; + + mutable ON_PhysicallyBasedMaterialUserData* m_pDummy; +}; + + + + + +ON_4fColor ON_PhysicallyBasedMaterial::BaseColor(void) const +{ + return Implementation().UserData().m_parameters.base_color; +} + +void ON_PhysicallyBasedMaterial::SetBaseColor(const ON_4fColor& c) +{ + Implementation().UserData().m_parameters.base_color = c; +} + +ON_PhysicallyBasedMaterial::BRDFs ON_PhysicallyBasedMaterial::BRDF(void) const +{ + return Implementation().UserData().m_parameters.brdf; +} + +void ON_PhysicallyBasedMaterial::SetBRDF(const BRDFs& b) +{ + Implementation().UserData().m_parameters.brdf = b; +} + +double ON_PhysicallyBasedMaterial::Subsurface(void) const +{ + return Implementation().UserData().m_parameters.subsurface; +} + +void ON_PhysicallyBasedMaterial::SetSubsurface(double d) +{ + Implementation().UserData().m_parameters.subsurface = d; +} + +ON_4fColor ON_PhysicallyBasedMaterial::SubsurfaceScatteringColor(void) const +{ + return Implementation().UserData().m_parameters.subsurface_scattering_color; +} + +void ON_PhysicallyBasedMaterial::SetSubsurfaceScatteringColor(const ON_4fColor& c) +{ + Implementation().UserData().m_parameters.subsurface_scattering_color = c; +} + +double ON_PhysicallyBasedMaterial::SubsurfaceScatteringRadius(void) const +{ + return Implementation().UserData().m_parameters.subsurface_scattering_radius; +} + +void ON_PhysicallyBasedMaterial::SetSubsurfaceScatteringRadius(double d) +{ + Implementation().UserData().m_parameters.subsurface_scattering_radius = d; +} + +double ON_PhysicallyBasedMaterial::Metallic(void) const +{ + return Implementation().UserData().m_parameters.metallic; +} + +void ON_PhysicallyBasedMaterial::SetMetallic(double d) +{ + Implementation().UserData().m_parameters.metallic = d; +} + +double ON_PhysicallyBasedMaterial::Specular(void) const +{ + return Implementation().UserData().m_parameters.specular; +} + +double ON_PhysicallyBasedMaterial::ReflectiveIOR(void) const +{ + const double d2 = Specular() * 0.08; + return sqrt(d2); +} + +void ON_PhysicallyBasedMaterial::SetReflectiveIOR(double ior) +{ + const double d = (ior - 1.0) / (ior + 1.0); + SetSpecular(d*d / 0.08); +} + +void ON_PhysicallyBasedMaterial::SetSpecular(double d) +{ + Implementation().UserData().m_parameters.specular = d; +} + +double ON_PhysicallyBasedMaterial::SpecularTint(void) const +{ + return Implementation().UserData().m_parameters.specular_tint; +} + +void ON_PhysicallyBasedMaterial::SetSpecularTint(double d) +{ + Implementation().UserData().m_parameters.specular_tint = d; +} + +double ON_PhysicallyBasedMaterial::Roughness(void) const +{ + return Implementation().UserData().m_parameters.roughness; +} + +void ON_PhysicallyBasedMaterial::SetRoughness(double d) +{ + Implementation().UserData().m_parameters.roughness = d; +} + +double ON_PhysicallyBasedMaterial::Anisotropic(void) const +{ + return Implementation().UserData().m_parameters.anisotropic; +} + +void ON_PhysicallyBasedMaterial::SetAnisotropic(double d) +{ + Implementation().UserData().m_parameters.anisotropic = d; +} + +double ON_PhysicallyBasedMaterial::AnisotropicRotation(void) const +{ + return Implementation().UserData().m_parameters.anisotropic_rotation; +} + +void ON_PhysicallyBasedMaterial::SetAnisotropicRotation(double d) +{ + Implementation().UserData().m_parameters.anisotropic_rotation = d; +} + +double ON_PhysicallyBasedMaterial::Sheen(void) const +{ + return Implementation().UserData().m_parameters.sheen; +} + +void ON_PhysicallyBasedMaterial::SetSheen(double d) +{ + Implementation().UserData().m_parameters.sheen = d; +} + +double ON_PhysicallyBasedMaterial::SheenTint(void) const +{ + return Implementation().UserData().m_parameters.sheen_tint; +} + +void ON_PhysicallyBasedMaterial::SetSheenTint(double d) +{ + Implementation().UserData().m_parameters.sheen_tint = d; +} + +double ON_PhysicallyBasedMaterial::Clearcoat(void) const +{ + return Implementation().UserData().m_parameters.clearcoat; +} + +void ON_PhysicallyBasedMaterial::SetClearcoat(double d) +{ + Implementation().UserData().m_parameters.clearcoat = d; +} + +double ON_PhysicallyBasedMaterial::ClearcoatRoughness(void) const +{ + return Implementation().UserData().m_parameters.clearcoat_roughness; +} + +void ON_PhysicallyBasedMaterial::SetClearcoatRoughness(double d) +{ + Implementation().UserData().m_parameters.clearcoat_roughness = d; +} + +double ON_PhysicallyBasedMaterial::OpacityIOR(void) const +{ + return Implementation().UserData().m_parameters.opacity_IOR; +} + +void ON_PhysicallyBasedMaterial::SetOpacityIOR(double d) +{ + Implementation().UserData().m_parameters.opacity_IOR = d; +} + +double ON_PhysicallyBasedMaterial::Opacity(void) const +{ + return Implementation().UserData().m_parameters.opacity; +} + +void ON_PhysicallyBasedMaterial::SetOpacity(double d) +{ + Implementation().UserData().m_parameters.opacity = d; +} + +double ON_PhysicallyBasedMaterial::OpacityRoughness(void) const +{ + return Implementation().UserData().m_parameters.opacity_roughness; +} + +void ON_PhysicallyBasedMaterial::SetOpacityRoughness(double d) +{ + Implementation().UserData().m_parameters.opacity_roughness = d; +} + +bool ON_PhysicallyBasedMaterial::IsValid(class ON_TextLog* text_log) const +{ + return Implementation().UserData().m_parameters.IsValid(text_log); +} + +ON_4fColor ON_PhysicallyBasedMaterial::Emission(void) const +{ + return Implementation().UserData().m_parameters.emission; +} + +void ON_PhysicallyBasedMaterial::SetEmission(ON_4fColor d) +{ + Implementation().UserData().m_parameters.emission = d; +} + +ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_Material& src) + : _pImpl(new Impl(const_cast(src))) +{ +} + +ON_PhysicallyBasedMaterial::~ON_PhysicallyBasedMaterial() +{ + delete _pImpl; +} + + +ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_PhysicallyBasedMaterial& src) + : _pImpl(new Impl(*src._pImpl->material)) +{ +} + +ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void) +{ + return ON_PhysicallyBasedMaterial(*this); +} + +const ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void) const +{ + return ON_PhysicallyBasedMaterial(*this); +} + + +const ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(void) const +{ + return *_pImpl; +} + +ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(void) +{ + return *_pImpl; +} + + +bool ON_PhysicallyBasedMaterial::Supported(void) const +{ + return BaseColor().IsValid(); +} + +void ON_PhysicallyBasedMaterial::SynchronizeLegacyMaterial(void) +{ + auto& mat = *Implementation().material; + + const bool bIsMetal = Metallic() > 0.5; + + mat.SetDiffuse(bIsMetal ? ON_Color::Black : (ON_Color)BaseColor()); + + const double reflectivity = bIsMetal ? 1.0 : 1.0 - Roughness(); + + mat.SetFresnelReflections(!bIsMetal); + + mat.SetReflectivity(reflectivity); + mat.SetTransparency(1.0 - Opacity()); + mat.SetAmbient(ON_Color::Black); + + //Gloss + mat.SetShine(ON_Material::MaxShine * reflectivity); + + if (bIsMetal) + { + mat.SetSpecular(BaseColor()); + mat.m_reflection = BaseColor(); + } + else + { + const int bf = (int)(255.0 * reflectivity); + mat.SetSpecular(ON_Color(bf, bf, bf)); + mat.m_reflection = ON_Color::White; + } + + mat.m_reflection_glossiness = Roughness(); + mat.m_refraction_glossiness = OpacityRoughness(); + + mat.SetEmission(Emission()); + + mat.m_index_of_refraction = OpacityIOR(); + + //No need to do the textures, because the ones that are supported are in the right channels already. +} + +int ON_PhysicallyBasedMaterial::FindTexture(const wchar_t* filename, ON_Texture::TYPE type, int i0) const +{ + return Material().FindTexture(filename, type, i0); +} + +int ON_PhysicallyBasedMaterial::AddTexture(const ON_Texture& tx) +{ + return Material().AddTexture(tx); +} + +int ON_PhysicallyBasedMaterial::AddTexture(const wchar_t* filename, ON_Texture::TYPE type) +{ + return Material().AddTexture(filename, type); +} + +int ON_PhysicallyBasedMaterial::DeleteTexture(const wchar_t* filename, ON_Texture::TYPE type) +{ + return Material().DeleteTexture(filename, type); +} + +ON_Material& ON_PhysicallyBasedMaterial::Material(void) +{ + return *Implementation().material; +} + +const ON_Material& ON_PhysicallyBasedMaterial::Material(void) const +{ + return *Implementation().material; +} + ON_wString ON_PhysicallyBasedMaterial::ParametersNames::BaseColor(void) { return L"pbr-base-color"; } ON_wString ON_PhysicallyBasedMaterial::ParametersNames::BRDF(void) { return L"pbr-brdf"; } ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Subsurface(void) { return L"pbr-subsurface"; } @@ -6312,13 +7072,8 @@ ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Sheen(void) { return L"p ON_wString ON_PhysicallyBasedMaterial::ParametersNames::SheenTint(void) { return L"pbr-sheen-tint"; } ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Clearcoat(void) { return L"pbr-clearcoat"; } ON_wString ON_PhysicallyBasedMaterial::ParametersNames::ClearcoatRoughness(void) { return L"pbr-clearcoat-roughness"; } +ON_wString ON_PhysicallyBasedMaterial::ParametersNames::ClearcoatBump(void) { return L"pbr-clearcoat-bump"; } ON_wString ON_PhysicallyBasedMaterial::ParametersNames::OpacityIor(void) { return L"pbr-opacity-ior"; } ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Opacity(void) { return L"pbr-opacity"; } ON_wString ON_PhysicallyBasedMaterial::ParametersNames::OpacityRoughness(void) { return L"pbr-opacity-roughness"; } ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Emission(void) { return L"pbr-emission"; } -ON_wString ON_PhysicallyBasedMaterial::ParametersNames::AmbientOcclusion(void) { return L"pbr-ambient-occlusion"; } -ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Smudge(void) { return L"pbr-smudge"; } -ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Displacement(void) { return L"pbr-displacement"; } -ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Normal(void) { return L"pbr-normal"; } -ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Bump(void) { return L"pbr-bump"; } - diff --git a/opennurbs_material.h b/opennurbs_material.h index 78013d7a..8ad7e28c 100644 --- a/opennurbs_material.h +++ b/opennurbs_material.h @@ -8,7 +8,7 @@ // 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 . // //////////////////////////////////////////////////////////////// @@ -17,11 +17,12 @@ #if !defined(OPENNURBS_MATERIAL_INC_) #define OPENNURBS_MATERIAL_INC_ +class ON_PhysicallyBasedMaterial; /////////////////////////////////////////////////////////////////////////////// // // Class ON_Material -// +// class ON_CLASS ON_Material : public ON_ModelComponent { ON_OBJECT_DECLARE(ON_Material); @@ -45,7 +46,7 @@ public: is nullptr Returns: If ON_Material::Cast(model_component_ref.ModelComponent()) is not nullptr, - that pointer is returned. Otherwise, none_return_value is returned. + that pointer is returned. Otherwise, none_return_value is returned. */ static const ON_Material* FromModelComponentRef( const class ON_ModelComponentReference& model_component_reference, @@ -53,31 +54,31 @@ public: ); // compare everything except Index() value. - static int Compare( + static int Compare( const ON_Material& a, const ON_Material& b ); // compare Id(), Name(), m_rdk_material_instance_id - static int CompareNameAndIds( + static int CompareNameAndIds( const ON_Material& a, const ON_Material& b ); - // Compare all settings (color, reflection, texture, plug-in id) - // that effect the appearance. + // Compare all settings (color, reflection, texture, plug-in id) + // that affect the appearance. // Ignore Index(), Id(), Name(), m_rdk_material_instance_id. - static int CompareAppearance( + static int CompareAppearance( const ON_Material& a, const ON_Material& b ); - static int CompareColorAttributes( + static int CompareColorAttributes( const ON_Material& a, const ON_Material& b ); - static int CompareReflectionAttributes( + static int CompareReflectionAttributes( const ON_Material& a, const ON_Material& b ); @@ -97,15 +98,15 @@ public: /* Parameters: fresnel_index_of_refraction - [in] - ON_Material::Material::Default.m_fresnel_index_of_refraction + ON_Material::Material::Default.m_fresnel_index_of_refraction is a good default N - [in] 3d surface normal R - [in] 3d reflection direction Returns: - 1.0: - The input values were not valid or the calculation failed due to + 1.0: + The input values were not valid or the calculation failed due to a divide by zero or some other numerical arithmetic failure. fresnel reflection coefficient 1/2 * ((g-c)/(g+c))^2 * (1 + ( (c*(g+c) -1)/(c*(g+c) + 1) )^2) @@ -149,9 +150,9 @@ public: ) override; ON::object_type ObjectType() const override; - + ///////////////////////////////////////////////////////////////// - // Interface + // Interface ON_Color Ambient() const; ON_Color Diffuse() const; @@ -177,7 +178,7 @@ public: // ID of the last plug-in to modify this material ON_UUID MaterialPlugInId() const; - void SetMaterialPlugInId( + void SetMaterialPlugInId( ON_UUID plugin_id ); @@ -242,7 +243,7 @@ public: void SetDisableLighting( bool bDisableLighting ); - + //If m_bUseDiffuseTextureAlphaForObjectTransparencyTexture is true, the alpha channel //of the texture in m_textures with m_type=bitmap_texture is used in addition to any //textures with m_type=transparency_texture. @@ -250,7 +251,7 @@ public: void SetUseDiffuseTextureAlphaForObjectTransparencyTexture( bool bUseDiffuseTextureAlphaForObjectTransparencyTexture ); - + ////////////////////////////////////////////////////////////// // // Reflection and Refraction settings @@ -272,9 +273,20 @@ public: //the function that the layer manager uses to color the little material swatch, for example. ON_Color PreviewColor() const; + + //Physically based material interface. Use this interface to set and get PBR parameters + //and to check if this material supports PBR. + //NOTE WELL - ON_PhysicallyBasedMaterial contains a pointer to this - the scope of the ON_PhysicallyBasedMaterial + //object must not exceed the scope of the material that it originally came from. Ideal usage is material.PhysicallyBased().Function() + const ON_PhysicallyBasedMaterial PhysicallyBased(void) const; + ON_PhysicallyBasedMaterial PhysicallyBased(void); + + //Internal use only + static ON_UUID PhysicallyBasedUserdataId(void); + private: // The value of m_rdk_material_id idetifies an RDK (rendering development kit) - // material. Multiple materials in a Rhino model can refer to the same + // material. Multiple materials in a Rhino model can refer to the same // RDK material id. In V5 this value is stored in user data. In V6 it is // saved in the m_rdk_material_id field. ON_UUID m_rdk_material_instance_id = ON_nil_uuid; @@ -309,7 +321,7 @@ public: /* m_reflection_glossiness: - Default is 0.0. + Default is 0.0. Values from 0.0 to 1.0 make sense. - 0.0 reflections are perfectly specular. - t > 0.0 permits reflection ray direction to vary @@ -319,7 +331,7 @@ public: /* m_refraction_glossiness: - Default is 0.0. + Default is 0.0. Values from 0.0 to 1.0 make sense. - 0.0 refractions are perfectly specular. - t > 0.0 permits refraction ray direction to vary @@ -330,7 +342,7 @@ public: /* m_index_of_refraction: Default is 1.0. - Physically, the index of refraction is >= 1.0 and is + Physically, the index of refraction is >= 1.0 and is the value (speed of light in vacum)/(speed of light in material). Some rendering algorithms set m_index_of_refraction to zero or values < 1.0 to generate desirable effects. @@ -340,15 +352,15 @@ public: /* m_fresnel_index_of_refraction: Default is 1.56. - This is the value ON:Material::FresnelReflectionCoefficient() passes + This is the value ON:Material::FresnelReflectionCoefficient() passes as the first parameter to ON_FresnelReflectionCoefficient(). - - Glass material types can be simulated with + - Glass material types can be simulated with m_index_of_refraction ~ 1.56 m_fresnel_index_of_refraction ~ 1.56 - - Thin glass can be simulated with + - Thin glass can be simulated with m_fresnel_index_of_refraction = 1.56 m_index_of_refraction = 0.0 - - Porcelain type materials can be simulated with + - Porcelain type materials can be simulated with m_fresnel_index_of_refraction = 1.56 m_index_of_refraction = 1.0 m_transparency = 0.0 @@ -363,18 +375,18 @@ public: 3d reflection direction Returns: If m_bFresnelReflections is false, then 1.0 is returned. - If m_bFresnelReflections is true, then the value of the fresnel + If m_bFresnelReflections is true, then the value of the fresnel reflection coefficient is returned. In typical rendering applications, the reflection term is multiplied by the fresnel reflection coefficient before it is added to the diffuse color. If any input is not valid or the calculation fails, then 1.0 is returned. Remarks: - When m_bFresnelReflections is true, the calculation is performed by + When m_bFresnelReflections is true, the calculation is performed by calling ON_FresnelReflectionCoefficient() with m_fresnel_index_of_refraction as the fresnel index of refraction. */ double FresnelReflectionCoefficient( - ON_3dVector N, + ON_3dVector N, ON_3dVector R ) const; @@ -402,12 +414,12 @@ public: filename - [in] If nullptr, then any filename matches. type - [in] If ON_Texture::no_texture_type, then any texture type matches. - i0 - [in] If i0 is < 0, the search begins at + i0 - [in] If i0 is < 0, the search begins at m_textures[0], if i0 >= m_textures.Count(), -1 is returnd, otherwise, the search begins at m_textures[i0+1]. Example: - Iterate through all the the bitmap textures on + Iterate through all the the bitmap textures on a material. ON_Material& mat = ...; @@ -415,9 +427,9 @@ public: int bitmap_texture_count = 0; for(;;) { - ti = mat.FindTexture( - nullptr, - ON_Texture::TYPE::bitmap_texture, + ti = mat.FindTexture( + nullptr, + ON_Texture::TYPE::bitmap_texture, ti ); if ( ti < 0 ) @@ -457,14 +469,14 @@ public: textures to the material. If you need to do something different, then just work on the m_textures[] array. */ - int AddTexture( + int AddTexture( const ON_Texture& tx ); /* Description: If there is a texture with a matching type, that texture's - filename is modified, otherwise a new texture is added. + filename is modified, otherwise a new texture is added. Parameters: filename - [in] new filename type - [in] @@ -477,7 +489,7 @@ public: */ int AddTexture( const wchar_t* filename, - ON_Texture::TYPE type + ON_Texture::TYPE type ); /* @@ -492,15 +504,15 @@ public: */ int DeleteTexture( const wchar_t* filename, - ON_Texture::TYPE type + ON_Texture::TYPE type ); ON_ObjectArray m_textures; /* Description: - Used to provide per face material support. - The parent object reference a basic material. + Used to provide per face material support. + The parent object reference a basic material. When a brep face or mesh facet wants to use a material besides the base material, it specifies a channelSupports material channel. The default @@ -508,17 +520,17 @@ public: material. A channel of n > 0 means that face used the material with id m_material_channel[n-1]. If (n-1) >= m_material_channel.Count(), then the base - material is used. The value of + material is used. The value of m_material_channel[n].m_id is persistent. The value of m_material_channel[n].m_i is a runtime - index in the CRhinoDoc::m_material_table[]. If + index in the CRhinoDoc::m_material_table[]. If CRhinoDoc::m_material_table[m_i].m_uuid != m_id, then m_id is assumed to be correct. */ ON_SimpleArray m_material_channel; - + private: - ON_UUID m_plugin_id = ON_nil_uuid; + ON_UUID m_plugin_id = ON_nil_uuid; private: bool Internal_ReadV3( ON_BinaryArchive& archive, int minor_version ); @@ -539,42 +551,179 @@ ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray; // NO! // ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray; -// It is a serious error to have an ON_ClassArray and crashes +// It is a serious error to have an ON_ClassArray and crashes // will occur when user data back pointers are not updated. #endif + +/////////////////////////////////////////////////////////////////////////////// +// +// Class ON_PBRMaterial +// class ON_CLASS ON_PhysicallyBasedMaterial { public: - class ParametersNames + ON_PhysicallyBasedMaterial(const ON_Material& src); + ON_PhysicallyBasedMaterial(const ON_PhysicallyBasedMaterial& src); + virtual ~ON_PhysicallyBasedMaterial(); + + virtual bool IsValid(class ON_TextLog* text_log = nullptr) const; + + ///////////////////////////////////////////////////////////////// + // Interface +public: + //Call this function to determine if the material supports a PBR definition. + //A material will support PBR if the base color is set. All other values are set + //to defaults that will produce a simple plaster-like surface. + virtual bool Supported(void) const; + + //Reflectance model to use. Default is GGX. Renderers do not need to support a specific + //model, but certain material definitions may specify in the hope that a renderer will support. + //GGX support is built into Rhino (Cycles, display) + enum class BRDFs : int { - public: - static ON_wString BaseColor(void); - static ON_wString BRDF(void); - static ON_wString Subsurface(void); - static ON_wString SubsurfaceScatteringColor(void); - static ON_wString SubsurfaceScatteringRadius(void); - static ON_wString Specular(void); - static ON_wString SpecularTint(void); - static ON_wString Metallic(void); - static ON_wString Roughness(void); - static ON_wString Anisotropic(void); - static ON_wString AnisotropicRotation(void); - static ON_wString Sheen(void); - static ON_wString SheenTint(void); - static ON_wString Clearcoat(void); - static ON_wString ClearcoatRoughness(void); - static ON_wString OpacityIor(void); - static ON_wString Opacity(void); - static ON_wString OpacityRoughness(void); - static ON_wString Emission(void); - static ON_wString AmbientOcclusion(void); - static ON_wString Smudge(void); - static ON_wString Displacement(void); - static ON_wString Normal(void); - static ON_wString Bump(void); + GGX = 0, //http://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf + Ward = 1, //https://pdfs.semanticscholar.org/330e/59117d7da6c794750730a15f9a178391b9fe.pdf }; + + virtual BRDFs BRDF(void) const; + virtual void SetBRDF(const BRDFs&); + + virtual ON_4fColor BaseColor(void) const; + virtual void SetBaseColor(const ON_4fColor&); + + //Controls diffuse shape using a subsurface approximation. If full subsurface transport is + //implemented, acts as a mix between diffuse and SSS + virtual double Subsurface(void) const; + virtual void SetSubsurface(double); + + //Color for full subsurface transport if implemented + virtual ON_4fColor SubsurfaceScatteringColor(void) const; + virtual void SetSubsurfaceScatteringColor(const ON_4fColor&); + + //Radius for full subsurface transport if implemented + virtual double SubsurfaceScatteringRadius(void) const; + virtual void SetSubsurfaceScatteringRadius(double); + + //The metallic-ness (0 = dielectric, 1 = metallic). This is a linear blend between two + //different models.The metallic model has no diffuse component and also has a tinted incident + //specular, equal to the base color + virtual double Metallic(void) const; + virtual void SetMetallic(double); + + //Incident specular amount. Linked to Ior property below. + //specular=((ior−1)/(ior+1))2/0.08 + virtual double Specular(void) const; + virtual void SetSpecular(double); + + //Reflective Ior - see specular amount above. Linked to Specular property + virtual double ReflectiveIOR(void) const; + virtual void SetReflectiveIOR(double); + + //A concession for artistic control that tints incident specular towards the base color. + //Grazing specular is still achromatic. + //Tints the facing specular reflection using the base color, while glancing reflection remains white. + //Normal dielectrics have colorless reflection, so this parameter is not technically physically correct and is provided for faking the appearance of materials with complex surface structure. + virtual double SpecularTint(void) const; + virtual void SetSpecularTint(double); + + //Surface roughness, controls both diffuse and specular response + virtual double Roughness(void) const; + virtual void SetRoughness(double); + + //Degree of anisotropy. This controls the aspect ratio of the specular highlight. (0 = isotropic, 1 = maximally anisotropic) + virtual double Anisotropic(void) const; + virtual void SetAnisotropic(double); + + //Rotates the direction of anisotropy, with 1.0 going full circle. + virtual double AnisotropicRotation(void) const; + virtual void SetAnisotropicRotation(double); + + //An additional grazing component, primarily intended for cloth. + //Amount of soft velvet like reflection near edges, for simulating materials such as cloth. + virtual double Sheen(void) const; + virtual void SetSheen(double); + + //Amount to tint sheen towards base color + virtual double SheenTint(void) const; + virtual void SetSheenTint(double); + + //A second, special-purpose specular lobe - Extra white specular layer on top of others. This is useful for materials like car paint and the like. + virtual double Clearcoat(void) const; + virtual void SetClearcoat(double); + + //Controls clearcoat glossiness (0 = a “satin” appearance, 1 = a “gloss” appearance) + virtual double ClearcoatRoughness(void) const; + virtual void SetClearcoatRoughness(double); + + //Index of refraction for transmission. + virtual double OpacityIOR(void) const; + virtual void SetOpacityIOR(double); + + //Mix between fully opaque surface at zero and fully glass like transmission at one. + virtual double Opacity(void) const; + virtual void SetOpacity(double); + + //Controls roughness used for transmitted light. + virtual double OpacityRoughness(void) const; + virtual void SetOpacityRoughness(double); + + //Controls emission - uses base color. + virtual ON_4fColor Emission(void) const; + virtual void SetEmission(ON_4fColor); + + //Texture access functions - exactly the same as ON_Material. Provided for ease of use. + int FindTexture(const wchar_t* filename, ON_Texture::TYPE type, int i0 = -1) const; + int AddTexture(const ON_Texture& tx); + int AddTexture( const wchar_t* filename, ON_Texture::TYPE type); + int DeleteTexture(const wchar_t* filename, ON_Texture::TYPE type); + + //Access the referenced ON_Material. + ON_Material& Material(void); + const ON_Material& Material(void) const; + + //Call this function to set the ON_Material up to represent the PBR material as well as possible. + void SynchronizeLegacyMaterial(void); + +public: + ON_DEPRECATED class ON_CLASS ParametersNames + { + public: + ON_DEPRECATED static ON_wString BaseColor(void); + ON_DEPRECATED static ON_wString BRDF(void); + ON_DEPRECATED static ON_wString Subsurface(void); + ON_DEPRECATED static ON_wString SubsurfaceScatteringColor(void); + ON_DEPRECATED static ON_wString SubsurfaceScatteringRadius(void); + ON_DEPRECATED static ON_wString Specular(void); + ON_DEPRECATED static ON_wString SpecularTint(void); + ON_DEPRECATED static ON_wString Metallic(void); + ON_DEPRECATED static ON_wString Roughness(void); + ON_DEPRECATED static ON_wString Anisotropic(void); + ON_DEPRECATED static ON_wString AnisotropicRotation(void); + ON_DEPRECATED static ON_wString Sheen(void); + ON_DEPRECATED static ON_wString SheenTint(void); + ON_DEPRECATED static ON_wString Clearcoat(void); + ON_DEPRECATED static ON_wString ClearcoatRoughness(void); + ON_DEPRECATED static ON_wString ClearcoatBump(void); + ON_DEPRECATED static ON_wString OpacityIor(void); + ON_DEPRECATED static ON_wString Opacity(void); + ON_DEPRECATED static ON_wString OpacityRoughness(void); + ON_DEPRECATED static ON_wString Emission(void); + }; + +private: + class Impl; + Impl* _pImpl; + const Impl& Implementation(void) const; + Impl& Implementation(void); + + //Ban copying - usage should be material.PhysicallyBased().Function() + ON_PhysicallyBasedMaterial& operator=(const ON_Material& src) = delete; + ON_PhysicallyBasedMaterial& operator=(const ON_PhysicallyBasedMaterial& src) = delete; + friend ON_Material; }; + + #endif diff --git a/opennurbs_math.cpp b/opennurbs_math.cpp index 3a6e5b4f..d53b31e2 100644 --- a/opennurbs_math.cpp +++ b/opennurbs_math.cpp @@ -3407,6 +3407,14 @@ int ON_Compare2dex( const ON_2dex* a, const ON_2dex* b) } +int ON_Compare2udex(const ON_2udex* a, const ON_2udex* b) +{ + if (a->i < b->i) return -1; + if (b->i < a->i) return 1; + return (b->j < a->j) - (a->j < b->j); +} + + int ON_Compare3dex( const ON_3dex* a, const ON_3dex* b) { int d; diff --git a/opennurbs_math.h b/opennurbs_math.h index 61516720..f34dd232 100644 --- a/opennurbs_math.h +++ b/opennurbs_math.h @@ -450,7 +450,7 @@ private: #define ON_IS_VALID_FLOAT(x) ((x) != ON_UNSET_FLOAT && (x) != ON_UNSET_POSITIVE_FLOAT && ON_IS_FINITE_FLOAT(x)) #define ON_IS_UNSET_DOUBLE(x) (ON_UNSET_VALUE == (x) || ON_UNSET_POSITIVE_VALUE == (x)) #define ON_IS_UNSET_FLOAT(x) (ON_UNSET_FLOAT == (x) || ON_UNSET_POSITIVE_FLOAT == (x)) -#define ON_IS_NAN(x) (!((x)==(x)) +#define ON_IS_NAN(x) (!((x)==(x))) ON_DECL float ON_ArrayDotProduct( // returns AoB @@ -1631,6 +1631,9 @@ bool ON_TuneupEvaluationParameter( ON_DECL int ON_Compare2dex( const ON_2dex* a, const ON_2dex* b); +ON_DECL +int ON_Compare2udex(const ON_2udex* a, const ON_2udex* b); + ON_DECL int ON_Compare3dex( const ON_3dex* a, const ON_3dex* b); @@ -2281,4 +2284,46 @@ Returns */ ON_DECL float ON_FloatCeil(double x); +/* +Description: + Determine if a polyline is convex. +Parameters: + point_dim - [in] + 2 or 3 + point_count - [in] + points - [in] + If point_count >= 4 and the first and last points are equal, + then the zero length segment between those points is ignored. + point_stride - [in] + number of doubles between points (>=point_dim) + bStrictlyConvex - [in] + If false, colinear segments are considered convex. +Returns + True if the polyline is convex. +*/ +ON_DECL bool ON_IsConvexPolyline( + size_t point_dim, + size_t point_count, + const double* points, + size_t point_stride, + bool bStrictlyConvex +); + +/* +Description: + Determine if a polyline is convex. +Parameters: + points - [in] + If points.Count() >= 4 and the first and last points are equal, + then the zero length segment between those points is ignored. + bStrictlyConvex - [in] + If false, colinear segments are considered convex. +Returns + True if the polyline is convex. +*/ +ON_DECL bool ON_IsConvexPolyline( + const ON_SimpleArray& points, + bool bStrictlyConvex +); + #endif diff --git a/opennurbs_mesh.cpp b/opennurbs_mesh.cpp index 866f3650..10b80ed7 100644 --- a/opennurbs_mesh.cpp +++ b/opennurbs_mesh.cpp @@ -282,10 +282,10 @@ bool ON_MeshFace::Repair( int fvi_count = 0; f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1; - if ( vi[0] >= 0 && vi[0] < mesh_vertex_count ) + if ( vi[0] >= 0 && vi[0] < mesh_vertex_count && V[vi[0]].IsValid() ) f.vi[fvi_count++] = vi[0]; - if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] ) + if (vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] && V[vi[1]].IsValid()) { if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] ) f.vi[fvi_count++] = vi[1]; @@ -294,7 +294,7 @@ bool ON_MeshFace::Repair( if ( fvi_count < 1 ) return false; - if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[f.vi[0]] != V[vi[2]] ) + if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[vi[2]].IsValid() && V[f.vi[0]] != V[vi[2]] ) { if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] ) f.vi[fvi_count++] = vi[2]; @@ -303,7 +303,7 @@ bool ON_MeshFace::Repair( if ( fvi_count < 2 ) return false; - if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] ) + if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[vi[3]].IsValid() && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] ) { if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] ) f.vi[fvi_count++] = vi[3]; @@ -336,10 +336,10 @@ bool ON_MeshFace::Repair( int fvi_count = 0; f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1; - if ( vi[0] >= 0 && vi[0] < mesh_vertex_count ) + if ( vi[0] >= 0 && vi[0] < mesh_vertex_count && V[vi[0]].IsValid() ) f.vi[fvi_count++] = vi[0]; - if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] ) + if (vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] && V[vi[1]].IsValid()) { if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] ) f.vi[fvi_count++] = vi[1]; @@ -348,7 +348,7 @@ bool ON_MeshFace::Repair( if ( fvi_count < 1 ) return false; - if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[f.vi[0]] != V[vi[2]] ) + if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[vi[2]].IsValid() && V[f.vi[0]] != V[vi[2]] ) { if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] ) f.vi[fvi_count++] = vi[2]; @@ -357,7 +357,7 @@ bool ON_MeshFace::Repair( if ( fvi_count < 2 ) return false; - if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] ) + if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[vi[3]].IsValid() && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] ) { if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] ) f.vi[fvi_count++] = vi[3]; @@ -3741,8 +3741,16 @@ bool ON_Mesh::IsEmpty() const { // Deep sigh. // The "this == &ON_Mesh::Empty" check is to handle the case when - // confused developers const cast ON_Mesh::Empty and modify it. - return (0 == VertexUnsignedCount() && 0 == FaceUnsignedCount()); + // confused developers const cast ON_Mesh::Empty and then modify it. + return (0 == VertexUnsignedCount() && 0 == FaceUnsignedCount()) || (this == &ON_Mesh::Empty); +} + +bool ON_Mesh::IsNotEmpty() const +{ + // Deep sigh. + // The "this != &ON_Mesh::Empty" check is to handle the case when + // confused developers const cast ON_Mesh::Empty and then modify it. + return ( VertexUnsignedCount() > 0 && FaceUnsignedCount() > 0 && this != &ON_Mesh::Empty); } bool ON_Mesh::SetVertex( @@ -4886,6 +4894,17 @@ static int CompareMeshPoint(const void* a,const void* b,void* ptr) return 0; } +unsigned int ON_Mesh::RemoveAllCreases() +{ + unsigned int vertex_count0 = this->VertexUnsignedCount(); + bool bChanged = this->CombineIdenticalVertices(true, true); + const unsigned int vertex_count1 = this->VertexUnsignedCount(); + + if (vertex_count0 == vertex_count1 && bChanged) + vertex_count0 = vertex_count1 + 1; + return (vertex_count0 - vertex_count1); +} + bool ON_Mesh::CombineIdenticalVertices( bool bIgnoreVertexNormals, bool bIgnoreTextureCoordinates @@ -5121,6 +5140,7 @@ bool ON_Mesh::CombineIdenticalVertices( mesh.DestroyPartition(); mesh.DestroyTopology(); + mesh.m_S.Destroy(); if ( mesh.m_V.Capacity() > 4*mesh.m_V.Count() && mesh.m_V.Capacity() > 50 ) { @@ -5701,7 +5721,17 @@ void ON_MeshParameters::SetSimplePlanes( Internal_SetBoolHelper(bSimplePlanes, &m_bSimplePlanes); } - +void ON_MeshParameters::SetSubDDisplayParameters( + const ON_SubDDisplayParameters& subd_parameters +) +{ + m_subd_mesh_parameters = subd_parameters.EncodeAsUnsignedChar(); +} + +const ON_SubDDisplayParameters ON_MeshParameters::SubDDisplayParameters() const +{ + return ON_SubDDisplayParameters::DecodeFromUnsignedChar(m_subd_mesh_parameters); +} const bool ON_MeshParameters::Refine() const { @@ -5822,23 +5852,6 @@ void ON_MeshParameters::SetTolerance( Internal_SetDoubleHelper(tolerance, 0.0, ON_UNSET_VALUE, &m_tolerance); } -unsigned int ON_MeshParameters::SubDDisplayMeshDensity() const -{ - // If this was set using the "slider", - const int mesh_density_percentage = GeometrySettingsDensityPercentage(-1); - - const double relative_density - = (mesh_density_percentage >= 0) - ? (((double)mesh_density_percentage) / 100.0) - : RelativeTolerance(); - - if (false == (relative_density >= 0.0 && relative_density <= 1.0)) - return ON_SubDLimitMesh::DefaultDisplayDensity; - - unsigned int subd_mesh_density = (unsigned int)floor(relative_density*ON_SubDLimitMesh::MaximumDisplayDensity); - return subd_mesh_density; -} - const double ON_MeshParameters::RelativeTolerance() const { return m_relative_tolerance; @@ -6023,6 +6036,26 @@ ON_MeshParameters::ON_MeshParameters( SetRelativeTolerance(density); SetRefine((density < 0.65)); SetSimplePlanes((0.0 == density)); + + + unsigned int subd_display_density = ON_SubDDisplayParameters::Default.DisplayDensity(); + + if (density <= ON_ZERO_TOLERANCE) + subd_display_density = 1; + else if (density < 1.0/6.0) + subd_display_density = ON_SubDDisplayParameters::CourseDensity; + else if (density < 1.0/3.0) + subd_display_density = (ON_SubDDisplayParameters::DefaultDensity+ON_SubDDisplayParameters::CourseDensity)/2; + else if (density <= 0.75) + subd_display_density = ON_SubDDisplayParameters::DefaultDensity; + else if (density <= 1.0-ON_ZERO_TOLERANCE) + subd_display_density = (ON_SubDDisplayParameters::DefaultDensity+ON_SubDDisplayParameters::MaximumDensity)/2; + else if (density >= 1.0 - ON_ZERO_TOLERANCE) + subd_display_density = ON_SubDDisplayParameters::MaximumDensity; + + ON_SubDDisplayParameters subd_parameters(ON_SubDDisplayParameters::Default); + subd_parameters.SetDisplayDensity(subd_display_density); + SetSubDDisplayParameters(subd_parameters); } } @@ -7838,8 +7871,19 @@ void ON_Mesh::DeleteMeshParameters() } } +static bool isValid3fPoint(const ON_3fPoint* a) +{ + return (ON_IS_VALID_FLOAT(a->x) && ON_IS_VALID_FLOAT(a->y) && ON_IS_VALID_FLOAT(a->z)) ? true : false; +} + static int compare3fPoint( const ON_3fPoint* a, const ON_3fPoint* b ) { + const bool aValid = isValid3fPoint(a); + const bool bValid = isValid3fPoint(b); + if (aValid != bValid) + return (aValid ? -1 : 1); // invalid points sort to end. + if (false == aValid) + return 0; // all invalid points are "equal" if ( a->x < b->x ) return -1; if ( a->x > b->x ) return 1; if ( a->y < b->y ) return -1; @@ -7849,8 +7893,20 @@ static int compare3fPoint( const ON_3fPoint* a, const ON_3fPoint* b ) return 0; } + +static bool isValid3dPoint(const ON_3dPoint* a) +{ + return (ON_IS_VALID(a->x) && ON_IS_VALID(a->y) && ON_IS_VALID(a->z)) ? true : false; +} + static int compare3dPoint( const ON_3dPoint* a, const ON_3dPoint* b ) { + const bool aValid = isValid3dPoint(a); + const bool bValid = isValid3dPoint(b); + if (aValid != bValid) + return (aValid ? -1 : 1); // invalid points sort to end. + if (false == aValid) + return 0; // all invalid points are "equal" if ( a->x < b->x ) return -1; if ( a->x > b->x ) return 1; if ( a->y < b->y ) return -1; @@ -7863,7 +7919,12 @@ static int compare3dPoint( const ON_3dPoint* a, const ON_3dPoint* b ) typedef int (*ON_COMPAR_LPVOID_LPVOID)(const void*,const void*); static -unsigned int GetPointMap(unsigned int pt_count, const ON_3fPoint* fV, const ON_3dPoint* dV, ON_SimpleArray& pt_map) +unsigned int GetRemoveDegenerateFacesPointMap( + unsigned int pt_count, + const ON_3fPoint* fV, + const ON_3dPoint* dV, + ON_SimpleArray& pt_map +) { // Faster than ON_Mesh::GetVertexLocationIds() // This static is used only in CullDegenerateFaces(). @@ -7904,6 +7965,15 @@ unsigned int GetPointMap(unsigned int pt_count, const ON_3fPoint* fV, const ON_3 map[index[vt0++]] = max_pt_index; } } + + // invalid points are sorted to the end + vt1 = pt_count; + while (vt1-- > 0) + { + if (isValid3dPoint(dV + index[vt1])) + break; + map[index[vt1]] = ON_UNSET_UINT_INDEX; // invalid point get invalid index + } } else { @@ -7917,6 +7987,15 @@ unsigned int GetPointMap(unsigned int pt_count, const ON_3fPoint* fV, const ON_3 map[index[vt0++]] = max_pt_index; } } + + // invalid points are sorted to the end + vt1 = pt_count; + while (vt1-- > 0) + { + if (isValid3fPoint(fV + index[vt1])) + break; + map[index[vt1]] = ON_UNSET_UINT_INDEX; // invalid point get invalid index + } } onfree(index); } @@ -7944,6 +8023,49 @@ unsigned int ON_Mesh::CullDegenerateFaces() return (face_count0 > face_count1) ? face_count0 - face_count1 : 0; } +unsigned int ON_Mesh::CullDegenerates() +{ + const int mesh_vertex_count0 = VertexCount(); + const int mesh_face_count0 = FaceCount(); + const int mesh_quad_count0 = QuadCount(); + const int mesh_tri_count0 = TriangleCount(); + + // now cull bad faces, invalid vertices, and unreferenced vertices + DeleteComponents( + nullptr, + 0, + true, + true, + true, + true + ); + + const int mesh_vertex_count1 = VertexUnsignedCount(); + const int mesh_face_count1 = FaceUnsignedCount(); + const int mesh_quad_count1 = QuadCount(); + const int mesh_tri_count1 = TriangleCount(); + if ( + mesh_vertex_count0 == mesh_vertex_count1 + && mesh_face_count0 == mesh_face_count1 + && mesh_quad_count0 == mesh_quad_count1 + && mesh_tri_count0 == mesh_tri_count1 + ) + return 0; + + int rc = abs(mesh_vertex_count1 - mesh_vertex_count0) + abs(mesh_face_count1 - mesh_face_count0); + if (0 == rc) + { + // need to return nonzero if a degenerate quad got changed into a triangle + rc = abs(mesh_quad_count1 - mesh_quad_count0); + if (0 == rc) + { + rc = abs(mesh_tri_count1 - mesh_tri_count0); + } + } + + return (unsigned int)rc; +} + int ON_Mesh::CullUnusedVertices() { const unsigned int vcount0 = m_V.UnsignedCount(); @@ -8553,6 +8675,82 @@ int* ON_MeshTopology::GetIntArray(int length) return a; } +bool ON_MeshTopology::IsWeldedEdge(int top_ei) const +{ + if (top_ei < 0 || top_ei >= m_tope.Count() || nullptr == m_mesh) + return false; + + const ON_MeshTopologyEdge& e = m_tope[top_ei]; + if (e.m_topf_count <= 1 || e.m_topvi[0] < 0 || e.m_topvi[1] < 0) + return false; + + const int face_count = m_topf.Count(); + if (face_count < 2 || face_count != m_mesh->FaceCount()) + return false; + + if (e.m_topvi[0] < 0 || e.m_topvi[1] < 0 || e.m_topvi[0] == e.m_topvi[1] ) + return false; + const int topv_count = m_topv.Count(); + if (e.m_topvi[0] >= topv_count || e.m_topvi[1] >= topv_count || topv_count < 3) + return false; + + const int meshv_count = m_mesh->VertexCount(); + if (meshv_count < topv_count || meshv_count != m_topv_map.Count()) + return false; + if (1 == m_topv[e.m_topvi[0]].m_v_count && 1 == m_topv[e.m_topvi[0]].m_v_count) + return true; + + // need to examine faces + int mesh_vi[2] = { -1,-1 }; + for (int efi = 0; efi < e.m_topf_count; ++efi) + { + int top_fi = e.m_topfi[efi]; + if (top_fi < 0 || top_fi >= face_count) + return false; + const ON_MeshTopologyFace& f = m_topf[top_fi]; + const int fe_count = f.IsTriangle() ? 3 : 4; + int fvi[2] = { -1, -1 }; + for (int fei = 0; fei < fe_count; ++fei) + { + if (top_ei != f.m_topei[fei]) + continue; + const ON_MeshFace& mf = m_mesh->m_F[top_fi]; + const bool bRev = f.m_reve[fei]; + fvi[bRev?1:0] = mf.vi[(fei + fe_count - 1) % fe_count]; + fvi[bRev?0:1] = mf.vi[fei]; + if (fvi[0] < 0 || fvi[0] >= meshv_count) + return false; + if (fvi[1] < 0 || fvi[1] >= meshv_count) + return false; + if (m_topv_map[fvi[0]] != e.m_topvi[0] || m_topv_map[fvi[1]] != e.m_topvi[1]) + { + ON_ERROR("Bug in this loop or bad mesh topology."); + fvi[0] = -1; + fvi[1] = -1; + continue; + } + break; + } + if (0 == efi) + { + if (fvi[0] < 0 || fvi[1] < 0 || fvi[0] == fvi[1]) + return false; + mesh_vi[0] = fvi[0]; + mesh_vi[1] = fvi[1]; + } + else + { + if (mesh_vi[0] != fvi[0]) + return false; + if (mesh_vi[1] != fvi[1]) + return false; + } + } + + return (mesh_vi[0] >= 0 && mesh_vi[1]); +} + + bool ON_MeshTopologyFace::IsTriangle() const { return ( m_topei[2] == m_topei[3] && m_topei[0] != m_topei[1] ) @@ -10489,7 +10687,7 @@ bool ON_Mesh::IsValidMeshComponentIndex( break; case ON_COMPONENT_INDEX::mesh_face: - if (ci.m_type >= m_F.Count()) + if (ci.m_index >= m_F.Count()) rc = false; break; @@ -10768,6 +10966,16 @@ ON_MappingTag::ON_MappingTag() Default(); } +ON_MappingTag::ON_MappingTag(const ON_TextureMapping & mapping, const ON_Xform * xform) +{ + Default(); + Set(mapping); + if ( + ON_TextureMapping::TYPE::no_mapping != mapping.m_type && ON_TextureMapping::TYPE::srfp_mapping != mapping.m_type + && nullptr != xform && xform->IsValid() && false == xform->IsIdentity(ON_ZERO_TOLERANCE) && false == xform->IsZero()) + m_mesh_xform = *xform; +} + void ON_MappingTag::Dump( ON_TextLog& text_log ) const { text_log.Print("Texture/color coordinates tag:\n"); @@ -11575,43 +11783,43 @@ static int compare2dPoint(const void* a, const void* b) return 0; } -static int compare3fPoint(const void* a, const void* b) -{ - const float* af = (const float*)a; - const float* bf = (const float*)b; - if (af[0] < bf[0]) - return -1; - if (af[0] > bf[0]) - return 1; - if (af[1] < bf[1]) - return -1; - if (af[1] > bf[1]) - return 1; - if (af[2] < bf[2]) - return -1; - if (af[2] > bf[2]) - return 1; - return 0; -} - -static int compare3dPoint(const void* a, const void* b) -{ - const double* af = (const double*)a; - const double* bf = (const double*)b; - if (af[0] < bf[0]) - return -1; - if (af[0] > bf[0]) - return 1; - if (af[1] < bf[1]) - return -1; - if (af[1] > bf[1]) - return 1; - if (af[2] < bf[2]) - return -1; - if (af[2] > bf[2]) - return 1; - return 0; -} +//static int compare3fPoint(const void* a, const void* b) +//{ +// const float* af = (const float*)a; +// const float* bf = (const float*)b; +// if (af[0] < bf[0]) +// return -1; +// if (af[0] > bf[0]) +// return 1; +// if (af[1] < bf[1]) +// return -1; +// if (af[1] > bf[1]) +// return 1; +// if (af[2] < bf[2]) +// return -1; +// if (af[2] > bf[2]) +// return 1; +// return 0; +//} +// +//static int compare3dPoint(const void* a, const void* b) +//{ +// const double* af = (const double*)a; +// const double* bf = (const double*)b; +// if (af[0] < bf[0]) +// return -1; +// if (af[0] > bf[0]) +// return 1; +// if (af[1] < bf[1]) +// return -1; +// if (af[1] > bf[1]) +// return 1; +// if (af[2] < bf[2]) +// return -1; +// if (af[2] > bf[2]) +// return 1; +// return 0; +//} static int comparedUnsignedPair(const void* a, const void* b) @@ -11672,9 +11880,9 @@ static unsigned int* ON_GetPointLocationIdsHelper( { // Dictionary sort the 3d points (sort on x, then y, then z). if (nullptr != dPoints) - ON_Sort(ON::sort_algorithm::quick_sort, Vid, dPoints, Vcount, point_stride*sizeof(dPoints[0]), compare3dPoint); + ON_Sort(ON::sort_algorithm::quick_sort, Vid, dPoints, Vcount, point_stride*sizeof(dPoints[0]), (ON_COMPAR_LPVOID_LPVOID)compare3dPoint); else - ON_Sort(ON::sort_algorithm::quick_sort, Vid, fPoints, Vcount, point_stride*sizeof(fPoints[0]), compare3fPoint); + ON_Sort(ON::sort_algorithm::quick_sort, Vid, fPoints, Vcount, point_stride*sizeof(fPoints[0]), (ON_COMPAR_LPVOID_LPVOID)compare3fPoint); } // Assign a unique temporary id to each group of coincident points. @@ -12143,17 +12351,112 @@ bool ON_Mesh::DeleteComponents( } bool ON_Mesh::DeleteComponents( - const ON_COMPONENT_INDEX* ci_list, - size_t ci_count, - bool bIgnoreInvalidComponents, - bool bRemoveDegenerateFaces, - bool bRemoveUnusedVertices, - bool bRemoveEmptyNgons - ) + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + bool bIgnoreInvalidComponents, + bool bRemoveDegenerateFaces, + bool bRemoveUnusedVertices, + bool bRemoveEmptyNgons +) { - if ( ci_count <= 0 && false == bRemoveUnusedVertices && false == bRemoveEmptyNgons ) + return DeleteComponents(ci_list, ci_count, + bIgnoreInvalidComponents, bRemoveDegenerateFaces, bRemoveUnusedVertices, bRemoveEmptyNgons, + nullptr); +} + +static int Internal_FaceDegenerateAreaCheck( + ON_MeshFace& f, + int vertex_count, + const ON_3fPoint* fV, + const ON_3dPoint* dV +) +{ + // returns: + // 0: f is degenerate + // 1: f is not degenerate + // 2: f was a degenerate quad and fixed to be a good triangle + ON_3dPoint V[4]; + double a[2]; + const double atol = 1.0e-36; // a hair bigger than the smallest positive normalized float. + if (nullptr != dV) + { + V[0] = dV[f.vi[0]]; + V[1] = dV[f.vi[1]]; + V[2] = dV[f.vi[2]]; + V[3] = dV[f.vi[3]]; + } + else + { + V[0] = fV[f.vi[0]]; + V[1] = fV[f.vi[1]]; + V[2] = fV[f.vi[2]]; + V[3] = fV[f.vi[3]]; + } + + if (f.IsTriangle()) + { + a[0] = ON_CrossProduct(V[1]-V[0],V[2]-V[0]).Length(); + return (a[0] > atol) ? 1 : 0; + } + + a[0] = V[0].DistanceTo(V[2]); + a[1] = V[1].DistanceTo(V[3]); + + // discard L shaped quads + if (false == (a[0] > atol && a[1] > atol)) + return 0; + + if (a[0] <= a[1] * 1e-8) + return 0; // quad is L shaped at floating point precision + if (a[1] <= a[0] * 1e-8) + return 0; // quad is L shaped at floating point precision + + if (a[0] <= a[1]) + { + a[0] = ON_CrossProduct(V[1] - V[0], V[2] - V[0]).Length(); + a[1] = ON_CrossProduct(V[2] - V[0], V[3] - V[0]).Length(); + if (a[0] > atol) + { + if (a[1] > atol) + return 1; + f.vi[3] = -1; + } + else if (a[1] > atol) + f.vi[1] = -1; + } + else if (a[1] < a[0] ) + { + a[0] = ON_CrossProduct(V[2] - V[1], V[3] - V[1]).Length(); + a[1] = ON_CrossProduct(V[3] - V[1], V[2] - V[1]).Length(); + if (a[0] > atol) + { + if (a[1] > atol) + return 1; + f.vi[0] = -1; + } + else if (a[1] > atol) + f.vi[2] = -1; + } + else + return 0; // nan snuck through + + const bool bRepaired = (nullptr != dV) ? f.Repair(vertex_count, dV) : f.Repair(vertex_count, fV); + return bRepaired ? 2 : 0; +} + +bool ON_Mesh::DeleteComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + bool bIgnoreInvalidComponents, + bool bRemoveDegenerateFaces, + bool bRemoveUnusedVertices, + bool bRemoveEmptyNgons, + unsigned int* faceMap +) +{ + if (ci_count <= 0 && false == bRemoveUnusedVertices && false == bRemoveEmptyNgons && false == bRemoveDegenerateFaces) return true; - if ( 0 == ci_list && ci_count > 0 ) + if (0 == ci_list && ci_count > 0) return false; const ON_MeshTopology* top = m_top.IsValid() ? &m_top : 0; @@ -12168,38 +12471,38 @@ bool ON_Mesh::DeleteComponents( unsigned int* point_id_map_fvi = (unsigned int*)point_id_map_f.vi; - if ( false == bIgnoreInvalidComponents ) + if (false == bIgnoreInvalidComponents) { - for ( size_t i = 0; i < ci_count; i++ ) + for (size_t i = 0; i < ci_count; i++) { cdex = (unsigned int)(ci_list[i].m_index); - switch(ci_list[i].m_type) + switch (ci_list[i].m_type) { case ON_COMPONENT_INDEX::mesh_vertex: - if ( cdex >= vertex_count0 ) + if (cdex >= vertex_count0) return false; break; case ON_COMPONENT_INDEX::meshtop_vertex: - if ( cdex >= topvertex_count0 ) + if (cdex >= topvertex_count0) return false; break; case ON_COMPONENT_INDEX::meshtop_edge: - if ( cdex >= topedge_count0 ) + if (cdex >= topedge_count0) return false; break; case ON_COMPONENT_INDEX::mesh_face: - if ( cdex >= face_count0 ) + if (cdex >= face_count0) return false; break; case ON_COMPONENT_INDEX::mesh_ngon: - if ( cdex >= ngon_count0 ) + if (cdex >= ngon_count0) return false; break; } } } - if ( vertex_count0 <= 0 ) + if (vertex_count0 <= 0) return (face_count0 <= 0); ON_SimpleArray vertex_status_buffer(vertex_count0); @@ -12207,10 +12510,19 @@ bool ON_Mesh::DeleteComponents( vertex_status_buffer.Zero(); unsigned int* vertex_status = vertex_status_buffer.Array(); - ON_SimpleArray face_status_buffer(face_count0); - face_status_buffer.SetCount(face_count0); - face_status_buffer.Zero(); - unsigned int* face_status = face_status_buffer.Array(); + ON_SimpleArray face_status_buffer; + unsigned int* face_status; + if (faceMap == nullptr) + { + face_status_buffer.SetCapacity(face_count0); + face_status_buffer.SetCount(face_count0); + face_status = face_status_buffer.Array(); + } + else + { + face_status = faceMap; + } + memset(face_status, 0, m_F.UnsignedCount() * sizeof(unsigned int)); bool bDoomedFaces = false; bool bDoomedVertices = false; @@ -12219,13 +12531,13 @@ bool ON_Mesh::DeleteComponents( const unsigned int* fvi; - for ( size_t i = 0; i < ci_count; i++ ) + for (size_t i = 0; i < ci_count; i++) { cdex = (unsigned int)(ci_list[i].m_index); - switch(ci_list[i].m_type) + switch (ci_list[i].m_type) { case ON_COMPONENT_INDEX::mesh_vertex: - if ( cdex < vertex_count0 ) + if (cdex < vertex_count0) { bDoomedVertices = true; vertex_status[cdex] = ON_UNSET_UINT_INDEX; @@ -12233,42 +12545,42 @@ bool ON_Mesh::DeleteComponents( break; case ON_COMPONENT_INDEX::meshtop_vertex: - if ( cdex < topvertex_count0 ) + if (cdex < topvertex_count0) { const ON_MeshTopologyVertex& tv = top->m_topv[cdex]; - for ( int tvi = 0; tvi < tv.m_v_count; tvi++ ) + for (int tvi = 0; tvi < tv.m_v_count; tvi++) { - const unsigned int vi =(unsigned int)(tv.m_vi[tvi]); - if ( vi < vertex_count0 ) + const unsigned int vi = (unsigned int)(tv.m_vi[tvi]); + if (vi < vertex_count0) { bDoomedVertices = true; vertex_status[vi] = ON_UNSET_UINT_INDEX; } - } + } } break; case ON_COMPONENT_INDEX::meshtop_edge: - if ( cdex < topedge_count0 ) + if (cdex < topedge_count0) { const ON_MeshTopologyEdge& te = top->m_tope[cdex]; - if ( 0 != te.m_topfi ) + if (0 != te.m_topfi) { - for ( int j = 0; j < te.m_topf_count; j++ ) + for (int j = 0; j < te.m_topf_count; j++) { - fi =(unsigned int)(te.m_topfi[j]); - if ( fi < face_count0 ) + fi = (unsigned int)(te.m_topfi[j]); + if (fi < face_count0) { bDoomedFaces = true; face_status[fi] = ON_UNSET_UINT_INDEX; } - } + } } } break; case ON_COMPONENT_INDEX::mesh_face: - if ( cdex < face_count0 ) + if (cdex < face_count0) { bDoomedFaces = true; face_status[cdex] = ON_UNSET_UINT_INDEX; @@ -12276,25 +12588,25 @@ bool ON_Mesh::DeleteComponents( break; case ON_COMPONENT_INDEX::mesh_ngon: - if ( cdex < ngon_count0 ) + if (cdex < ngon_count0) { const ON_MeshNgon* ngon = Ngon(cdex); - if ( 0 != ngon ) + if (0 != ngon) { const int ngon_index = cdex; bDoomedNgons = true; m_NgonMap.SetCount(0); - if ( 0 != ngon->m_fi ) + if (0 != ngon->m_fi) { - for ( unsigned int j = 0; j < ngon->m_Fcount; j++ ) + for (unsigned int j = 0; j < ngon->m_Fcount; j++) { fi = ngon->m_fi[j]; - if ( fi < face_count0 ) + if (fi < face_count0) { bDoomedFaces = true; face_status[fi] = ON_UNSET_UINT_INDEX; } - } + } } RemoveNgon(ngon_index); } @@ -12308,12 +12620,25 @@ bool ON_Mesh::DeleteComponents( const unsigned int* point_id_map = nullptr; if (bRemoveDegenerateFaces) { + // point_id_map[vi0] == point_id_map[vi1] if and only if m_V[vi0] == m_V[vi1]. + const unsigned int point_count = m_V.UnsignedCount(); ON_3fPoint* fV = m_V.Array(); ON_3dPoint* dV = HasDoublePrecisionVertices() ? DoublePrecisionVertices().Array() : 0; - point_id_count = GetPointMap(m_V.UnsignedCount(), fV, dV, point_id_map_buffer); + if (nullptr != dV) + { + ON_3dPoint* p1 = dV + point_count; + const ON_3dPoint badp(ON_DBL_MAX, ON_DBL_MAX, ON_DBL_MAX); + for (ON_3dPoint* p = dV; p < p1; ++p) + { + if (p->IsValid()) + continue; + *p = badp; // so bad points will sort las + } + } + point_id_count = GetRemoveDegenerateFacesPointMap(point_count, fV, dV, point_id_map_buffer); if (point_id_count > 0 && point_id_count <= point_id_map_buffer.UnsignedCount() - && m_V.UnsignedCount() == point_id_map_buffer.UnsignedCount() + && point_count == point_id_map_buffer.UnsignedCount() ) { point_id_map = point_id_map_buffer.Array(); @@ -12325,103 +12650,89 @@ bool ON_Mesh::DeleteComponents( } unsigned int face_count1 = 0; - for ( fi = 0; fi < face_count0; fi++ ) { - if ( 0 == face_status[fi] ) + const ON_3fPoint* fV = m_V.Array(); + const ON_3dPoint* dV = HasDoublePrecisionVertices() ? DoublePrecisionVertices().Array() : 0; + for (fi = 0; fi < face_count0; fi++) { - fvi = (const unsigned int*)m_F[fi].vi; - for ( unsigned int j = 0; j < 4; j++ ) + if (0 != face_status[fi]) + continue; // this face status is already known + + ON_MeshFace& f0 = m_F[fi]; + fvi = (const unsigned int*)f0.vi; + for (unsigned int j = 0; j < 4; j++) { - if ( fvi[j] >= vertex_count0 || ON_UNSET_UINT_INDEX == vertex_status[fvi[j]] ) + if (fvi[j] >= vertex_count0 || ON_UNSET_UINT_INDEX == vertex_status[fvi[j]]) { bDoomedFaces = true; face_status[fi] = ON_UNSET_UINT_INDEX; break; } } - if ( 0 == face_status[fi] ) + if (ON_UNSET_UINT_INDEX == face_status[fi]) + continue; // f0 has invalid vertex indices + + if (point_id_count > 0) { - if (point_id_count > 0) + // if point_id_count > 0, then bRemoveDegenerateFaces = true. + // set point_id_map_f.vi[] to values of "topological indices" + point_id_map_fvi[0] = point_id_map[f0.vi[0]]; + point_id_map_fvi[1] = point_id_map[f0.vi[1]]; + point_id_map_fvi[2] = point_id_map[f0.vi[2]]; + point_id_map_fvi[3] = point_id_map[f0.vi[3]]; + if (false == point_id_map_f.IsValid(point_id_count)) { - ON_MeshFace& f0 = m_F[fi]; - // set f.vi[] to values of topological indices + // point_id_map_f invalid means we have a degenerate quad or worse. + + if (f0.IsQuad() && (point_id_map_f.vi[0] == point_id_map_f.vi[2] || point_id_map_f.vi[1] == point_id_map_f.vi[3])) { - unsigned int f0vi = (unsigned int)f0.vi[0]; - point_id_map_fvi[0] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi]; - - f0vi = (unsigned int)f0.vi[1]; - point_id_map_fvi[1] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi]; - - f0vi = (unsigned int)f0.vi[2]; - point_id_map_fvi[2] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi]; - - f0vi = (unsigned int)f0.vi[3]; - point_id_map_fvi[3] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi]; + // "L" quads just get deleted. + bDoomedFaces = true; + face_status[fi] = ON_UNSET_UINT_INDEX; + continue; } - if (!point_id_map_f.IsValid(point_id_count)) + // the corners of face f0 are not perfect. We either have to convert a quad to a triangle + // or we have a degenerate face. + if ((nullptr != dV) ? f0.Repair(vertex_count0, dV) : f0.Repair(vertex_count0, fV)) { - // determine if a degenerate quad can be made into a valid triangle. - if (point_id_map_fvi[0] == point_id_map_fvi[1] || point_id_map_fvi[0] >= point_id_count) - { - f0.vi[0] = f0.vi[1]; - f0.vi[1] = f0.vi[2]; - f0.vi[2] = f0.vi[3]; - point_id_map_fvi[0] = point_id_map_fvi[1]; - point_id_map_fvi[1] = point_id_map_fvi[2]; - point_id_map_fvi[2] = point_id_map_fvi[3]; - } - - if (point_id_map_fvi[1] == point_id_map_fvi[2] || point_id_map_fvi[1] >= point_id_count) - { - f0.vi[1] = f0.vi[2]; - f0.vi[2] = f0.vi[3]; - point_id_map_fvi[1] = point_id_map_fvi[2]; - point_id_map_fvi[2] = point_id_map_fvi[3]; - } - - if (point_id_map_fvi[2] >= point_id_count) - { - f0.vi[2] = f0.vi[3]; - point_id_map_fvi[2] = point_id_map_fvi[3]; - } - - if (point_id_map_fvi[3] >= point_id_count) - { - f0.vi[3] = f0.vi[2]; - point_id_map_fvi[3] = point_id_map_fvi[2]; - } - else if (point_id_map_fvi[0] == point_id_map_fvi[3] && point_id_map_fvi[2] != point_id_map_fvi[3]) - { - f0.vi[0] = f0.vi[1]; - f0.vi[1] = f0.vi[2]; - f0.vi[2] = f0.vi[3]; - point_id_map_fvi[0] = point_id_map_fvi[1]; - point_id_map_fvi[1] = point_id_map_fvi[2]; - point_id_map_fvi[2] = point_id_map_fvi[3]; - } - - if (!f0.IsValid(vertex_count0) || !point_id_map_f.IsValid(point_id_count)) - { - // face cannot be repaired by juggling vertex indices - bDoomedFaces = true; - face_status[fi] = ON_UNSET_UINT_INDEX; - continue; - } - else - { - bModifiedFaces = true; - } - + // f0 what an invalid quad fixed to be a valid triangle + bModifiedFaces = true; + } + else + { + // face cannot be repaired by juggling vertex indices + bDoomedFaces = true; + face_status[fi] = ON_UNSET_UINT_INDEX; + continue; } } - - face_status[fi] = face_count1++; - for ( unsigned int j = 0; j < 4; j++ ) + switch (Internal_FaceDegenerateAreaCheck(f0, (int)vertex_count0, fV, dV)) { - vertex_status[fvi[j]] = 1; // vertex is referenced by a face + case 0: + bDoomedFaces = true; + face_status[fi] = ON_UNSET_UINT_INDEX; + break; + + case 1: + // f0 is totally valid + break; + + case 2: + // f0 what an invalid quad fixed to be a valid triangle + bModifiedFaces = true; + break; } + + if (ON_UNSET_UINT_INDEX == face_status[fi]) + continue; + } + + face_status[fi] = face_count1++; + for (unsigned int j = 0; j < 4; j++) + { + vertex_status[fvi[j]] = 1; // vertex is referenced by a face } } } @@ -12448,7 +12759,7 @@ bool ON_Mesh::DeleteComponents( } } - if ( 0 == vertex_count1 && 0 == face_count1 ) + if ( 0 == vertex_count1 || 0 == face_count1 ) { Destroy(); return true; @@ -12634,6 +12945,7 @@ bool ON_Mesh::DeleteComponents( m_invalid_count = 0; m_quad_count = 0; m_triangle_count = 0; + SetClosed(-1); } if ( bRemoveEmptyNgons ) @@ -13721,3 +14033,1160 @@ bool ON_MeshCache::Transform( return rc; } +////////////////////////////////////////////////////////////////////////// +// +// ON_MeshRef +// + +ON_MeshRef::ON_MeshRef() ON_NOEXCEPT +{} + +ON_MeshRef::~ON_MeshRef() +{ + m_mesh_sp.reset(); +} + +ON_MeshRef::ON_MeshRef(const ON_MeshRef& src) ON_NOEXCEPT + : m_mesh_sp(src.m_mesh_sp) +{} + +ON_MeshRef& ON_MeshRef::operator=(const ON_MeshRef& src) +{ + if ( this != &src ) + m_mesh_sp = src.m_mesh_sp; + return *this; +} + +#if defined(ON_HAS_RVALUEREF) +// rvalue copy constructor +ON_MeshRef::ON_MeshRef( ON_MeshRef&& src) ON_NOEXCEPT + : m_mesh_sp(std::move(src.m_mesh_sp)) +{} + +// rvalue assignment operator +ON_MeshRef& ON_MeshRef::operator=(ON_MeshRef&& src) +{ + m_mesh_sp.reset(); + m_mesh_sp = std::move(src.m_mesh_sp); + return *this; +} +#endif + +const class ON_Mesh& ON_MeshRef::Mesh() const +{ + const ON_Mesh* mesh = m_mesh_sp.get(); + if ( nullptr == mesh ) + mesh = &ON_Mesh::Empty; + return *mesh; +} + +unsigned int ON_MeshRef::ReferenceCount() const +{ + return (unsigned int)m_mesh_sp.use_count(); +} + +void ON_MeshRef::Clear() +{ + m_mesh_sp.reset(); +} + +class ON_Mesh& ON_MeshRef::NewMesh() +{ + ON_Mesh* mesh = new ON_Mesh(); + ON_Mesh* managed_mesh = SetMeshForExperts(mesh); + return *managed_mesh; +} + +class ON_Mesh& ON_MeshRef::CopyMesh( + const ON_MeshRef& src + ) +{ + return CopyMesh(src.Mesh()); +} + +class ON_Mesh& ON_MeshRef::CopyMesh( + const ON_Mesh& src + ) +{ + ON_Mesh* mesh_copy = new ON_Mesh(src); + ON_Mesh* managed_mesh = SetMeshForExperts(mesh_copy); + return *managed_mesh; +} + +class ON_Mesh& ON_MeshRef::UniqueMesh() +{ + const ON_Mesh& mesh = Mesh(); + if (m_mesh_sp.use_count() > 1 ) + return CopyMesh(mesh); + return const_cast< ON_Mesh& >(mesh); +} + +class ON_Mesh* ON_MeshRef::SetMeshForExperts( + class ON_Mesh*& mesh + ) +{ + Clear(); + ON_Mesh* managed_mesh = ( mesh == &ON_Mesh::Empty ) ? nullptr : mesh; + mesh = nullptr; + if (nullptr != managed_mesh ) + m_mesh_sp = std::shared_ptr(managed_mesh); + return managed_mesh; +} + + + +unsigned int ON_Mesh::DissolveOrDelete( + const ON_SimpleArray& ci_list +) +{ + // Dissolve edges and vertices, delete faces + const unsigned int bailout_rc = ON_UNSET_UINT_INDEX; + + const int ci_list_count = ci_list.UnsignedCount(); + if (ci_list_count <= 0) + return bailout_rc; + + + const int mesh0_vertex_count = this->VertexCount(); + if (mesh0_vertex_count < 3) + return bailout_rc; + const int mesh0_face_count = this->FaceCount(); + if (mesh0_face_count < 1) + return bailout_rc; + + const ON_MeshTopology& top0 = Topology(); + const int top0_vertex_count = top0.m_topv.Count(); + + + ON_SimpleArray faces(ci_list_count); + ON_SimpleArray edges_and_vertices(ci_list_count); + ON_SimpleArray edges(ci_list_count); + + for (int i = 0; i < ci_list_count; ++i) + { + ON_COMPONENT_INDEX ci = ci_list[i]; + if (ci.m_index < 0) + continue; + switch (ci.m_type) + { + case ON_COMPONENT_INDEX::TYPE::mesh_vertex: + if ( ci.m_index < mesh0_vertex_count) + edges_and_vertices.Append(ci); + break; + + case ON_COMPONENT_INDEX::TYPE::meshtop_vertex: + // must save input top vertex as an ordinary vertex index + // because input topology indices will be changed if faces are deleted. + if (ci.m_index < top0_vertex_count) + { + const ON_MeshTopologyVertex& v = top0.m_topv[ci.m_index]; + if (v.m_v_count > 0 && nullptr != v.m_vi) + { + ON_COMPONENT_INDEX vci(ON_COMPONENT_INDEX::TYPE::mesh_vertex, v.m_vi[0]); + if ( vci.m_index >= 0 && vci.m_index < mesh0_vertex_count) + edges_and_vertices.Append(vci); + } + } + break; + + case ON_COMPONENT_INDEX::TYPE::meshtop_edge: + // must save input edge as a pair of ordinary vertex indices + // because input topology indices will be changed if faces are deleted. + if ( ci.m_index < top0.m_tope.Count()) + { + const ON_MeshTopologyEdge& e = top0.m_tope[ci.m_index]; + ON_2dex evdex(-1, -1); + int* evi = &evdex.i; + for (int j = 0; j < 2; j++) + { + if (e.m_topvi[j] < 0 || e.m_topvi[j] >= top0_vertex_count) + break; + const ON_MeshTopologyVertex& v = top0.m_topv[e.m_topvi[j]]; + if (v.m_v_count > 0 && nullptr != v.m_vi) + evi[j] = v.m_vi[0]; + } + if (evdex.i > evdex.j) + { + const int x = evdex.i; + evdex.i = evdex.j; + evdex.j = x; + } + if (0 <= evdex.i && evdex.i < evdex.j && evdex.j < mesh0_vertex_count) + edges.Append(evdex); + } + break; + + case ON_COMPONENT_INDEX::TYPE::mesh_face: + faces.Append(ci); + break; + + case ON_COMPONENT_INDEX::TYPE::mesh_ngon: + { + // must convert ngon to a bucnh of face indices + const ON_MeshNgon* ngon = this->Ngon(ci.m_index); + if (nullptr == ngon || nullptr == ngon->m_fi) + break; + ON_COMPONENT_INDEX fci(ON_COMPONENT_INDEX::TYPE::mesh_face, -1); + for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi) + { + fci.m_index = ngon->m_fi[nfi]; + if ( fci.m_index < mesh0_face_count) + faces.Append(fci); + } + } + break; + }; + } + + + // delete the faces in a way that does not change ordinary vertex indices + DeleteComponents( + faces.Array(), + faces.UnsignedCount(), + true, // bIgnoreInvalidComponents, + true, // bRemoveDegenerateFaces, + false, // bRemoveUnusedVertices CRITICAL to preserve vertex index values + true, // bRemoveEmptyNgons, + nullptr // unsigned int* faceMap + ); + + if (FaceCount() <= 0) + { + Destroy(); + return bailout_rc; + } + + if (mesh0_vertex_count != VertexCount()) + return bailout_rc; // vertex array was modified + + if (edges.Count() > 0 || edges_and_vertices.Count() > 0) + { + // convert ordinary vertex indices into top1 indices + const ON_MeshTopology& top1 = Topology(); + const int top1_vertex_count = top1.m_topv.Count(); + const int top1_edge_count = top1.m_tope.Count(); + if (mesh0_vertex_count == top1.m_topv_map.Count()) + { + // get a clean list of top1.m_topv[] indices + int vertex_count = 0; + for (int i = 0; i < edges_and_vertices.Count(); ++i) + { + ON_COMPONENT_INDEX ci = edges_and_vertices[i]; + ci.m_type = ON_COMPONENT_INDEX::TYPE::meshtop_vertex; + ci.m_index = top1.m_topv_map[ci.m_index]; + if (ci.m_index >= 0 && ci.m_index < top1_vertex_count) + edges_and_vertices[vertex_count++] = ci; + } + edges_and_vertices.SetCount(vertex_count); + edges_and_vertices.QuickSortAndRemoveDuplicates(ON_COMPONENT_INDEX::Compare); + vertex_count = edges_and_vertices.Count(); + + // now add edges to edges_and_vertices[] + ON_COMPONENT_INDEX eci(ON_COMPONENT_INDEX::TYPE::meshtop_edge, -1); + for (int i = 0; i < edges.Count(); i++) + { + ON_2dex evi = edges[i]; + const int topv[2] = { top1.m_topv_map[evi.i],top1.m_topv_map[evi.j] }; + if ( + topv[0] != topv[1] + && topv[0] >= 0 && topv[0] < top1_vertex_count + && topv[1] >= 0 && topv[1] < top1_vertex_count + ) + { + const ON_MeshTopologyVertex& v0 = top1.m_topv[topv[0]]; + if ( v0.m_tope_count > 0 && nullptr != v0.m_topei ) + { + // find the edge connecting the two vertices + for (int k = 0; k < v0.m_tope_count; ++k) + { + eci.m_index = v0.m_topei[k]; + if (eci.m_index >= 0 && eci.m_index < top1_edge_count) + { + const ON_MeshTopologyEdge& e = top1.m_tope[eci.m_index]; + if ( + (e.m_topvi[0] == topv[0] && e.m_topvi[1] == topv[1]) + || (e.m_topvi[0] == topv[1] && e.m_topvi[1] == topv[0])) + { + edges_and_vertices.Append(eci); + } + } + } + } + } + } + } + } + + // dissolve the edges and vertices by merging faces + unsigned int rc + = (edges_and_vertices.Count() > 0) + ? MergeFaceSets(edges_and_vertices) + : bailout_rc; + + // Finally, we can remove unused vertices. + DeleteComponents( + nullptr, + 0, + true, // bIgnoreInvalidComponents, + true, // bRemoveDegenerateFaces, + true, // bRemoveUnusedVertices, + true, // bRemoveEmptyNgons, + nullptr // unsigned int* faceMap + ); + + return rc; +} + +static void Internal_WeldNgonCandiate( + const ON_Mesh& mesh, + const ON_MeshTopology& top, + unsigned char* fmarks, + const unsigned char xmark, + const ON_SimpleArray& ngon_fi +) +{ + const unsigned ngon_face_count = ngon_fi.UnsignedCount(); + if (ngon_face_count < 2) + return; + + // Assumption fmarks[] has no xmark. + // Set xmark for all faces referenced in ngon_fi[] and + // use that mark to find interior vertices that need + // to be welded before the ngon can be created. + for (unsigned nfi = 0; nfi < ngon_face_count; ++nfi) + fmarks[ngon_fi[nfi]] |= xmark; + + for (unsigned nfi = 0; nfi < ngon_face_count; ++nfi) + { + const int fi = ngon_fi[nfi]; + const ON_MeshTopologyFace& f = top.m_topf[fi]; + const int fv_count = f.IsTriangle() ? 3 : 4; + int* fvi = const_cast( mesh.m_F[fi].vi ); + for (int fei = 0; fei < fv_count; ++fei) + { + const int evi = (0 == f.m_reve[fei]) ? 1 : 0; + const ON_MeshTopologyEdge& fe = top.m_tope[f.m_topei[fei]]; + const int topvi = fe.m_topvi[evi]; + const ON_MeshTopologyVertex& v = top.m_topv[topvi]; + if (v.m_v_count <= 1 || nullptr == v.m_vi) + continue; + int mesh_vi[2] = { fvi[fei],fvi[fei] }; + for (int pass = 0; pass < 2; ++pass) + { + for (int vei = 0; vei < v.m_v_count; ++vei) + { + const ON_MeshTopologyEdge& ve = top.m_tope[f.m_topei[fei]]; + for (int efi = 0; efi < ve.m_topf_count; ++efi) + { + const int f1i = ve.m_topfi[efi]; + if (fi == f1i) + continue; + if (0 == (fmarks[f1i] |= xmark)) + continue; // this face is not in the ngon + const ON_MeshTopologyFace& f1 = top.m_topf[f1i]; + int* f1vi = const_cast( mesh.m_F[f1i].vi ); + const int f1v_count = f1.IsTriangle() ? 3 : 4; + for (int f1ei = 0; f1ei < f1v_count; ++f1ei) + { + const int k = (0 == f1.m_reve[f1ei]) ? 1 : 0; + const ON_MeshTopologyEdge& f1e = top.m_tope[f1.m_topei[f1ei]]; + if (topvi == f1e.m_topvi[k]) + { + if (0 == pass) + { + if (f1vi[f1ei] < mesh_vi[0]) + mesh_vi[0] = f1vi[f1ei]; + else if (f1vi[f1ei] > mesh_vi[1]) + mesh_vi[1] = f1vi[f1ei]; + } + else + { + // weld all faces in ngon to use vertex mesh_vi[0] + if (2 == f1ei && f1vi[2] == f1vi[3]) + { + f1vi[2] = mesh_vi[0]; + f1vi[3] = mesh_vi[0]; + } + else + { + f1vi[f1ei] = mesh_vi[0]; + } + } + } + } + if (3 == f1v_count) + f1vi[3] = f1vi[2]; + } + } + if (0 == pass) + { + if (mesh_vi[0] == mesh_vi[1]) + break; + } + else + { + // weld all faces in ngon to use vertex mesh_vi[0] + if (2 == fei && fvi[2] == fvi[3]) + { + fvi[2] = mesh_vi[0]; + fvi[3] = mesh_vi[0]; + } + else + { + fvi[fei] = mesh_vi[0]; + } + } + } + } + } + + // clear the xmarks set above + const unsigned char mask = ~xmark; + for (unsigned nfi = 0; nfi < ngon_face_count; ++nfi) + fmarks[ngon_fi[nfi]] &= mask; +} + +static void Internal_GrowNgon( + const ON_MeshTopology& top, + unsigned char* emarks, + unsigned char* fmarks, + unsigned char etest_mask, + unsigned char etest_result, + unsigned char ftest_mask, + unsigned char ftest_result, + const unsigned char merged_mark, + ON_SimpleArray& ngon_fi +) +{ + if (ngon_fi.Count() <= 0) + return; + + const int face_count = top.m_topf.Count(); + + for (int nfi = 0; nfi < ngon_fi.Count(); ++nfi) + fmarks[ngon_fi[nfi]] |= merged_mark; + + etest_mask |= merged_mark; + etest_result &= ~merged_mark; + + ftest_mask |= merged_mark; + ftest_result &= ~merged_mark; + + for (int nfi = 0; nfi < ngon_fi.Count(); ++nfi) + { + const int f0i = ngon_fi[nfi]; + const ON_MeshTopologyFace& f0 = top.m_topf[f0i]; + const int f0_ecount = f0.IsTriangle() ? 3 : 4; + for (int fei = 0; fei < f0_ecount; ++fei) + { + const int ei = f0.m_topei[fei]; + if (ei < 0 || ei > top.m_tope.Count()) + continue; + if (etest_result != (emarks[ei] & etest_mask)) + continue; + emarks[ei] |= merged_mark; + const ON_MeshTopologyEdge& e = top.m_tope[ei]; + if (2 != e.m_topf_count || nullptr == e.m_topfi) + continue; + const int efi = (f0i != e.m_topfi[0]) ? 0 : 1; + const int f1i = e.m_topfi[efi]; + if (f1i < 0 || f1i >= face_count) + continue; + if (ftest_result != (fmarks[f1i] & ftest_mask)) + continue; + fmarks[f1i] |= merged_mark; + ngon_fi.Append(f1i); + } + } +} + +static void Internal_AddMarkToFaceAndEdgesAndVertices( + const ON_Mesh& mesh, + const ON_MeshTopology& top, + const unsigned int* ngon0_map, + int face_index, + const unsigned char mark, + unsigned char* fmarks, + unsigned char* emarks, + unsigned char* vmarks +) +{ + if (0 == mark || face_index < 0 || face_index > top.m_topf.Count()) + return; + if (nullptr == fmarks && nullptr == emarks && nullptr == vmarks) + return; + + unsigned fi_count = 1; + const int* fi_list = &face_index; + if (nullptr != ngon0_map) + { + const unsigned ni = ngon0_map[face_index]; + if (ni < mesh.NgonUnsignedCount()) + { + const ON_MeshNgon* ngon = mesh.Ngon(ni); + if (ngon->m_Fcount > 1 && nullptr != ngon->m_fi) + { + for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi) + { + if (face_index == (int)(ngon->m_fi[nfi])) + { + fi_count = ngon->m_Fcount; + fi_list = (const int*)ngon->m_fi; + break; + } + } + } + } + } + + for (unsigned nfi = 0; nfi < fi_count; ++nfi) + { + int fi = fi_list[nfi]; + if (fi < 0 || fi >= top.m_topf.Count()) + continue; + if (nullptr != fmarks) + fmarks[fi] |= mark; + if (nullptr != emarks || nullptr != vmarks) + { + const ON_MeshTopologyFace& f = top.m_topf[fi]; + //const int fecount = f.IsTriangle() ? 3 : 4; + const int tope_count = top.m_tope.Count(); + const int topv_count = top.m_topv.Count(); + for (int fei = 0; fei < 4; fei++) + { + const int ei = f.m_topei[fei]; + if (ei < 0 || ei >= tope_count) + continue; + if (nullptr != emarks) + emarks[ei] |= mark; + if (nullptr != vmarks) + { + const ON_MeshTopologyEdge& e = top.m_tope[ei]; + for (int evi = 0; evi < 2; ++evi) + { + const int topvi = e.m_topvi[evi]; + if (topvi < 0 || topvi >= topv_count) + continue; + vmarks[topvi] |= mark; + } + } + } + } + } +} + + +static void Internal_AddMarkToNgonInteriorEdges( + const ON_Mesh& mesh, + const ON_MeshTopology& top, + const unsigned int* ngon0_map, + int face_index, + const unsigned char interior_edge_mark, + unsigned char* emarks +) +{ + if (nullptr == ngon0_map) + return; + const int face_count = top.m_topf.Count(); + const int edge_count = top.m_tope.Count(); + if (face_index < 0 || face_index >= face_count) + return; + const unsigned ni = ngon0_map[face_index]; + if (ni >= mesh.NgonUnsignedCount()) + return; + const ON_MeshNgon* ngon = mesh.Ngon(ni); + if (nullptr == ngon || ngon->m_Fcount < 2 || nullptr == ngon->m_fi) + return; + + for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi) + { + const int fi = (int)ngon->m_fi[nfi]; + if (fi < 0 || fi >= face_count) + continue; + const ON_MeshTopologyFace& f = top.m_topf[fi]; + const int f_ecount = f.IsTriangle() ? 3 : 4; + for (int fei = 0; fei < f_ecount; ++fei) + { + const int ei = f.m_topei[fei]; + if (ei < 0 || ei >= edge_count) + continue; + const ON_MeshTopologyEdge& e = top.m_tope[ei]; + if (2 != e.m_topf_count || nullptr == e.m_topfi) + continue; + const int f1i = e.m_topfi[(fi != e.m_topfi[0]) ? 0 : 1]; + if (ni == ngon0_map[f1i]) + emarks[ei] |= interior_edge_mark; + } + } +} + +static void Internal_WeldAndAddNgon( + ON_Mesh& mesh, + const ON_MeshTopology& top, + const unsigned char xmark, + unsigned char* fmarks, + ON_SimpleArray& ngon_fi +) +{ + if (ngon_fi.Count() < 2) + return; + + // ngons must be welded and MergeFaces supports making an ngon across an unwelded edge. + Internal_WeldNgonCandiate(mesh,top,fmarks,xmark,ngon_fi); + + if ( + 2 == ngon_fi.Count() + &&ngon_fi[0] != ngon_fi[1] + && mesh.m_F[ngon_fi[0]].IsTriangle() + && mesh.m_F[ngon_fi[1]].IsTriangle() + ) + { + // make a single quad face + ON_MeshFace f[2] = {mesh.m_F[ngon_fi[0]],mesh.m_F[ngon_fi[1]]}; + for (int f0ei = 0; f0ei < 3; ++f0ei) + { + const int f0vi[2] = { f[0].vi[f0ei],f[0].vi[(f0ei + 1) % 3] }; + for (int f1ei = 0; f1ei < 3; ++f1ei) + { + const int f1vi[2] = { f[1].vi[f1ei],f[1].vi[(f1ei + 1) % 3] }; + if (f0vi[0] != f1vi[1] || f0vi[1] != f1vi[0]) + continue; + // merge triangles into a quad + ON_MeshFace q; + q.vi[f0ei] = f[0].vi[f0ei]; + q.vi[(f0ei+1)%4] = f[1].vi[(f1ei+2)%3]; + q.vi[(f0ei+2)%4] = f[0].vi[(f0ei+1)%3]; + q.vi[(f0ei+3)%4] = f[0].vi[(f0ei+2)%3]; + if (q.IsQuad() && q.IsValid(mesh.m_V.Count())) + { + mesh.m_F[ngon_fi[0]] = q; + q.vi[0] = -1; + q.vi[1] = -1; + q.vi[2] = -1; + q.vi[3] = -1; + mesh.m_F[ngon_fi[1]] = q; + ngon_fi.SetCount(1); + mesh.AddNgon(1, ngon_fi.Array()); + return; + } + } + } + } + + mesh.AddNgon(ngon_fi.UnsignedCount(), ngon_fi.Array()); +} + +unsigned int ON_Mesh::MergeFaceSets( + const ON_SimpleArray& ci_list +) +{ + const unsigned int bailout_rc = ON_UNSET_UINT_INDEX; + + const int ci_list_count = ci_list.UnsignedCount(); + if (ci_list_count <= 0) + return bailout_rc; + + const int vertex_count = VertexCount(); + const int face_count = FaceCount(); + if (face_count < 1 || vertex_count < 3) + return bailout_rc; + + + const ON_MeshTopology& top = Topology(); + if (top.m_topf.Count() != face_count) + return bailout_rc; + + const int topv_count = top.m_topv.Count(); + const int tope_count = top.m_tope.Count(); + + if (topv_count < 3 || topv_count > vertex_count || tope_count < 3) + return bailout_rc; + + ON_SimpleArray vmarks_buffer(topv_count); + vmarks_buffer.SetCount(topv_count); + vmarks_buffer.Zero(); + unsigned char* vmarks = vmarks_buffer.Array(); + + ON_SimpleArray emarks_buffer(tope_count); + emarks_buffer.SetCount(tope_count); + emarks_buffer.Zero(); + unsigned char* emarks = emarks_buffer.Array(); + + ON_SimpleArray fmarks_buffer(face_count); + fmarks_buffer.SetCount(face_count); + fmarks_buffer.Zero(); + unsigned char* fmarks = fmarks_buffer.Array(); + + + const unsigned char in_ci_list_mark = 1; + const unsigned char vmark = 2; + const unsigned char emark = 4; + const unsigned char fmark = 8; + const unsigned char xmark = 0x10; + const unsigned char v_list_mark = vmark | in_ci_list_mark; + const unsigned char e_list_mark = emark | in_ci_list_mark; + const unsigned char f_list_mark = fmark | in_ci_list_mark; + const unsigned char v_x_mark = vmark | xmark; + const unsigned char e_x_mark = emark | xmark; + //const unsigned char f_x_mark = fmark | xmark; + const unsigned char merged_mark = 0x80; // face has been merged + + ON_MeshNgonBuffer ngon_buffer; + + unsigned ngon0_count = this->NgonCount(); + const unsigned int* ngon0_map = ( ngon0_count > 0) ? this->NgonMap(true) : nullptr; + + // Mark faces, edges, and vertices referenced in ci_list[] + // by setting bits in fmarks[], emarks[], and vmarks[] + for (int i = 0; i < ci_list_count; ++i) + { + ON_COMPONENT_INDEX ci = ci_list[i]; + if (ci.m_index < 0) + continue; + switch (ci.m_type) + { + case ON_COMPONENT_INDEX::mesh_vertex: + // convert ci.m_index from a ON_COMPONENT_INDEX::mesh_vertex + // to a ON_COMPONENT_INDEX::meshtop_vertex index. + if (ci.m_index >= vertex_count) + break; + ci.m_index = top.m_topv_map[ci.m_index]; + if (ci.m_index < 0) + break; + // no break here + case ON_COMPONENT_INDEX::meshtop_vertex: + if (ci.m_index < topv_count) + { + const ON_MeshTopologyVertex& v = top.m_topv[ci.m_index]; + if (nullptr == v.m_topei || v.m_tope_count < 2) + break; + + // validate the vertex + unsigned ni[2] = { ON_UNSET_UINT_INDEX,ON_UNSET_UINT_INDEX }; + bool bValid = (v.m_tope_count >= 2 && nullptr != v.m_topei); + for (int vei = 0; vei < v.m_tope_count && bValid; ++vei) + { + int topei = v.m_topei[vei]; + bValid = (topei >= 0 && topei < tope_count); + if (bValid) + { + const ON_MeshTopologyEdge& e = top.m_tope[topei]; + bValid = (nullptr != e.m_topfi && e.m_topf_count >= 1 && e.m_topf_count <= 2); + for (int efi = 0; efi < e.m_topf_count && bValid; ++efi) + { + int fi = e.m_topfi[efi]; + bValid = (fi >= 0 && fi < face_count); + if (nullptr != ngon0_map && ni[0] == ni[1]) + { + const unsigned k = ngon0_map[fi]; + const ON_MeshNgon* ngon = (k < ngon0_count) ? Ngon(k) : nullptr; + if (nullptr != ngon && ngon->m_Fcount >= 2 && nullptr != ngon->m_fi) + { + if (ON_UNSET_UINT_INDEX == ni[0]) + ni[0] = k; + ni[1] = k; + } + else + { + // this face is not part of an ngon - stop checking + ni[0] = ON_UNSET_UINT_INDEX; + ni[1] = 0; + } + } + } + } + } + if (false == bValid) + break; + if (ON_UNSET_UINT_INDEX != ni[0] && ni[0] == ni[1]) + break; // v is inside an ngon + + vmarks[ci.m_index] |= v_list_mark; + + // add a vmark to every face touching this vertex + for (int vei = 0; vei < v.m_tope_count; ++vei) + { + const ON_MeshTopologyEdge& e = top.m_tope[v.m_topei[vei]]; + for (int efi = 0; efi < e.m_topf_count; ++efi) + Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, e.m_topfi[efi], vmark, fmarks, nullptr, nullptr); + } + } + break; + + case ON_COMPONENT_INDEX::meshtop_edge: + if (ci.m_index < tope_count) + { + const ON_MeshTopologyEdge& e = top.m_tope[ci.m_index]; + if (nullptr == e.m_topfi || 2 != e.m_topf_count) + continue; + if (e.m_topvi[0] < 0 || e.m_topvi[0] >= topv_count) + continue; + if (e.m_topvi[1] < 0 || e.m_topvi[1] >= topv_count) + continue; + if (e.m_topfi[0] < 0 || e.m_topfi[0] >= face_count) + continue; + if (e.m_topfi[1] < 0 || e.m_topfi[1] >= face_count) + continue; + + if (nullptr != ngon0_map) + { + const unsigned ni[2] = { ngon0_map[e.m_topfi[0]],ngon0_map[e.m_topfi[1]] }; + if (ni[0] < ngon0_count && ni[0] == ni[1]) + continue; // edge is inside an ngon + } + + emarks[ci.m_index] |= e_list_mark; + + // add an emark to face tounching this edge + vmarks[e.m_topvi[0]] |= emark; + vmarks[e.m_topvi[1]] |= emark; + for (int efi = 0; efi < e.m_topf_count; ++efi) + { + const int fi = e.m_topfi[efi]; + Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, emark, fmarks, nullptr, nullptr); + Internal_AddMarkToNgonInteriorEdges(*this, top, ngon0_map, fi, e_list_mark, emarks); + } + } + break; + + case ON_COMPONENT_INDEX::mesh_face: + if (ci.m_index >= face_count) + break; + if (ngon0_count > 0) + { + const unsigned ni = ngon0_map[ci.m_index]; + if (ni < ngon0_count ) + { + const ON_MeshNgon* ngon = Ngon(ni); + if (nullptr != ngon && ngon->m_Fcount > 1 && nullptr != ngon->m_fi) + { + ci.m_type = ON_COMPONENT_INDEX::mesh_ngon; + ci.m_index = (int)ni; + } + } + } + // no break here; + case ON_COMPONENT_INDEX::mesh_ngon: + if (ON_COMPONENT_INDEX::mesh_face == ci.m_type || ci.m_index < NgonCount()) + { + const ON_MeshNgon* ngon = + (ON_COMPONENT_INDEX::mesh_face == ci.m_type) + ? ngon_buffer.CreateFromMeshFaceIndex(this,ci.m_index) + : Ngon(ci.m_index); + if (nullptr == ngon || nullptr == ngon->m_fi || ngon->m_Fcount < 1) + break; + for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi) + { + int fi = ngon->m_fi[nfi]; + if (fi >= 0 && fi < face_count) + { + const ON_MeshTopologyFace& f = top.m_topf[fi]; + bool bValidFace = true; + for (int fei = 0; fei < 4 && bValidFace; fei++) + { + const int ei = f.m_topei[fei]; + bValidFace = (ei >= 0 && ei < tope_count); + if (bValidFace) + { + const ON_MeshTopologyEdge& e = top.m_tope[ei]; + for (int evi = 0; evi < 2 && bValidFace; ++evi) + { + int topvi = e.m_topvi[evi]; + bValidFace = (topvi >= 0 && topvi < topv_count); + } + } + } + + if (bValidFace) + { + fmarks[fi] |= f_list_mark; + Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, fmark, fmarks, emarks, vmarks); + } + } + } + } + break; + + default: + break; + } + } + + // Resolve multi-marked faces and remove all marked faces from ngons. + unsigned int fmark_face_count = 0; + unsigned int vmark_face_count = 0; + unsigned int emark_face_count = 0; + + for (int fi = 0; fi < face_count; ++fi) + { + if (f_list_mark == ( fmarks[fi] & f_list_mark )) + { + fmarks[fi] = (f_list_mark | xmark); + ++fmark_face_count; + } + else if (emark == (fmarks[fi] & e_list_mark)) + { + fmarks[fi] = emark; + ++emark_face_count; + } + else if (vmark == (fmarks[fi] & v_list_mark)) + { + fmarks[fi] = vmark; + ++vmark_face_count; + } + else + { + fmarks[fi] = 0; + } + } + + unsigned int emark_count = 0; + if (emark_face_count >= 2) + { + // ignore edges that are part of a boundary of a face in ci_list[] + emark_face_count = 0; + for (int ei = 0; ei < tope_count; ++ei) + { + unsigned char m = emarks[ei]; + emarks[ei] = 0; + if (e_list_mark != m) + continue; + const ON_MeshTopologyEdge& e = top.m_tope[ei]; + for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi) + { + const int fi = e.m_topfi[efi]; + if (emark != fmarks[fi] && e_x_mark != fmarks[fi]) + m = 0; + } + if (0 != m) + { + emarks[ei] = e_list_mark; + ++emark_count; + for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi) + { + const int fi = e.m_topfi[efi]; + Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, emark, fmarks, emarks, vmarks); + if (emark == fmarks[fi]) + { + fmarks[fi] |= xmark; + ++emark_face_count; + } + } + } + } + } + if (0 == emark_count) + emark_face_count = 0; + + unsigned int vmark_count = 0; + if (vmark_face_count >= 2) + { + // ignore vertices that belong to faces or edges in ci_list[] + vmark_face_count = 0; + for (int topvi = 0; topvi < topv_count; ++topvi) + { + unsigned char m = vmarks[topvi]; + vmarks[topvi] = 0; + if (v_list_mark != m) + continue; + const ON_MeshTopologyVertex& v = top.m_topv[topvi]; + for (int vei = 0; vei < v.m_tope_count && 0 != m; ++vei) + { + const int ei = v.m_topei[vei]; + if (0 != (emarks[ei] & (fmark|emark)) ) + m = 0; + else + { + const ON_MeshTopologyEdge& e = top.m_tope[ei]; + for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi) + { + const int fi = e.m_topfi[efi]; + if (vmark != fmarks[fi] && v_x_mark != fmarks[fi]) + m = 0; + } + } + } + if (0 != m) + { + vmarks[topvi] = v_list_mark; + ++vmark_count; + for (int vei = 0; vei < v.m_tope_count && 0 != m; ++vei) + { + const int ei = v.m_topei[vei]; + emarks[ei] |= vmark; + const ON_MeshTopologyEdge& e = top.m_tope[ei]; + for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi) + { + const int fi = e.m_topfi[efi]; + Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, vmark, fmarks, nullptr, vmarks); + Internal_AddMarkToNgonInteriorEdges(*this, top, ngon0_map, fi, vmark, emarks); + if (vmark == fmarks[fi]) + { + fmarks[fi] |= xmark; + ++vmark_face_count; + } + } + } + } + } + } + if (0 == vmark_count) + vmark_face_count = 0; + + unsigned max_ngon_face_count = fmark_face_count; + if (max_ngon_face_count < emark_face_count) + max_ngon_face_count = emark_face_count; + if (max_ngon_face_count < vmark_face_count) + max_ngon_face_count = vmark_face_count; + + if (max_ngon_face_count < 2 ) + return bailout_rc; + + ON_SimpleArray ngon_fi(max_ngon_face_count); + + if (ngon0_count > 0) + { + // remove ngons that include marked faces + const unsigned int* ngon_map = ( ngon0_count > 0) ? this->NgonMap(true) : nullptr; + for (int fi = 0; fi < face_count; ++fi) + { + if (0 != (fmarks[fi] & xmark)) + { + unsigned int ngon_index = (nullptr != ngon_map) ? ngon_map[fi] : ON_UNSET_UINT_INDEX; + if (ngon_index < ngon0_count) + ngon_fi.Append(ngon_index); + } + } + ngon_fi.QuickSortAndRemoveDuplicates(ON_CompareDecreasing< unsigned>); + for (int i = 0; i < ngon_fi.Count(); ++i) + this->RemoveNgon((unsigned)ngon_fi[i]); + this->RemoveEmptyNgons(); + + // Any ngons with indices >= ngon0_count are ngons added by the code below. + ngon0_count = this->NgonCount(); + + + ngon_fi.SetCount(0); + } + + // zero x mark in fmarks[] because it is used in Internal_WeldAndAddNgon() + for (int fi = 0; fi < face_count; ++fi) + fmarks[fi] &= ~xmark; + + for (int fi = 0; fi < face_count; ++fi) + { + if (f_list_mark != (fmarks[fi] & (f_list_mark | merged_mark))) + continue; // face not in ci_list[] or already merged + + // seed ngon with this face + ngon_fi.SetCount(0); + fmarks[fi] |= merged_mark; + ngon_fi.Append((unsigned)fi); + + // grow ngon by adding other faces in ci_list[] that share and edge with a face in the ngon. + Internal_GrowNgon( + top, emarks, fmarks, + 0, // etest_mask, + 0, // etest_result, + f_list_mark, // ftest_mask, + f_list_mark, // ftest_result, + merged_mark, + ngon_fi + ); + + // weld ngon vertices and then add ngon + Internal_WeldAndAddNgon(*this, top, xmark, fmarks, ngon_fi); + } + + for (int ei = 0; ei < tope_count; ++ei) + { + if ( e_list_mark != (emarks[ei] & (e_list_mark | merged_mark)) ) + continue; // edge not in ci_list[] or already merged + emarks[ei] |= merged_mark; + const ON_MeshTopologyEdge& e = top.m_tope[ei]; + + // seed ngon with faces attached to this edge + ngon_fi.SetCount(0); + for (int efi = 0; efi < e.m_topf_count; ++efi) + { + const int fi = e.m_topfi[efi]; + if (fi < 0 || fi >= face_count) + break; + if (emark != (fmarks[fi] & (emark|fmark|in_ci_list_mark|merged_mark))) + break; + fmarks[fi] |= merged_mark; + ngon_fi.Append(fi); + } + + // grow ngon by jumping accross edges in ci_list[] + Internal_GrowNgon( + top, emarks, fmarks, + e_list_mark | fmark, // etest_mask, + e_list_mark, // etest_result, + emark | fmark, // ftest_mask, + emark, // ftest_result, + merged_mark, + ngon_fi + ); + + // weld ngon vertices and then add ngon + Internal_WeldAndAddNgon(*this, top, xmark, fmarks, ngon_fi); + } + + + unsigned int mark_mask = v_list_mark | merged_mark; + for (int vi = 0; vi < topv_count; ++vi) + { + if (v_list_mark != (vmarks[vi] & mark_mask)) + continue; // edge not in ci_list[] or already merged + vmarks[vi] |= merged_mark; + const ON_MeshTopologyVertex& v = top.m_topv[vi]; + + // seed ngon with faces attached to this vertex + ngon_fi.SetCount(0); + for (int vei = 0; vei < v.m_tope_count; ++vei) + { + const ON_MeshTopologyEdge& e = top.m_tope[v.m_topei[vei]]; + for (int efi = 0; efi < e.m_topf_count; ++efi) + { + int fi = e.m_topfi[efi]; + if (fi < 0 || fi >= face_count) + continue; + if (vmark != (fmarks[fi] & (vmark|emark|fmark|in_ci_list_mark|merged_mark))) + continue; + fmarks[fi] |= merged_mark; + ngon_fi.Append(fi); + } + } + + // grow ngon by jumping across edges with a vmark + Internal_GrowNgon( + top, emarks, fmarks, + vmark, // etest_mask, + vmark, // etest_result, + vmark | emark | fmark, // ftest_mask, + vmark, // ftest_result, + merged_mark, + ngon_fi + ); + + // weld ngon vertices and then add ngon + Internal_WeldAndAddNgon(*this, top, xmark, fmarks, ngon_fi); + } + + // clean up + DeleteComponents( + nullptr, + 0, + true, // bIgnoreInvalidComponents + true, // bRemoveDegenerateFaces + true, // bRemoveUnusedVertices + true // bRemoveEmptyNgons + ); + + const unsigned ngon1_count = this->NgonCount(); + + // return index where new ngons begin + return (ngon1_count > ngon0_count) ? ngon0_count : bailout_rc; +} + diff --git a/opennurbs_mesh.h b/opennurbs_mesh.h index 3aab0f5e..eeababfb 100644 --- a/opennurbs_mesh.h +++ b/opennurbs_mesh.h @@ -17,6 +17,214 @@ #if !defined(OPENNURBS_MESH_INC_) #define OPENNURBS_MESH_INC_ +#pragma region RH_C_SHARED_ENUM [ON_SubDComponentLocation] [Rhino.Geometry.SubDComponentLocation] [byte] + /// + /// The ON_SubDComponentLocation enum is used when an ON_SubD component + /// is referenced and it is important to distinguish between the + /// component's location in the SubD control net and its location + /// in the SubD limit surface. + /// +enum class ON_SubDComponentLocation : unsigned char +{ + /// + /// Not a valid component location and used to indicate the value is not initialized. + /// + Unset = 0, + + /// + /// The component's location in the SubD control net. + /// + ControlNet = 1, + + /// + /// The component's location in the SubD limit surface. + /// + Surface = 2 +}; +#pragma endregion + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDDisplayParameters +// +// A collection of parameters that are passed to functions that +// calculate a various representations of ON_SubD objects. +// + +class ON_CLASS ON_SubDDisplayParameters +{ +public: + ON_SubDDisplayParameters() = default; + ~ON_SubDDisplayParameters() = default; + ON_SubDDisplayParameters(const ON_SubDDisplayParameters&) = default; + ON_SubDDisplayParameters& operator=(const ON_SubDDisplayParameters&) = default; + +public: + enum : unsigned int + { + CourseDensity = 2, // (2^2 = 4, 4x4 = 16 quads per SubD quad) + DefaultDensity = 4, // (2^4 = 16, 16x16 = 256 quads per SubD quad) + MaximumDensity = 6 // (2^6 = 64, 64x64 = 4096 quads per SubD quad) + }; + +public: + static const ON_SubDDisplayParameters Empty; + + // Parameters for the default limit surface display mesh. + // m_display_density = ON_SubDMesh::DefaultDisplayDensity + static const ON_SubDDisplayParameters Course; + + // Parameters for the default limit surface display mesh. + // m_display_density = ON_SubDMesh::DefaultDisplayDensity + static const ON_SubDDisplayParameters Default; + + /* + Description: + In most applications, the caller sets the mesh_density + and leaves the other parameters set to the default + values. + */ + static const ON_SubDDisplayParameters CreateFromDisplayDensity( + unsigned int display_density + ); + + // TODO - add functional interface and hide implementation + +public: + // 0 <= m_display_density <= ON_SubDDisplayParameters::MaximumDensity + // If n = m_display_density, then each SubD quad face will have + // a grid of 2^n x 2^n mesh quads for a total of 2^(2n) mesh quads. + // n grid size total number of mesh faces per SubD quad + // 0 1 x 1 1 + // 1 2 x 2 4 + // 2 4 x 4 16 + // 3 8 x 8 64 + // 4 16 x 16 128 + // 5 32 x 32 1,024 + // 6 64 x 64 4,096 + unsigned int DisplayDensity() const; + + void SetDisplayDensity( + unsigned int display_density + ); + + + /* + Description: + The MeshLocation() property determines if the mesh is + on the SubD's control net or the SubD's surface. + */ + ON_SubDComponentLocation MeshLocation() const; + + /* + Description: + The MeshLocation() property determines if the mesh is + on the SubD's control net or the SubD's surface. + Parameters: + mesh_location - [in] + */ + void SetMeshLocation(ON_SubDComponentLocation mesh_location); + + // By default, limit surface mesh quads for each subd face are grouped into + // a single ON_Mesh n-gon. + // If you don't want n-gons, then set m_bSkipMeshNgons = true; + bool AddNgons() const; + void SetAddNgons( + bool bAddNgons + ); + + // By default, limit surface mesh quads for each subd face are grouped into + // a single ON_Mesh n-gon. + // If you don't want n-gons, then set m_bSkipMeshNgons = true; + bool AddFakeEvaluationParameters() const; + void SetAddFakeEvaluationParameters( + bool bAddFakeEvaluationParameters + ); + + unsigned char EncodeAsUnsignedChar() const; + static const ON_SubDDisplayParameters DecodeFromUnsignedChar( + unsigned char encoded_parameters + ); + +private: + enum : unsigned char + { + // do not change these values - they control how m_subd_mesh_parameters is set + // and the value of m_subd_mesh_parameters is saved in 3dm archives. + subd_mesh_density_mask = 0x07, + subd_mesh_location_bit = 0x08, + subd_mesh_skip_ngons_bit = 0x10, + subd_mesh_skip_fakeevalparams_bit = 0x20, + subd_mesh_nondefault_bit = 0x80 + }; + +private: + // 0 <= m_display_density <= ON_SubDDisplayParameters::MaximumDensity + // If n = m_display_density, then each SubD quad face will have + // a grid of 2^n x 2^n mesh quads for a total of 2^(2n) mesh quads. + // n grid size total number of mesh faces per SubD quad + // 0 1 x 1 1 + // 1 2 x 2 4 + // 2 4 x 4 16 + // 3 8 x 8 64 + // 4 16 x 16 128 + // 5 32 x 32 1,024 + // 6 64 x 64 4,096 + unsigned int m_display_density = 0; + + // If m_bControlNetMesh is false, a mesh of the limit surface is produced. + // If m_bControlNetMesh is true, a mesh of the subdivided control net is produced. + bool m_bControlNetMesh = false; + + // By default, limit surface mesh quads for each subd face are grouped into + // a single ON_Mesh n-gon. + // If you don't want n-gons, then set m_bSkipMeshNgons = true; + bool m_bSkipMeshNgons = false; + + // By default, fake evaluation parameters are assigned to mesh + // vertex locations and used to set normalized texture coordinates. + // If you don't want fake evaluation parameters, then set m_bSkipFakeEvaluationParameters = true; + bool m_bSkipFakeEvaluationParameters = false; + +private: + unsigned char m_reserved2 = 0; + unsigned int m_reserved3 = 0; + unsigned int m_reserved4 = 0; + ON__UINT_PTR m_reserved5 = 0; + double m_reserved6 = 0.0; + + + // TODO - split this class into two - what's above and one derived from that with what's below. +public: + bool UseMultipleThreads() const; + void SetUseMultipleThreads( + bool bUseMultipleThreads + ); + + ON_Terminator* Terminator() const; + void SetTerminator( + ON_Terminator* terminator + ); + + ON_ProgressReporter* ProgressReporter() const; + const ON_Interval ProgressReporterInterval() const; + void SetProgressReporter( + ON_ProgressReporter* progress_reporter, + ON_Interval progress_reporter_interval + ); + +private: + bool m_bUseMultipleThreads = false; + +private: + ON_Terminator* m_terminator = nullptr; + +private: + // optional progress reporting + ON_ProgressReporter* m_progress_reporter = nullptr; + ON_Interval m_progress_reporter_interval = ON_Interval::ZeroToOne; +}; + /////////////////////////////////////////////////////////////////////////////// // // Class ON_Mesh @@ -341,6 +549,14 @@ public: bool bSimplePlanes ); +public: + void SetSubDDisplayParameters( + const class ON_SubDDisplayParameters& subd_mesh_parameters + ); + + const ON_SubDDisplayParameters SubDDisplayParameters() const; + + public: // false - skip stage 2 mesh refinement step // true - (default) do stage 2 mesh refinement step @@ -452,19 +668,6 @@ public: double minimum_edge_length ); - /* - Returns: - SubD display mesh density. - Example: - Use ON_MeshParameters to control the density of a SubD limit mesh. - ON_MeshParameters mp = ...; - ON_Mesh* mesh = subd->GetLimitSurfaceMesh( - ON_SubDDisplayParameters::CreateFromDisplayDensity( mp.SubDDisplayMeshDensity() ), - nullptr - ); - */ - unsigned int SubDDisplayMeshDensity() const; - public: // edges longer than MaximumEdgeLength() will // be split even when they meet all other @@ -584,7 +787,10 @@ private: unsigned char m_texture_range = 2; unsigned char m_face_type = 0; - unsigned char m_reserved1 = 0; + // Uses ON_SubDDisplayParameters::EncodeAsUnsignedChar() / ON_SubDDisplayParameters::DecodeFromUnsignedChar() + // to save ON_SubDDisplayParameters settings in this class. + // (Done this way to avoid breaking the C++ public SDK.) + unsigned char m_subd_mesh_parameters = 0; int m_grid_min_count = 0; int m_grid_max_count = 0; @@ -638,9 +844,11 @@ private: // END Pangolin parameters // ////////////////////////////////////////////////////////// +private: + unsigned int m_subd_stuff_reserved5 = 0; private: - ON__UINT_PTR m_reserved5 = 0; + ON__UINT_PTR m_reserved6 = 0; }; ON_DECL @@ -944,7 +1152,6 @@ public: const class ON_3dPointListRef& vertex_list, ON_PlaneEquation& face_plane_equation ) const; - }; @@ -1504,6 +1711,36 @@ public: ON_SimpleArray& ngon_vi ); + /* +Description: + Get a list of vertices that form any boundary of a set of faces. + This includes inner boundaries. +Parameters: + mesh_vertex_list - [in] + mesh_face_list - [in] + vertex_face_map - [in] + null or a vertex map made from the information in + mesh_vertex_list and mesh_face_list. + ngon_fi_count - [in] + length of ngon_fi[] array + ngon_fi - [in] + An array of length ngon_fi_count that contains the indices + of the faces that form the ngon. + ngon_vi - [out] + An array of vertex indices that make the ngon boundary. +Returns: + Number of vertices in the ngon outer boundary or 0 if the input is + not valid. +*/ + static unsigned int FindNgonBoundary( + const class ON_3dPointListRef& mesh_vertex_list, + const class ON_MeshFaceList& mesh_face_list, + const unsigned int *const* vertex_face_map, + size_t ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray& ngon_vi + ); + /* Description: Create an ngon pointer that contains a triangle (3-gon) @@ -2095,6 +2332,18 @@ public: */ bool TopFaceIsHidden( int topfi ) const; + /* + Parameters: + topei - [in] + m_tope[] index + Returns: + true if the edge + has 2 distinct vertices, + 2 or more attached faces, + and all attached faces reference the same ON_Mesh vertices along this edge. + */ + bool IsWeldedEdge( int topei ) const; + ////////// // m_topv_map[] has length m_mesh.VertexCount() and // m_topv[m_topv_map[vi]] is the topological mesh vertex that is assocated @@ -2189,6 +2438,11 @@ class ON_CLASS ON_MappingTag { public: ON_MappingTag(); + ON_MappingTag(const ON_TextureMapping& mapping,const ON_Xform* xform); + + static const ON_MappingTag Unset; + static const ON_MappingTag SurfaceParameterMapping; + void Default(); bool Write(ON_BinaryArchive&) const; bool Read(ON_BinaryArchive&); @@ -2231,10 +2485,10 @@ public: // m_mesh_xform to zero so that compares will work right. // // - ON_UUID m_mapping_id; // ON_TextureMapping::m_mapping_id - ON_TextureMapping::TYPE m_mapping_type; // ON_TextureMapping::m_type - ON__UINT32 m_mapping_crc; // ON_TextureMapping::MappingCRC() - ON_Xform m_mesh_xform; + ON_UUID m_mapping_id = ON_nil_uuid; // ON_TextureMapping::m_mapping_id + ON_TextureMapping::TYPE m_mapping_type = ON_TextureMapping::TYPE::no_mapping; // ON_TextureMapping::m_type + ON__UINT32 m_mapping_crc = 0; // ON_TextureMapping::MappingCRC() + ON_Xform m_mesh_xform = ON_Xform::IdentityTransformation; }; class ON_CLASS ON_TextureCoordinates @@ -2269,6 +2523,10 @@ public: ON_Mesh& operator=( const ON_Mesh& ); ~ON_Mesh(); +public: + static const ON_Mesh Empty; + + // Override of virtual ON_Object::MemoryRelocate void MemoryRelocate() override; @@ -2377,6 +2635,14 @@ public: */ bool IsEmpty() const; + + /* + Returns + true if there are vertices and faces. + */ + + bool IsNotEmpty() const; + // creation bool SetVertex( int, // vertex index @@ -2509,6 +2775,49 @@ public: bool bRemoveEmptyNgons ); + /* +Description: + Delete the portions of the mesh identified in ci_list[]. +Parameters: + ci_list - [in] + List of components to delete. + ci_list_count - [in] + Number of elements in the ci_list[] array. + Can be zero if you are using this function to remove + unused vertices or empty ngons. + bIgnoreInvalidComponents - [in] + If true, invalid elements in ci_list[] are ignored. + If false and ci_list[] contains an invalid element, + then no changes are made and false is returned. + bRemoveDegenerateFaces - [in] + If true, remove degenerate faces. + bCullUnusedVertices - [in] + Remove vertices that are not referenced by a face. + Pass true unless you have a good reason for keeping + unreferenced vertices. + bRemoveEmptyNgons - [in] + Remove ngons that are empty. + Pass true unless you have a good reason for keeping + empty ngons. + faceMap - [i] + If anything other than nullptr is passed in, then + faceMap[fi] is the index of the new face after the + removal of vertices, faces, etc. + This needs to be allocated to be at least m_F.Count() long. +Returns: + True: succesful + False: failure - no changes. +*/ + bool DeleteComponents( + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count, + bool bIgnoreInvalidComponents, + bool bRemoveDegenerateFaces, + bool bRemoveUnusedVertices, + bool bRemoveEmptyNgons, + unsigned int* faceMap + ); + /* Description: Calls the detailed version of DeleteComponents() with @@ -2546,6 +2855,35 @@ public: ON_COMPONENT_INDEX ci ); + /* + Description: + Merge faces like ON_SubD::MergeFaces() does. + Parameters: + ci_list - [in] + vertices, edges, and faces that trigger merging. + Returns: + If ngons were added, then the index of the first added ngon is returned. + Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int MergeFaceSets( + const ON_SimpleArray& ci_list + ); + + /* + Description: + Merge faces like ON_SubD::MergeFaces() does. + Parameters: + ci_list - [in] + vertices, edges, and faces that trigger merging. + Returns: + If ngons were added, then the index of the first added ngon is returned. + Otherwise ON_UNSET_UINT_INDEX is returned. + */ + unsigned int DissolveOrDelete( + const ON_SimpleArray& ci_list + ); + + /* Description: Copy the subset of the mesh idenfied in the component list. @@ -2785,6 +3123,14 @@ public: int CullUnusedVertices(); // returns number of culled vertices + /* + Description: + Removes degenerate and unused mesh components. + Returns: + Number of removed components; + */ + unsigned int CullDegenerates(); + // Description: // Removes any unreferenced objects from arrays, reindexes as needed, // and shrinks arrays to minimum required size. @@ -2913,6 +3259,8 @@ public: bool bIgnoreTextureCoordinates = false ); + unsigned int RemoveAllCreases(); + void Append( const ON_Mesh& ); // appends a copy of mesh to this and updates // indices of appended mesh parts @@ -3507,7 +3855,7 @@ public: in m_V[], then you must call UpdateDoublePrecisionVertices(). Remarks: If double precision vertices are not present, this function - does nothing. + creates them. */ void UpdateDoublePrecisionVertices(); @@ -3653,6 +4001,85 @@ public: ON_COMPONENT_INDEX ci ) const; + unsigned int AddNgons( + const ON_SimpleArray& ci_list + ); + + /* + Description: + Add a new ngon to the mesh. + Does not allow the creation of inner boundaries. + Parameters: + ngon_fi[] + An array of distinct ON_Mesh.m_F[] face indices referencing + a set of connected faces. + Returns: + index of the new n-gon. + -1: If input information is not valid. + */ + int AddNgon( + const ON_SimpleArray& ngon_fi + ); + + /* + Description: + Add a new ngon to the mesh. + Parameters: + ngon_fi[] + An array of distinct ON_Mesh.m_F[] face indices referencing + a set of connected faces. + bPermitHoles + If true, also ngons that contain inner boundaries are allowed. + Returns: + index of the new n-gon. + -1: If input information is not valid. + */ + int AddNgon( + const ON_SimpleArray& ngon_fi, + bool bPermitHoles + ); + + /* + Description: + Add a new ngon to the mesh. + Does not allow the creation of inner boundaries. + Parameters: + Fcount - [in] + Number of face that make up the ngon. + ngon_fi[] + An array of N distinct ON_Mesh.m_F[] face indices referencing + a set of connected faces. + Returns: + index of the new n-gon. + -1: If input information is not valid. + */ + int AddNgon( + unsigned int Fcount, + const unsigned int* ngon_fi + ); + + /* + Description: + Add a new ngon to the mesh. + Parameters: + Fcount - [in] + Number of face that make up the ngon. + ngon_fi[] + An array of N distinct ON_Mesh.m_F[] face indices referencing + a set of connected faces. + bPermitHoles + If true, also ngons that contain inner boundaries are allowed. + Returns: + index of the new n-gon. + -1: If input information is not valid. +*/ + int AddNgon( + unsigned int Fcount, + const unsigned int* ngon_fi, + bool bPermitHoles + ); + + /* Description: Add a new ngon to the mesh. @@ -4105,7 +4532,38 @@ public: - Otherwise, ngon_map[fi] = -1. */ const unsigned int* CreateNgonMap(); - + + /* + Description: + Expert user function to construct n-gon map even on const objects + after the expert user did something that made the current one invalid. + map [in-out]: + The map must have at least m_F elements + It is modified to match the new ngon map, if true is returned. + Returns: + A value indicating if the mesh has ngon information. + The map is an array of length m_F.Count(), ngon_map[] + - If ngon_map[fi] >= 0, then ON_MeshFace.m_F[fi] belongs + to ON_Mesh.Ngon(ngon_map[fi]). + - Otherwise, ngon_map[fi] = -1. + */ + bool CreateNgonMap(unsigned int* ngon_map) const; + + + /* +Description: + Expert user function to construct n-gon map even on const objects + after the expert user did something that made the current one invalid. +map [out]: + The map is modified to match the new ngon map, if true is returned. +Returns: +A value indicating if the mesh has ngon information. +The map is an array of length m_F.Count(), ngon_map[] + - If ngon_map[fi] >= 0, then ON_MeshFace.m_F[fi] belongs + to ON_Mesh.Ngon(ngon_map[fi]). + - Otherwise, ngon_map[fi] = -1. +*/ + bool CreateNgonMap(ON_SimpleArray& map) const; /* Description: @@ -4353,6 +4811,115 @@ private: bool SwapEdge_Helper( int, bool ); }; +////////////////////////////////////////////////////////////////////////// +// +// ON_MeshRef +// +class ON_CLASS ON_MeshRef +{ +public: + static const ON_MeshRef Empty; + + ON_MeshRef() ON_NOEXCEPT; + ~ON_MeshRef(); + ON_MeshRef(const ON_MeshRef& src) ON_NOEXCEPT; + ON_MeshRef& operator=(const ON_MeshRef& src); + +#if defined(ON_HAS_RVALUEREF) + // rvalue copy constructor + ON_MeshRef( ON_MeshRef&& ) ON_NOEXCEPT; + // rvalue assignment operator + ON_MeshRef& operator=( ON_MeshRef&& ); +#endif + + const class ON_Mesh& Mesh() const; + + /* + Returns: + Number of references to the managed ON_Mesh, including the one by this ON_MeshRef. + */ + unsigned int ReferenceCount() const; + + /* + Description: + Allocates a new empty ON_Mesh and has this ON_MeshRef reference it. + */ + class ON_Mesh& NewMesh(); + + /* + Description: + Allocates a new ON_Mesh and has this ON_MeshRef reference it. + Parameters: + src - [in] + The new ON_Mesh managed by this ON_MeshRef will be a copy of src.Mesh(). + Returns: + A reference to the new ON_Mesh managed by this ON_MeshRef. + */ + class ON_Mesh& CopyMesh( + const ON_MeshRef& src + ); + + /* + Description: + Allocates a new ON_Mesh and has this ON_MeshRef reference it. + Parameters: + src - [in] + The new ON_Mesh managed by this ON_MeshRef will be a copy of src. + Returns: + A reference to the new ON_Mesh managed by this ON_MeshRef. + */ + class ON_Mesh& CopyMesh( + const ON_Mesh& src + ); + + /* + Description: + If ReferenceCount() > 1, then have this ON_MeshRef reference a + new copy. Otherwise do nothing. The result being that this will + be the unique reference to the ON_Mesh managed by this ON_MeshRef. + Returns: + A reference to the ON_Mesh uniquely managed by this ON_MeshRef. + */ + class ON_Mesh& UniqueMesh(); + + /* + Description: + Remove this reference to the managed ON_Mesh. + If this is the last reference, then the managed ON_Mesh is deleted. + */ + void Clear(); + +public: + /* + Description: + Expert user function to have this ON_MeshRef manage mesh. + Parameters: + mesh - [in/out] + mesh must point to an ON_Mesh that was constructed on the heap using + an operator new call with a public ON_Mesh constructor. + Returns: + a pointer to the managed mesh + Example: + ON_Mesh* mesh = new ON_Mesh(...); + ON_MeshRef mesh_ref; + ON_Mesh* managed_mesh = mesh_ref.SetMesh(mesh); + // mesh = nullptr + // managed_mesh = pointer you can use + */ + class ON_Mesh* SetMeshForExperts( + class ON_Mesh*& mesh + ); + +private: +#pragma ON_PRAGMA_WARNING_PUSH +#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) + // C4251: ... needs to have dll-interface to be used by clients of class ... + // m_mesh_sp is private and all code that manages m_mesh_sp is explicitly implemented in the DLL. +private: + std::shared_ptr m_mesh_sp; +#pragma ON_PRAGMA_WARNING_POP +}; + ////////////////////////////////////////////////////////////////////////// // // ON_MeshCache diff --git a/opennurbs_mesh_ngon.cpp b/opennurbs_mesh_ngon.cpp index 5df77950..bf397b2d 100644 --- a/opennurbs_mesh_ngon.cpp +++ b/opennurbs_mesh_ngon.cpp @@ -26,6 +26,8 @@ const class ON_MeshNgon* ON_MeshNgonBuffer::CreateFromMeshFaceIndex( const class ON_MeshFace* mesh_face = ( mesh && face_index < mesh->m_F.UnsignedCount() ) ? &mesh->m_F[face_index] : 0; + if (nullptr == mesh_face || false == mesh_face->IsValid(mesh->m_V.Count())) + return nullptr; return CreateFromMeshFace(mesh_face,face_index); } @@ -762,7 +764,7 @@ const unsigned int* ON_Mesh::NgonMap( return fdex_to_ndex_map; } -const unsigned int* ON_Mesh::CreateNgonMap() +bool ON_Mesh::CreateNgonMap(ON_SimpleArray& map) const { unsigned int* ngon_map = 0; const ON_MeshNgon* ngon; @@ -770,17 +772,18 @@ const unsigned int* ON_Mesh::CreateNgonMap() unsigned int ngon_index; unsigned int j; - m_NgonMap.SetCount(0); - const unsigned int ngon_count = m_Ngon.UnsignedCount(); const unsigned int meshFcount = m_F.UnsignedCount(); - if ( meshFcount <= 0 ) - return 0; + if (meshFcount <= 0) + { + map.SetCount(0); + return false; + } - m_NgonMap.Reserve(meshFcount); - m_NgonMap.SetCount(meshFcount); - ngon_map = m_NgonMap.Array(); + map.Reserve(meshFcount); + map.SetCount(meshFcount); + ngon_map = map.Array(); for ( fi = 0; fi < meshFcount; fi++ ) ngon_map[fi] = ON_UNSET_UINT_INDEX; @@ -806,9 +809,28 @@ const unsigned int* ON_Mesh::CreateNgonMap() } } - return ngon_map; + return true; } +bool ON_Mesh::CreateNgonMap(unsigned int* map) const +{ + ON_SimpleArray new_map {}; + new_map.SetArray(map, m_F.Count(), m_F.Count()); + + if (!CreateNgonMap(new_map)) return false; + new_map.KeepArray(); + return true; +} + +const unsigned int* ON_Mesh::CreateNgonMap() +{ + if (!CreateNgonMap(m_NgonMap)) return nullptr; + + return m_NgonMap.Array(); +} + + + bool ON_Mesh::ModifyNgon( unsigned int ngon_index, unsigned int Vcount, @@ -894,6 +916,335 @@ bool ON_Mesh::ModifyNgon( return false; } +static bool Internal_ListContainNgon( + unsigned int count, + const int* fi, + ON_SimpleArray< const ON_MeshNgon* >& tested_ngons, + const ON_MeshNgon* ngon +) +{ + if (nullptr == ngon || ngon->m_Fcount <= 0) + return true; // nothing to test. + + if (ngon->m_Fcount > count) + return false; + + if (tested_ngons.Search(ngon) >= 0) + return true; // already tested this ngon + + // fi[] is a sorted list + for (unsigned int k = 0; k < ngon->m_Fcount; ++k) + { + if (nullptr == ON_BinarySearchIntArray((int)ngon->m_fi[k], fi, count)) + return false; // ngon->m_fi[k] is not in fi[] + } + + if (ngon->m_Fcount > 1) + tested_ngons.Append(ngon); // no need to test this one again + + // Every index in ngon->m_fi[] is also in fi[] + return true; +} + +unsigned int ON_Mesh::AddNgons( + const ON_SimpleArray& ci_list +) +{ + const int ci_count = ci_list.UnsignedCount(); + if (ci_count < 2) + return 0; + + const int face_count = FaceCount(); + if (face_count < 2) + return 0; + const int ngon_count = NgonCount(); + const unsigned int ngon_ucount = NgonUnsignedCount(); + + ///////////////////// + // + // fi_list[] = list of faces to be merged into ngons + ON_SimpleArray fi_list(ci_count); + ON_COMPONENT_INDEX ci; + for (int i = 0; i < ci_count; ++i) + { + ci = ci_list[i]; + if (ci.m_index < 0) + continue; + switch (ci.m_type) + { + case ON_COMPONENT_INDEX::TYPE::mesh_face: + if (ci.m_index < face_count) + fi_list.Append(ci.m_index); + break; + case ON_COMPONENT_INDEX::TYPE::mesh_ngon: + if (ci.m_index < ngon_count) + { + const ON_MeshNgon* ngon = Ngon(ci.m_index); + if (nullptr == ngon) + break; + for (unsigned j = 0; j < ngon->m_Fcount; ++j) + { + const int fi = ngon->m_fi[j]; + if (fi >= 0 && fi < face_count) + fi_list.Append(fi); + } + } + break; + default: + break; + } + } + fi_list.QuickSortAndRemoveDuplicates(ON_CompareIncreasing); + const int fi_count = fi_list.Count(); + if (fi_count < 2) + return 0; + + ///////////////////// + // + // Partition fi_list[] into subsets where each subset will be a new ngon. + const ON_MeshTopology& top = Topology(); + if (face_count != top.m_topf.Count()) + return 0; + + const int edge_count = top.TopEdgeCount(); + + ON_SimpleArray face_available_buffer(face_count); + face_available_buffer.SetCount(face_count); + face_available_buffer.Zero(); + bool* face_available = face_available_buffer.Array(); + + for (int i = 0; i < fi_count; ++i) + face_available[fi_list[i]] = true; + + ///////////////////// + // + // ngon_faces[] is the same set of indiced that are in fi_list[], but with indices for + // each new ngon grouped together. + // ngon_partition[] is used to find the new ngon subsets. + // If i0 = ngon_partition[j] && i1 = ngon_partition[j+1], then + // (ngon_faces[i0],...,ngon_faces[i1-1]) are the face indices for a new ngon. + // + ON_SimpleArray ngon_faces(fi_count); + ON_SimpleArray ngon_partition(64); + + const unsigned int* ngon_map = (ngon_count > 0) ? NgonMap(true) : nullptr; + + ON_SimpleArray< const ON_MeshNgon* > old_ngons_inside_new_ngon(32); + + for (int i = 0; i < fi_count; ++i) + { + const int face_index = fi_list[i]; + if (false == face_available[face_index]) + continue; // face is no longer available. + + // find the new ngon contain face_index. + face_available[face_index] = false; + const unsigned int count0 = ngon_faces.UnsignedCount(); + unsigned int i0 = 0; + unsigned int i1 = count0; + ngon_partition.Append(count0); + ngon_faces.Append(face_index); + for (;;) + { + i0 = i1; + i1 = ngon_faces.Count(); + if (i0 >= i1) + break; + for (/*empty init*/; i0 < i1; ++i0) + { + const int fi = ngon_faces[i0]; + const ON_MeshTopologyFace& topf = top.m_topf[fi]; + const int fe_count = topf.IsQuad() ? 4 : 3; + for (int fei = 0; fei < fe_count; ++fei) + { + int ei = topf.m_topei[fei]; + if (ei < 0 || ei >= edge_count) + continue; + const ON_MeshTopologyEdge& tope = top.m_tope[ei]; + if (2 != tope.m_topf_count) + continue; + if (2 != tope.m_topf_count || false == top.IsWeldedEdge(ei)) + continue; + int neighbor_fi = tope.m_topfi[(fi == tope.m_topfi[0]) ? 1 : 0]; + if (neighbor_fi < 0 || neighbor_fi >= face_count || false == face_available[neighbor_fi]) + continue; + face_available[neighbor_fi] = false; + ngon_faces.Append(neighbor_fi); + } + } + } + + const unsigned new_ngon_F_count = ngon_faces.UnsignedCount() - count0; + int* new_ngon_fi = ngon_faces.Array() + count0; + ON_SortIntArray(ON::sort_algorithm::quick_sort, new_ngon_fi, new_ngon_F_count); + bool bSkipNewNgon = (new_ngon_F_count < 2); + if (false == bSkipNewNgon && nullptr != ngon_map) + { + old_ngons_inside_new_ngon.SetCount(0); + + // Make sure the new ngon doesn't contain proper subsets of existing ngons. + for (unsigned j = 0; j < new_ngon_F_count; ++j) + { + int fi = new_ngon_fi[j]; + const unsigned int ngon_dex = ngon_map[fi]; + if (ngon_dex >= ngon_ucount) + continue; + const ON_MeshNgon* existing_ngon = Ngon(ngon_dex); + if (nullptr == existing_ngon) + continue; + if (false == Internal_ListContainNgon(new_ngon_F_count, new_ngon_fi, old_ngons_inside_new_ngon, existing_ngon)) + { + // existing ngon has faces that are not in new ngon. + bSkipNewNgon = true; + break; + } + if (existing_ngon->m_Fcount == new_ngon_F_count) + { + // new ngon and existing ngon are the same. + bSkipNewNgon = true; + break; + } + } + } + if (bSkipNewNgon) + { + ngon_faces.SetCount(count0); + ngon_partition.Remove(); + } + } + + if (ngon_partition.UnsignedCount() < 1 || ngon_faces.UnsignedCount() < 2) + return 0; + + ngon_partition.Append(ngon_faces.UnsignedCount()); + + bool bRemoveddNgons = false; + if (nullptr != ngon_map) + { + // Delete ngons that are being merged into bigger ngons + ON_SimpleArray delete_old_ngon(ngon_count); + delete_old_ngon.SetCount(ngon_count); + for (int j = 0; j < ngon_faces.Count(); ++j) + { + unsigned ngon_dex = ngon_map[ngon_faces[j]]; + if (ngon_dex < ngon_ucount) + delete_old_ngon[ngon_dex] = true; + } + for (int j = ngon_count; j > 0; --j) + { + unsigned int ngon_dex = (unsigned int)(j - 1); + if (delete_old_ngon[ngon_dex]) + { + RemoveNgon(ngon_dex); + bRemoveddNgons = true; + } + } + } + + // Add new ngons + unsigned int new_ngon_count = 0; + unsigned int i1 = ngon_partition[0]; + for (unsigned int i = 1; i < ngon_partition.UnsignedCount(); ++i) + { + const unsigned int i0 = i1; + i1 = ngon_partition[i]; + if (i0 + 2 <= i1) + { + if (AddNgon(i1 - i0, (const unsigned int*)(ngon_faces.Array() + i0)) >= 0) + ++new_ngon_count; + } + } + + if (bRemoveddNgons || new_ngon_count > 0) + { + // clean up ngon storage + m_NgonMap.SetCount(0); + int count = 0; + for (int i = 0; i < m_Ngon.Count(); ++i) + { + ON_MeshNgon* n = m_Ngon[i]; + if (nullptr == n) + continue; + m_Ngon[count++] = n; + } + m_Ngon.SetCount(count); + NgonMap(true); + } + + return new_ngon_count; +} + +int ON_Mesh::AddNgon(const ON_SimpleArray& ngon_fi) +{ + return AddNgon(ngon_fi.UnsignedCount(), ngon_fi.Array()); +} + +int ON_Mesh::AddNgon(const ON_SimpleArray& ngon_fi, bool bPermitHoles) +{ + return AddNgon(ngon_fi.UnsignedCount(), ngon_fi.Array(), bPermitHoles); +} + +int ON_Mesh::AddNgon( + unsigned int Fcount, + const unsigned int* ngon_fi +) +{ + return AddNgon(Fcount, ngon_fi, false); +} + +int ON_Mesh::AddNgon( + unsigned int Fcount, + const unsigned int* ngon_fi, + bool bPermitHoles +) +{ + unsigned int ngon_index = ON_UNSET_UINT_INDEX; + if (Fcount < 1 || nullptr == ngon_fi) + return ngon_index; + + ON_SimpleArray ngon_vi; + const ON_3dPointListRef mesh_vertex_list(this); + const ON_MeshFaceList& mesh_face_list(this); + const unsigned int *const* vertex_face_map = nullptr; + + const int face_count = m_F.Count(); + const unsigned int ngon_count0 = HasNgons() ? NgonUnsignedCount() : 0U; + bool bCheckNgonMap = (ngon_count0 > 0 && face_count == m_NgonMap.Count()); + for (unsigned int i = 0; i < Fcount; ++i) + { + unsigned int fi = ngon_fi[i]; + if (fi >= (unsigned int)face_count) + return ngon_index; + if (bCheckNgonMap && m_NgonMap[fi] < ngon_count0) + return ngon_index; // input faces are already in an ngon + } + + unsigned int vi_count; + if (bPermitHoles) + vi_count = ON_MeshNgon::FindNgonBoundary( + mesh_vertex_list, + mesh_face_list, + vertex_face_map, + Fcount, + ngon_fi, + ngon_vi + ); + else + vi_count = ON_MeshNgon::FindNgonOuterBoundary( + mesh_vertex_list, + mesh_face_list, + vertex_face_map, + Fcount, + ngon_fi, + ngon_vi + ); + + if (vi_count < 3 || ngon_vi.Count() < 3) + return ngon_index; // + + return AddNgon(ngon_vi.UnsignedCount(), ngon_vi.Array(), Fcount, ngon_fi); +} + int ON_Mesh::AddNgon( unsigned int Vcount, const unsigned int* ngon_vi, @@ -3660,26 +4011,27 @@ const ON_MeshNgon* ON_Mesh::NgonFromComponentIndex( return ngon; } -unsigned int ON_MeshNgon::FindNgonOuterBoundary( +static unsigned int FindNgonBoundary_Helper( const ON_3dPointListRef& mesh_vertex_list, const ON_MeshFaceList& mesh_face_list, const unsigned int *const* vertex_face_map, size_t ngon_fi_count, const unsigned int* ngon_fi, - ON_SimpleArray& ngon_vi - ) + ON_SimpleArray& ngon_vi, + bool permitOnlyOneBoundary +) { const unsigned int mesh_vertex_count = mesh_vertex_list.PointCount(); const unsigned int ngon_boundary_index = 1; unsigned int boundary_edge_count; - for(;;) + for (;;) { - if ( mesh_vertex_count <= 0 || ON_UNSET_UINT_INDEX == mesh_vertex_count ) + if (mesh_vertex_count <= 0 || ON_UNSET_UINT_INDEX == mesh_vertex_count) break; - if ( ngon_fi_count <= 0 || 0 == ngon_fi ) + if (ngon_fi_count <= 0 || 0 == ngon_fi) break; ON_SimpleArray ngon_nbr_map; @@ -3692,14 +4044,14 @@ unsigned int ON_MeshNgon::FindNgonOuterBoundary( (unsigned int)ngon_fi_count, ngon_fi, ngon_nbr_map.Array() - ); + ); - if ( boundary_edge_count <= 0 ) + if (boundary_edge_count <= 0) break; ngon_vi.SetCount(0); ngon_vi.Reserve(boundary_edge_count); - if ( !GetNgonBoundarySegments( + if (!GetNgonBoundarySegments( mesh_face_list, (unsigned int)ngon_fi_count, ngon_fi, @@ -3707,10 +4059,10 @@ unsigned int ON_MeshNgon::FindNgonOuterBoundary( ngon_nbr_map.Array(), &ngon_vi, 0 - )) + )) break; - if ( boundary_edge_count != ngon_vi.UnsignedCount() ) + if (permitOnlyOneBoundary && boundary_edge_count != ngon_vi.UnsignedCount()) break; // inner boundaries exist - ngon has holes return ngon_vi.UnsignedCount(); @@ -3721,6 +4073,47 @@ unsigned int ON_MeshNgon::FindNgonOuterBoundary( return 0; } + +unsigned int ON_MeshNgon::FindNgonOuterBoundary( + const ON_3dPointListRef& mesh_vertex_list, + const ON_MeshFaceList& mesh_face_list, + const unsigned int *const* vertex_face_map, + size_t ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray& ngon_vi + ) +{ + return FindNgonBoundary_Helper( + mesh_vertex_list, + mesh_face_list, + vertex_face_map, + ngon_fi_count, + ngon_fi, + ngon_vi, + true + ); +} + +unsigned int ON_MeshNgon::FindNgonBoundary( + const ON_3dPointListRef& mesh_vertex_list, + const ON_MeshFaceList& mesh_face_list, + const unsigned int *const* vertex_face_map, + size_t ngon_fi_count, + const unsigned int* ngon_fi, + ON_SimpleArray& ngon_vi +) +{ + return FindNgonBoundary_Helper( + mesh_vertex_list, + mesh_face_list, + vertex_face_map, + ngon_fi_count, + ngon_fi, + ngon_vi, + false + ); +} + unsigned int ON_MeshNgon::GetBoundarySides( const class ON_MeshFaceList& mesh_face_list, ON_SimpleArray& ngon_boundary_sides @@ -4660,7 +5053,7 @@ ON_Plane ON_Plane::FromPointList( // // Find a plane to project the 3d points to. // - ON_3dVector N(ON_3dVector::UnsetVector); + ON_3dVector N(ON_3dVector::ZeroVector); ON_3dVector X(ON_3dVector::UnsetVector); if ( point_index_count <= 4 ) { @@ -4680,7 +5073,8 @@ ON_Plane ON_Plane::FromPointList( if ( X.Length() < Pi1.DistanceTo(Pi0) ) X = Pi1-Pi0; } - else + + if (N == ON_3dVector::ZeroVector) { // find far apart points unsigned int i0 = 0; @@ -4774,7 +5168,7 @@ ON_Plane ON_Plane::FromPointList( -const ON_MeshNgonIterator ON_MeshNgonIterator::EmptyMeshNgonIterator; +const ON_MeshNgonIterator ON_MeshNgonIterator::EmptyMeshNgonIterator ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_MeshNgonIterator); ON_MeshNgonIterator::ON_MeshNgonIterator( const class ON_Mesh* mesh diff --git a/opennurbs_nurbscurve.cpp b/opennurbs_nurbscurve.cpp index 0f96f59d..3332e792 100644 --- a/opennurbs_nurbscurve.cpp +++ b/opennurbs_nurbscurve.cpp @@ -30,44 +30,80 @@ ON_OBJECT_IMPLEMENT(ON_NurbsCurve,ON_Curve,"4ED7D4DD-E947-11d3-BFE5-0010830122F0 Description: Helper to make a deep copy (duplicate memory) from src to dest */ -static void ON_NurbsCurve_copy_member_data( const ON_NurbsCurve& src, ON_NurbsCurve& dest ) +void ON_NurbsCurve::Internal_DeepCopyFrom( const ON_NurbsCurve& src ) { - int i; - dest.m_dim = src.m_dim; - dest.m_is_rat = src.m_is_rat; - dest.m_order = src.m_order; - dest.m_cv_count = src.m_cv_count; - dest.m_cv_stride = dest.m_is_rat ? dest.m_dim+1 : dest.m_dim; - if ( src.m_knot ) + if (this == &src) { - // copy knot array - i = dest.KnotCount(); - dest.ReserveKnotCapacity( i ); - memcpy( dest.m_knot, src.m_knot, i*sizeof(*dest.m_knot) ); + ON_ERROR("this and &src must be different."); + return; } - if ( src.m_cv ) + + // erase tags + m_knot_capacity_and_tags &= ON_NurbsCurve::masks::knot_capacity; + + const int knot_capacity = (nullptr != src.m_knot) ? src.KnotCount() : 0; + if (knot_capacity > 0) { - // copy cv array - dest.ReserveCVCapacity( dest.m_cv_stride*dest.m_cv_count ); - const int dst_cv_size = dest.CVSize()*sizeof(*dest.m_cv); - const int src_stride = src.m_cv_stride; - const int dst_stride = dest.m_cv_stride; - const double *src_cv = src.CV(0); - double *dst_cv = dest.m_cv; - if ( src_stride == dst_stride ) + ReserveKnotCapacity(knot_capacity); + if ( nullptr != m_knot ) + memcpy( m_knot, src.m_knot, knot_capacity*sizeof(m_knot[0]) ); + } + else if (nullptr != m_knot && KnotCapacity() > 0) + { + onfree(m_knot); + m_knot = nullptr; + m_knot_capacity_and_tags = 0; + } + + int cv_count = (src.m_cv_count>0) ? src.m_cv_count : 0; + int cv_stride = (src.m_dim > 0) ? ((src.m_is_rat) ? (src.m_dim + 1) : src.m_dim) : 0; + const int cv_capacity = (nullptr != src.m_cv) ? (cv_count*cv_stride) : 0; + if (cv_capacity > 0) + { + ReserveCVCapacity(cv_capacity); + if ( nullptr != m_cv ) { - memcpy( dst_cv, src_cv, dest.m_cv_count*dst_stride*sizeof(*dst_cv) ); - } - else - { - for ( i = 0; i < dest.m_cv_count; i++ ) + // copy cv array + const int src_stride = src.m_cv_stride; + const double *src_cv = src.m_cv; + double *dst_cv = m_cv; + if (src_stride == cv_stride) { - memcpy( dst_cv, src_cv, dst_cv_size ); - dst_cv += dst_stride; - src_cv += src_stride; + memcpy(dst_cv, src_cv, cv_capacity * sizeof(dst_cv[0])); + } + else + { + const size_t sizeof_cv = (size_t)(cv_stride * sizeof(dst_cv[0])); + for (int i = 0; i < cv_count; i++) + { + memcpy(dst_cv, src_cv, sizeof_cv); + dst_cv += cv_stride; + src_cv += src_stride; + } } } } + else + { + if (nullptr != m_cv && m_cv_capacity > 0) + { + onfree(m_cv); + m_cv = nullptr; + } + m_cv_capacity = 0; + cv_count = 0; + cv_stride = 0; + } + + m_dim = src.m_dim; + m_is_rat = src.m_is_rat; + m_order = src.m_order; + m_cv_count = cv_count; + m_cv_stride = cv_stride; + + // copy tags + const unsigned int src_tags = (src.m_knot_capacity_and_tags & ON_NurbsCurve::masks::all_tags); + m_knot_capacity_and_tags |= src_tags; } /* @@ -75,17 +111,22 @@ Description: Helper to move (shallow copy) from src to dest */ #if defined(ON_HAS_RVALUEREF) -static void ON_NurbsCurve_move_member_data( const ON_NurbsCurve& src, ON_NurbsCurve& dest ) +void ON_NurbsCurve::Internal_ShallowCopyFrom( const ON_NurbsCurve& src ) { - dest.m_dim = src.m_dim; - dest.m_is_rat = src.m_is_rat; - dest.m_order = src.m_order; - dest.m_cv_count = src.m_cv_count; - dest.m_knot_capacity = src.m_knot_capacity; - dest.m_knot = src.m_knot; - dest.m_cv_stride = src.m_cv_stride; - dest.m_cv_capacity = src.m_cv_capacity; - dest.m_cv = src.m_cv; + if (this == &src) + { + ON_ERROR("this and &src must be different."); + return; + } + m_dim = src.m_dim; + m_is_rat = src.m_is_rat; + m_order = src.m_order; + m_cv_count = src.m_cv_count; + m_knot_capacity_and_tags = src.m_knot_capacity_and_tags; + m_knot = src.m_knot; + m_cv_stride = src.m_cv_stride; + m_cv_capacity = src.m_cv_capacity; + m_cv = src.m_cv; } #endif @@ -93,28 +134,28 @@ static void ON_NurbsCurve_move_member_data( const ON_NurbsCurve& src, ON_NurbsCu Description: Helper to clear (set all member vars to zero) */ -static void ON_NurbsCurve_zero_member_data( ON_NurbsCurve& dest ) +void ON_NurbsCurve::Internal_InitializeToZero() { - dest.m_dim = 0; - dest.m_is_rat = 0; - dest.m_order = 0; - dest.m_cv_count = 0; - dest.m_knot_capacity = 0; - dest.m_knot = 0; - dest.m_cv_stride = 0; - dest.m_cv_capacity = 0; - dest.m_cv = 0; + m_dim = 0; + m_is_rat = 0; + m_order = 0; + m_cv_count = 0; + m_knot_capacity_and_tags = 0; + m_knot = 0; + m_cv_stride = 0; + m_cv_capacity = 0; + m_cv = 0; } /* Description: Helper to make a deep copy (duplicate memory) from src to dest */ -static void ON_NurbsCurve_delete_member_data( ON_NurbsCurve& dest ) +void ON_NurbsCurve::Internal_Destroy() { - double* cv = ( dest.m_cv && dest.m_cv_capacity ) ? dest.m_cv : 0; - double* knot = ( dest.m_knot && dest.m_knot_capacity ) ? dest.m_knot : 0; - ON_NurbsCurve_zero_member_data(dest); + double* cv = (nullptr != m_cv && CVCapacity() > 0 ) ? m_cv : nullptr; + double* knot = (nullptr != m_knot && KnotCapacity() > 0 ) ? m_knot : nullptr; + Internal_InitializeToZero(); if ( cv ) onfree(cv); if ( knot ) @@ -124,23 +165,24 @@ static void ON_NurbsCurve_delete_member_data( ON_NurbsCurve& dest ) ON_NurbsCurve::ON_NurbsCurve() ON_NOEXCEPT { ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr); - ON_NurbsCurve_zero_member_data(*this); + Internal_InitializeToZero(); } ON_NurbsCurve::ON_NurbsCurve( const ON_NurbsCurve& src ) : ON_Curve(src) { ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr); - ON_NurbsCurve_zero_member_data(*this); - ON_NurbsCurve_copy_member_data(src,*this); + Internal_InitializeToZero(); + Internal_DeepCopyFrom(src); } ON_NurbsCurve& ON_NurbsCurve::operator=( const ON_NurbsCurve& src ) { if ( this != &src ) { + Internal_Destroy(); // consider removing this so "expert" knots and cv can be used. ON_Curve::operator=(src); - ON_NurbsCurve_copy_member_data(src,*this); + Internal_DeepCopyFrom(src); } return *this; } @@ -151,18 +193,18 @@ ON_NurbsCurve::ON_NurbsCurve( ON_NurbsCurve&& src) ON_NOEXCEPT : ON_Curve(std::move(src)) { ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr); - ON_NurbsCurve_move_member_data(src,*this); - ON_NurbsCurve_zero_member_data(src); + Internal_ShallowCopyFrom(src); + src.Internal_InitializeToZero(); } ON_NurbsCurve& ON_NurbsCurve::operator=( ON_NurbsCurve&& src) { if ( this != &src ) { - ON_NurbsCurve_delete_member_data(*this); + Internal_Destroy(); ON_Curve::operator=(std::move(src)); - ON_NurbsCurve_move_member_data(src,*this); - ON_NurbsCurve_zero_member_data(src); + Internal_ShallowCopyFrom(src); + src.Internal_InitializeToZero(); } return *this; } @@ -221,8 +263,8 @@ unsigned int ON_NurbsCurve::SizeOf() const { unsigned int sz = ON_Curve::SizeOf(); sz += (sizeof(*this) - sizeof(ON_Curve)); - sz += m_knot_capacity*sizeof(*m_knot); - sz += m_cv_capacity*sizeof(*m_cv); + sz += KnotCapacity()*sizeof(*m_knot); + sz += CVCapacity()*sizeof(*m_cv); return sz; } @@ -404,18 +446,18 @@ bool ON_NurbsCurve::Create( void ON_NurbsCurve::Destroy() { DestroyCurveTree(); - ON_NurbsCurve_delete_member_data(*this); + Internal_Destroy(); } void ON_NurbsCurve::EmergencyDestroy() { - ON_NurbsCurve_zero_member_data(*this); + Internal_InitializeToZero(); } void ON_NurbsCurve::Initialize() { - ON_NurbsCurve_zero_member_data(*this); + Internal_InitializeToZero(); } ON_NurbsCurve& ON_NurbsCurve::operator=(const ON_BezierCurve& src) @@ -724,7 +766,8 @@ bool ON_NurbsCurve::Write( ) const { // NOTE - check legacy I/O code if changed - bool rc = file.Write3dmChunkVersion(1,0); + const int minor_version = (file.Archive3dmVersion() >= 60); + bool rc = file.Write3dmChunkVersion(1,minor_version); if (rc) { if (rc) rc = file.WriteInt( m_dim ); @@ -763,6 +806,12 @@ bool ON_NurbsCurve::Write( } } + if (rc && minor_version >= 1) + { + // April 2019 Chunk version 1.1 - added SubDFriendlyTag + const bool bSubDFriendlyTag = SubDFriendlyTag(); + rc = file.WriteBool(bSubDFriendlyTag); + } } return rc; } @@ -825,6 +874,15 @@ bool ON_NurbsCurve::Read( rc = file.ReadDouble( cv_size, CV(i) ); } } + + if (rc && minor_version >= 1) + { + // April 2019 Chunk version 1.1 - added SubDFriendlyTag + bool bSubDFriendlyTag = false; + rc = file.ReadBool(&bSubDFriendlyTag); + if (bSubDFriendlyTag) + SetSubDFriendlyTag(bSubDFriendlyTag); + } } if ( !rc ) Destroy(); @@ -870,6 +928,109 @@ bool ON_NurbsCurve::SetDomain( double t0, double t1 ) return rc; } + +static ON_NurbsCurve* MoveSeamPeriodicKnot(const ON_NurbsCurve& crv, int knot_index) + +{ + int dg = crv.Degree(); + if (dg < 2) + return 0; + if (!crv.IsPeriodic()) + return 0; + if (knot_index < dg || knot_index >= crv.KnotCount()-dg) + return 0; + int sc = crv.SpanCount(); + if (sc < crv.KnotCount()-2*dg+1)//Not all knots are simple + return 0; + const double* knots = &crv.m_knot[dg-1]; + int distinct_cvc = crv.CVCount()-dg; + double dom_len = crv.Domain().Length(); + ON_NurbsCurve* pNC = ON_NurbsCurve::New(crv); + int curr = dg-1; + for (int i=knot_index; iSetKnot(curr, pNC->Knot(i)); + curr++; + } + + for (int i=0; i<=knot_index-dg+1; i++){ + pNC->SetKnot(curr, knots[i] + dom_len); + curr++; + } + + for (int i=0; iSetKnot(curr+i, pNC->Knot(curr+i-1)+pNC->Knot(dg+i) - pNC->Knot(dg+i-1)); + pNC->SetKnot(dg-2-i, pNC->Knot(dg-i-1) - pNC->Knot(curr-1-i) + pNC->Knot(curr-2-i)); + } + + int cv_id = knot_index-dg+1; + for (int i=0; iCVCount(); i++){ + ON_4dPoint cv; + crv.GetCV(cv_id%distinct_cvc, cv); + pNC->SetCV(i, cv); + cv_id++; + } + + return pNC; +} + + +static ON_NurbsCurve* MoveSeamPeriodic(const ON_Curve& crv, double t) + +{ + ON_NurbsCurve nc; + double s = t; + const ON_NurbsCurve*pNC = ON_NurbsCurve::Cast(&crv); + if (pNC) + nc = *pNC; + else { + if (!crv.GetNurbFormParameterFromCurveParameter(t, &s)) + return 0; + if (!crv.GetNurbForm(nc)) + return 0; + } + if (!nc.IsPeriodic()) + return 0; + //Make sure all interior knots are simple + + int sc = nc.SpanCount(); + if (sc < nc.KnotCount()-2*nc.Degree()+1)//Not all knots are simple + return 0; + + int knot_index = -1; + for (int i=0; i s){ + knot_index = i; + break; + } + } + if (knot_index < nc.Degree() || knot_index > nc.KnotCount()-nc.Degree()) + return 0; + + double k0 = nc.Knot(knot_index-1); + double d0 = s-k0; + double k1 = nc.Knot(knot_index); + double d1 = k1-s; + bool bInsert = true; + if (d0<=d1){ + if (d0 < ON_ZERO_TOLERANCE*k0){ + knot_index--; + bInsert = false; + } + } + else { + if (d1 < ON_ZERO_TOLERANCE*k1) + bInsert = false; + } + + if (bInsert && !nc.InsertKnot(s, 1)) + return 0; + + return MoveSeamPeriodicKnot(nc, knot_index); + +} + + + bool ON_NurbsCurve::ChangeClosedCurveSeam( double t ) { bool rc = IsClosed(); @@ -890,8 +1051,16 @@ bool ON_NurbsCurve::ChangeClosedCurveSeam( double t ) if ( old_dom.Includes(k,true) ) { ON_NurbsCurve left, right; - // TODO - if periodic - dont' put multi knot in middle - //if ( IsPeriodic() ){} else + // TODO - if periodic - dont' put multi knot in middle. + bool bGotIt = false; + if ( IsPeriodic() ){//This only works if the curve has only simple knots + ON_NurbsCurve* pMovedNC = MoveSeamPeriodic(*this, t); + if (pMovedNC){ + *this = *pMovedNC; + delete pMovedNC; + } + } + if (!bGotIt) { ON_Curve* pleft = &left; ON_Curve* pright = &right; @@ -2034,9 +2203,11 @@ bool ON_NurbsCurve::ZeroCVs() { bool rc = false; int i; - if ( m_cv ) { - if ( m_cv_capacity > 0 ) { - memset( m_cv, 0, m_cv_capacity*sizeof(*m_cv) ); + if ( m_cv ) + { + if ( CVCapacity() > 0 ) + { + memset( m_cv, 0, CVCapacity()*sizeof(*m_cv) ); if ( m_is_rat ) { for ( i = 0; i < m_cv_count; i++ ) { SetWeight( i, 1.0 ); @@ -2067,78 +2238,124 @@ bool ON_NurbsCurve::IsClamped( // determine if knot vector is clamped return ON_IsKnotVectorClamped( m_order, m_cv_count, m_knot, end ); } +bool ON_NurbsCurve::IsNatural(int end) const +{ + bool rc = false; + ON_3dPoint CV0, CV2, P; + ON_3dVector D1, D2; + const ON_Interval domain(Domain()); + for (int pass = ((0==end||2==end) ? 0 : 1); pass < ((1==end||2==end)?2 : 1); ++pass) + { + ON_BezierCurve span; + if (false == ConvertSpanToBezier(((0 == pass) ? 0 : (m_cv_count - m_order)), span) || span.m_order < 2) + return false; + int i0 = (0 == pass) ? 0 : span.m_order - 1; + int di = (0 == pass) ? 1 : -1; + if (span.m_order > 2) + di *= 2; + if (false == span.GetCV(i0, CV0)) + return false; + if (false == span.GetCV(i0 + di, CV2)) + return false; + Ev2Der(domain[pass], P, D1, D2, (0 == pass) ? 1 : -1); + const double d2 = D2.Length(); + const double tol = CV0.DistanceTo(CV2)*1.0e-8; + if (false == (d2 <= tol)) + return false; + rc = true; + } + return rc; +} + +int ON_NurbsCurve::CVCapacity() const +{ + // If m_cv_capacity > 0, then m_cv[] was allocated by onmalloc(sz)/onrealloc(...,sz) + // where sz = m_cv_capacity*sizeof(double). + return m_cv_capacity; // number of doubles in m_cv +} bool ON_NurbsCurve::ReserveCVCapacity(int desired_capacity) { - // If m_cv_capacity == 0 and m_cv != nullptr, then the user - // has hand built the ON_NurbsCurve.m_cv array and is responsible - // for making sure it's always big enough. - bool rc = true; - if ( desired_capacity > m_cv_capacity ) { - if ( !m_cv ) { - // no cv array - allocate one - m_cv = (double*)onmalloc(desired_capacity*sizeof(*m_cv)); - if ( !m_cv ) { - rc = false; - } - else { - m_cv_capacity = desired_capacity; - } - } - else if ( m_cv_capacity > 0 ) { - // existing m_cv[] is too small and the fact that - // m_cv_capacity > 0 indicates that ON_NurbsCurve is - // managing the m_cv[] memory, so we need to grow - // the m_cv[] array. - m_cv = (double*)onrealloc(m_cv,desired_capacity*sizeof(*m_cv)); - if ( !m_cv ) { - rc = false; - m_cv_capacity = 0; - } - else { - m_cv_capacity = desired_capacity; - } - } + if (nullptr != m_cv && 0 == m_cv_capacity) + { + // If m_cv_capacity == 0 and m_cv != nullptr, then the "expert" user + // has hand built the ON_NurbsCurve.m_cv array and is responsible + // for making sure it's always big enough. + return true; } + + const int current_capacity = (nullptr != m_cv && m_cv_capacity > 0) ? m_cv_capacity : 0; + if ( desired_capacity > current_capacity ) + { + m_cv = (0 == current_capacity) + ? (double*)onmalloc(desired_capacity*sizeof(*m_cv)) + : (double*)onrealloc(m_cv,desired_capacity*sizeof(*m_cv)); + m_cv_capacity = ( nullptr != m_cv ) ? desired_capacity : 0; + } + + const bool rc = (m_cv_capacity >= desired_capacity); return rc; } +int ON_NurbsCurve::KnotCapacity() const +{ + const int knot_capacity + = (nullptr == m_knot) + ? 0 + : ((int)(m_knot_capacity_and_tags & ON_NurbsCurve::masks::knot_capacity)); + return knot_capacity; +} + +void ON_NurbsCurve::UnmanageKnotForExperts( + int& knot_capacity, + double*& knot +) +{ + knot_capacity = KnotCapacity(); + knot = m_knot; + m_knot_capacity_and_tags &= ON_NurbsCurve::masks::all_tags; // knot_capacity = 0; + m_knot = nullptr; +} + +void ON_NurbsCurve::ManageKnotForExperts( + int knot_capacity, + double* knot +) +{ + // Unconditionally set m_knot and KnotCapacity(). + // (Do not free preexisting m_knot.) + const unsigned int tags = (m_knot_capacity_and_tags & ON_NurbsCurve::masks::all_tags); + const unsigned int new_knot_capacity = (knot_capacity > 0) ? (((unsigned int)knot_capacity) & ON_NurbsCurve::masks::knot_capacity) : 0U; + m_knot_capacity_and_tags = (tags | new_knot_capacity); + m_knot = knot; +} + bool ON_NurbsCurve::ReserveKnotCapacity(int desired_capacity) { - // If m_knot_capacity == 0 and m_knot != nullptr, then the user - // has hand built the ON_NurbsCurve.m_knot array and is responsible - // for making sure it's always big enough. - bool rc = true; - if ( desired_capacity > m_knot_capacity ) { - if ( !m_knot ) { - // no knot array - allocate one - m_knot = (double*)onmalloc(desired_capacity*sizeof(*m_knot)); - if ( !m_knot ) { - m_knot_capacity = 0; - rc = false; - } - else { - m_knot_capacity = desired_capacity; - } - } - else if ( m_knot_capacity > 0 ) { - // existing m_knot[] is too small and the fact that - // m_knot_capacity > 0 indicates that ON_NurbsCurve is - // managing the m_knot[] memory, so we need to grow - // the m_knot[] array. - m_knot = (double*)onrealloc(m_knot,desired_capacity*sizeof(*m_knot)); - if ( !m_knot ) { - rc = false; - m_knot_capacity = 0; - } - else { - m_knot_capacity = desired_capacity; - } - } + const int current_knot_capacity = KnotCapacity(); + + if (nullptr != m_knot && 0 == current_knot_capacity) + { + // If knot_capacity == 0 and m_knot != nullptr, then the "expert" user + // has hand built the ON_NurbsCurve.m_knot array and is responsible + // for making sure it's always big enough. + return true; } + + if ( desired_capacity > current_knot_capacity ) + { + double* knot + = ( 0 == current_knot_capacity ) + ? (double*)onmalloc(desired_capacity*sizeof(knot[0])) + : (double*)onrealloc(m_knot,desired_capacity*sizeof(knot[0])); + this->ManageKnotForExperts((nullptr != knot) ? desired_capacity : 0, knot); + } + + bool rc = (nullptr != m_knot && KnotCapacity() >= desired_capacity); return rc; } + bool ON_NurbsCurve::ConvertSpanToBezier( int span_index, ON_BezierCurve& bez ) const { bool rc = false; @@ -2506,7 +2723,7 @@ static bool IncrementNurbDegree(ON_NurbsCurve& N) i+=mult; } //zero out N's cvs - memset(N.m_cv, 0, N.m_cv_capacity*sizeof(double)); + memset(N.m_cv, 0, N.CVCapacity()*sizeof(double)); int cvdim = N.CVSize(); const double* cvM; double* cvN; @@ -2595,9 +2812,12 @@ bool ON_NurbsCurve::ChangeDimension( int desired_dimension ) //const int cv_size0 = CVSize(); const int cv_size1 = m_is_rat ? desired_dimension + 1 : desired_dimension; const int cv_stride1 = (m_cv_stride < cv_size1) ? cv_size1 : m_cv_stride; - if ( m_cv_stride < cv_stride1 && m_cv_capacity > 0 ) { - m_cv_capacity = cv_stride1*m_cv_count; - m_cv = (double*)onrealloc( m_cv, m_cv_capacity*sizeof(*m_cv) ); + if ( m_cv_stride < cv_stride1 && CVCapacity() > 0 ) + { + const int new_cv_capacity = cv_stride1*m_cv_count; + m_cv = (double*)onrealloc( m_cv, new_cv_capacity*sizeof(*m_cv) ); + if (nullptr != m_cv) + m_cv_capacity = new_cv_capacity; } for ( i = CVCount()-1; i >= 0; i-- ) { cv0 = CV(i); @@ -3058,7 +3278,7 @@ int ON_NurbsCurve::GetNurbForm( ON_NurbsCurve& curve, // 4 May 2007 Dale Lear // I'm replacing the call to operator= with a call to - // ON_NurbsCurveCopyHelper(). The operator= call + // Internal_DeepCopyFrom(). The operator= call // was copying userdata and that does not happen for // any other GetNurbForm overrides. Copying userdata // in GetNurbForm is causing trouble in Make2D and @@ -3068,8 +3288,10 @@ int ON_NurbsCurve::GetNurbForm( ON_NurbsCurve& curve, // curve = *this; // copied user data curve.DestroyRuntimeCache(true); - if ( this != &curve ) - ON_NurbsCurve_copy_member_data(*this,curve); // does not copy user data + if (this != &curve) + { + curve.Internal_DeepCopyFrom(*this); // does not copy user data + } if ( subdomain ) { @@ -4140,3 +4362,236 @@ int ON_NurbsCurve::RemoveSingularSpans() return singular_span_count; } + +bool ON_NurbsCurve::SubDFriendlyTag() const +{ + if (0 != (m_knot_capacity_and_tags & ON_NurbsCurve::masks::subdfriendly_tag)) + { + if (IsSubDFriendly(true)) + return true; + // Something modified NURBS properties after the tag was set. + const_cast(this)->SetSubDFriendlyTag(false); + } + return false; +} + +void ON_NurbsCurve::SetSubDFriendlyTag( + bool bSubDFriendlyTag +) +{ + if (bSubDFriendlyTag && IsSubDFriendly(true)) + m_knot_capacity_and_tags |= ON_NurbsCurve::masks::subdfriendly_tag; + else + m_knot_capacity_and_tags &= ~ON_NurbsCurve::masks::subdfriendly_tag; +} + +static bool Internal_IsSubDFriendlyEnd( + int endex, + const double* cubic_cpan_knots, + const ON_3dPoint& cv0, + const ON_3dPoint& cv1, + const ON_3dPoint& cv2 +) +{ + ON_3dPoint Q; + if (0 == endex) + { + Q = (cubic_cpan_knots[0] == cubic_cpan_knots[2] && cubic_cpan_knots[3] < cubic_cpan_knots[5]) + ? ((2.0*cv0 + cv2) / 3.0) + : (0.5*(cv0 + cv2)) + ; + } + else + { + Q = (cubic_cpan_knots[0] < cubic_cpan_knots[2] && cubic_cpan_knots[3] == cubic_cpan_knots[5]) + ? ((cv0 + 2.0*cv2) / 3.0) + : (0.5*(cv0 + cv2)) + ; + } + const double d = cv0.DistanceTo(cv2); + const double tol = 1.0e-6*d; + const double e = cv1.DistanceTo(Q); + return (e <= tol); +} + +bool ON_NurbsCurve::IsSubDFriendly( + bool bPermitCreases +) const +{ + for (;;) + { + if (m_dim <= 0) + break; + if (false != m_is_rat) + break; + if (4 != m_order) + break; + if (m_cv_count < 4) + break; + if (nullptr == m_knot) + break; + if (m_cv_stride < m_dim) + break; + if (nullptr == m_cv) + break; + + // knots must be piecewise uniform + const ON_SubD::SubDFriendlyKnotType knot_type = ON_SubD::NurbsKnotType(m_order, m_cv_count, m_knot); + if (ON_SubD::SubDFriendlyKnotType::Unset == knot_type || ON_SubD::SubDFriendlyKnotType::Unfriendly == knot_type) + return false; + if (ON_SubD::SubDFriendlyKnotType::ClampedPiecewiseUniform == knot_type && false == bPermitCreases) + return false; + + ON_3dPoint CV[2] = { ON_3dPoint::NanPoint,ON_3dPoint::NanPoint }; + if (false == GetCV(0, CV[0])) + break; + if (false == GetCV(1, CV[1])) + break; + bool bAllEqual = CV[0] == CV[1]; + if (bAllEqual) + { + // All CVs must be equal - curve is a "point" used in lofting to a point. + for (int i = 2; bAllEqual && i < m_cv_count; ++i) + { + bAllEqual = GetCV(i, CV[1]) && CV[0] == CV[1]; + } + if (false == bAllEqual) + break; + } + + // curve must be periodic or natural + bool bNaturalEnds = (m_cv_count >= 6 && IsPeriodic()); + if ( false == bNaturalEnds) + { + // test ends for natural end conditions + ON_3dPoint P[3]; + bNaturalEnds = true; + for (int endex = 0; endex < 2 && bNaturalEnds; ++endex) + { + const int cv_dex = (0 == endex) ? 0 : ( CVCount() - 3); + for (int j = 0; j < 3 && bNaturalEnds; ++j) + bNaturalEnds = GetCV(cv_dex+j, P[j]); + if (false == bNaturalEnds) + break; + const double* span_knots = m_knot + ((0 == endex) ? 0 : (m_cv_count - m_order)); + bNaturalEnds = Internal_IsSubDFriendlyEnd(endex, span_knots, P[0], P[1], P[2]); + } + } + + if ( bNaturalEnds && ON_SubD::SubDFriendlyKnotType::ClampedPiecewiseUniform == knot_type ) + { + // Interior triple knots must be natural on either side of the triple knot. + // Interior triple knots are used to place kinks inside the curve. + ON_3dPoint P[5]; + const unsigned int knot_count = (unsigned int)KnotCount(); + const double* knot = m_knot; + for (unsigned int knot_dex = 3; knot_dex < knot_count - 4U && bNaturalEnds; ++knot_dex) + { + if (knot[knot_dex] == knot[knot_dex + 1]) + { + // This tests that the 2nd derivative is zero when evaluated from both below and above knot[knot_dex]. + bNaturalEnds = + knot[knot_dex - 1] < knot[knot_dex] + // tested above to enter this scope // && knot[knot_dex] == knot[knot_dex+1] + && knot[knot_dex + 1] == knot[knot_dex + 2] + && knot[knot_dex + 2] < knot[knot_dex + 3] + && GetCV(knot_dex - 2, P[0]) + && GetCV(knot_dex - 1, P[1]) + && GetCV(knot_dex, P[2]) + && GetCV(knot_dex + 1, P[3]) + && GetCV(knot_dex + 2, P[4]) + && Internal_IsSubDFriendlyEnd(1,knot+knot_dex-3, P[0], P[1], P[2]) + && Internal_IsSubDFriendlyEnd(0,knot+knot_dex, P[2], P[3], P[4]); + ++knot_dex; + } + } + } + + if (false == bNaturalEnds) + break; + + return true; + } + + return false; +} + +ON_SubD::SubDFriendlyKnotType ON_SubD::NurbsKnotType( + int order, + int cv_count, + const double* knots +) +{ + return ON_SubD::NurbsKnotType(order, cv_count, knots, nullptr); +} + +ON_SubD::SubDFriendlyKnotType ON_SubD::NurbsKnotType( + int order, + int cv_count, + const double* knots, + ON_SimpleArray* triple_knots + ) +{ + if (nullptr != triple_knots) + triple_knots->SetCount(0); + for (;;) + { + if (4 != order || cv_count < order || nullptr == knots) + break; + + const double delta = knots[3] - knots[2]; + if (false == (delta > 0.0)) + break; + + const double deltatol = ON_SQRT_EPSILON * delta; + + const bool bClamped0 = knots[0] == knots[1] && knots[1] == knots[2]; + const bool bClamped1 = knots[cv_count - 1] == knots[cv_count] && knots[cv_count] == knots[cv_count + 1]; + if (bClamped0 && nullptr != triple_knots) + triple_knots->Append(knots[2]); + + const bool bClamped = bClamped0 && bClamped1; + const bool bUnclamped = + (false == bClamped) + && fabs(knots[1] - knots[0] - delta) <= deltatol + && fabs(knots[2] - knots[1] - delta) <= deltatol + && fabs(knots[cv_count] - knots[cv_count - 1] - delta) <= deltatol + && fabs(knots[cv_count + 1] - knots[cv_count] - delta) <= deltatol + ; + if ( bClamped == bUnclamped) + break; + + bool bPiecewiseUniform = false; + for (int knot_dex = 3; knot_dex < cv_count - 1; ++knot_dex) + { + const double d = knots[knot_dex + 1] - knots[knot_dex]; + if (0.0 == d) + { + if (bClamped && knots[knot_dex + 2] == knots[knot_dex]) + { + bPiecewiseUniform = true; + ++knot_dex; + if (bClamped1 && nullptr != triple_knots) + triple_knots->Append(knots[knot_dex]); + continue; + } + } + else + { + if (fabs(d - delta) <= deltatol) + continue; + } + return ON_SubD::SubDFriendlyKnotType::Unfriendly; + } + + if (bClamped1 && nullptr != triple_knots) + triple_knots->Append(knots[cv_count - 1]); + + return + bClamped + ? (bPiecewiseUniform ? ON_SubD::SubDFriendlyKnotType::ClampedPiecewiseUniform : ON_SubD::SubDFriendlyKnotType::ClampedUniform) + : ON_SubD::SubDFriendlyKnotType::UnclampedUniform; + } + + return ON_SubD::SubDFriendlyKnotType::Unfriendly; +} diff --git a/opennurbs_nurbscurve.h b/opennurbs_nurbscurve.h index 926447de..6952f343 100644 --- a/opennurbs_nurbscurve.h +++ b/opennurbs_nurbscurve.h @@ -941,9 +941,31 @@ public: double delta = 1.0 ); - bool IsClamped( // determine if knot vector is clamped - int = 2 // end to check: 0 = start, 1 = end, 2 = start and end + /* + Description: + Test the knot vector to see if it is clamped. + Parameters: + end: + 0: test start + 1: test end + 2: test start and end. + */ + bool IsClamped( + int end = 2 ) const; + + /* + Description: + Test the start or end of a curve to see if it's natural (Zero 2nd derivative). + Parameters: + end: + 0: test start + 1: test end + 2: test start and end. + */ + bool IsNatural( + int end = 2 + ) const; double SuperfluousKnot( int // 0 = start, 1 = end @@ -1002,10 +1024,58 @@ public: bool ReserveCVCapacity( int // number of doubles to reserve ); + + /* + Returns: + If this class is managing m_cv, then CVCapacity() is the number of doubles + m_cv[] can accomodate. Otherwise, CVCapacity() is 0. + */ + int CVCapacity() const; + bool ReserveKnotCapacity( int // number of doubles to reserve ); + /* + Returns: + If this class is managing m_knot, then KnotCapacity() is the number of doubles + m_knot[] can accomodate. Otherwise, KnotCapacity() is 0. + */ + int KnotCapacity() const; + + /* + Description: + Unconditionally transfer knot managment to caller and zero m_knot and KnotCapacity(). + Parameters: + knot_capacity - [out] + knot_capacity is set to input value of KnotCapacity() and then KnotCapacity() is set to 0. + knot - [out] + knot is set to input value of m_knot and then this->m_knot is set to nullptr. + Remarks: + If knot_capacity > 0, then knot points to memory allocated by onmalloc()/onrealloc(). + */ + void UnmanageKnotForExperts( + int& knot_capacity, + double*& knot + ); + + /* + Description: + Unconditionally transfer knot management to this NURBS curve. + Sets KnotCapacity() to knot_capacity and m_knot to knot. + If knot_capacity > 0, then knot must point to memory allocated by onmalloc()/onrealloc(). + Parameters: + knot_capacity - [in] + KnotCapacity() is set to knot_capacity. + knot - [in] + m_knot is set to knot. + */ + void ManageKnotForExperts( + int knot_capacity, + double* knot + ); + + ////////// // returns the length of the control polygon double ControlPolygonLength() const; @@ -1139,6 +1209,42 @@ public: bool Reparameterize( double c ); + public: + /* + Returns: + True if this curve was explicitly tagged as SubDFriendly and is currently SubDFriendly. + */ + bool SubDFriendlyTag() const; + + public: + /* + Returns: + True if this NURBS curve is cubic, non-rational, uniform, and is either periodic or has clamped end knots. + Parameters: + bPermitCreases - [in] + If true, then a curve with clamped end knots may have interior triple knots. + Remarks: + The value of SubDFriendlyTag() is ignored. + See Also: + SubDFriendlyTag(). + */ + bool IsSubDFriendly( + bool bPermitCreases + ) const; + + /* + Description: + Set the curve's SubDFriendlyTag() property. + Parameters: + bSubDFriendlyTag - [in] + If bSubDFriendlyTag is true and IsSubDFriendly(true) is true, + then the SubDFriendlyTag() property is set to true. + Otherwise the SubDFriendlyTag property is set to false. + */ + void SetSubDFriendlyTag( + bool bSubDFriendlyTag + ); + ///////////////////////////////////////////////////////////////// // Implementation @@ -1167,17 +1273,24 @@ public: // knot vector memory - int m_knot_capacity; // If m_knot_capacity > 0, then m_knot[] - // is an array of at least m_knot_capacity - // doubles whose memory is managed by the - // ON_NurbsCurve class using onmalloc(), - // onrealloc(), and onfree(). - // If m_knot_capacity is 0 and m_knot is - // not nullptr, then m_knot[] is assumed to - // be big enough for any requested operation - // and m_knot[] is not deleted by the - // destructor. +private: + enum masks : unsigned int + { + knot_capacity = 0x0FFFFFFFU, + subdfriendly_tag = 0x80000000U, + all_tags = 0xF0000000U, + }; + unsigned int m_knot_capacity_and_tags; + // unsigned int tags = (m_knot_capacity_and_tags & ON_NurbsCurve::masks::tags); + // unsigned intknot_capacity = (m_knot_capacity_and_tags & ON_NurbsCurve::masks::knot_capacity_mask); + // If knot_capacity > 0, then m_knot[] is an array of at least knot_capacity + // doubles whose memory is managed by the ON_NurbsCurve class using + // onmalloc(), onrealloc(), and onfree(). + // If knot_capacity is 0 and m_knot is not nullptr, then m_knot[] is assumed to + // be big enough for any requested operation and m_knot[] is not deleted by the + // destructor. +public: double* m_knot; // Knot vector. ( The knot vector has length // m_order+m_cv_count-2. ) @@ -1186,6 +1299,7 @@ public: int m_cv_stride; // The pointer to start of "CV[i]" is // m_cv + i*m_cv_stride. +public: int m_cv_capacity; // If m_cv_capacity > 0, then m_cv[] is an array // of at least m_cv_capacity doubles whose // memory is managed by the ON_NurbsCurve @@ -1195,6 +1309,7 @@ public: // for any requested operation and m_cv[] is not // deleted by the destructor. +public: double* m_cv; // Control points. // - The i-th control point begins at // CV(i) = m_cv + (i*m_cv_stride). @@ -1203,6 +1318,17 @@ public: // - If m_is_rat is true, then the i-th control // point is stored in HOMOGENEOUS form and is // [ CV(i)[0], ..., CV(i)[m_dim] ]. + +private: + #if defined(ON_HAS_RVALUEREF) + void Internal_ShallowCopyFrom(const ON_NurbsCurve& src); + #endif +private: + void Internal_DeepCopyFrom(const ON_NurbsCurve& src); +private: + void Internal_InitializeToZero(); +private: + void Internal_Destroy(); }; /* Adjust the second point to be within the domains, when the first point is diff --git a/opennurbs_nurbssurface.cpp b/opennurbs_nurbssurface.cpp index e1f4b395..74fbd870 100644 --- a/opennurbs_nurbssurface.cpp +++ b/opennurbs_nurbssurface.cpp @@ -1017,7 +1017,7 @@ static bool FromCurve( ON_NurbsCurve& crv, { onfree( srf.m_cv ); } - srf.m_cv_capacity = crv.m_cv_capacity; + srf.m_cv_capacity = crv.CVCapacity(); srf.m_cv = crv.m_cv; crv.m_cv_capacity = 0; crv.m_cv = 0; @@ -1027,10 +1027,12 @@ static bool FromCurve( ON_NurbsCurve& crv, } srf.m_order[dir] = crv.m_order; srf.m_cv_count[dir] = crv.m_cv_count; - srf.m_knot_capacity[dir] = crv.m_knot_capacity; - srf.m_knot[dir] = crv.m_knot; - crv.m_knot_capacity = 0; - crv.m_knot = 0; + // transfer knot vector from crv to srf.m_knot[dir] + crv.UnmanageKnotForExperts( + srf.m_knot_capacity[dir], + srf.m_knot[dir] + ); + srf.m_cv_stride[dir] = crv.m_cv_stride; srf.m_cv_stride[1-dir] = srf_cv_size; return true; @@ -2209,6 +2211,165 @@ bool ON_NurbsSurface::IsClamped( // determine if knot vector is clamped return rc; } +bool ON_NurbsSurface::IsNatural(int dir, int end) const +{ + if (dir < 0 || dir > 1 || end < 0 || end > 2) + return false; + + const ON_Interval domain = Domain(dir); + size_t parameter_count = 0; + double parameter_list[2] = {ON_DBL_QNAN,ON_DBL_QNAN}; + if (0 == end || 2 == end) + parameter_list[parameter_count++] = domain[0]; + if (1 == end || 2 == end) + parameter_list[parameter_count++] = domain[1]; + return ON_NurbsSurface::IsNatural(dir, parameter_count, parameter_list); +} + +bool ON_NurbsSurface::IsNatural( + int dir, + size_t parameter_count, + const double* parameter_list +) const +{ + if (dir < 0 || dir > 1 || parameter_count < 1 || nullptr == parameter_list) + return false; + + const int degree_dir = Degree(dir); + if (degree_dir < 1) + return false; + + const ON_Interval domain[2] = { Domain(0), Domain(1) }; + ON_SimpleArray g(m_cv_count[1 - dir]); + const int g_count = m_cv_count[1 - dir]; + g.SetCount(g_count); + if (false == ON_GetGrevilleAbcissae( // get Greville abcissae from knots + m_order[1 - dir], + m_cv_count[1 - dir], + m_knot[1 - dir], + false, + g.Array() + )) + return false; + + int gdex0 = 0; + int gdex1 = g_count; + if (0 == dir) + { + // 0 = south, 1 = east, 2 = north, 3 = west + if (IsSingular(0)) + ++gdex0; // skip south side singular check - 2nd der is tiny and noisy + if (IsSingular(2)) + --gdex1; // skip north side singular check - 2nd der is tiny and noisy + } + else + { + // 0 = south, 1 = east, 2 = north, 3 = west + if (IsSingular(3)) + ++gdex0; // skip west side singular check - 2nd der is tiny and noisy + if (IsSingular(1)) + --gdex1; // skip east side singular check - 2nd der is tiny and noisy + } + while (gdex0 < gdex1 && false == domain[1 - dir].Includes(g[gdex0])) + ++gdex0; + while (gdex0 < gdex1 && false == domain[1 - dir].Includes(g[gdex1-1])) + --gdex1; + + if (gdex0 >= gdex1) + return false; + + const int knot_count_dir = KnotCount(dir); + int quadrant; + int cv0dex[2] = {}; + int cv2dex[2] = {}; + int hint[2] = { 0,0 }; + double st[2] = { ON_DBL_QNAN, ON_DBL_QNAN }; + ON_3dVector D1[2], D2[2], Duv; + ON_3dPoint CV0, CV2, P; + + bool bIsNatural = false; + for (size_t tdex = 0; tdex < parameter_count; ++tdex) + { + const double t = parameter_list[tdex]; + if (false == domain[dir].Includes(t)) + return false; + for (int side = -1; side <= 1; side += 2) + { + const int span_index = ON_NurbsSpanIndex(m_order[dir], m_cv_count[dir], m_knot[dir], t, side, hint[dir]); + if (span_index < 0 || span_index + 2*degree_dir > knot_count_dir) + return false; + const ON_Interval span_domain(m_knot[dir][span_index + degree_dir-1], m_knot[dir][span_index + degree_dir]); + if (false == span_domain.Includes(t)) + return false; + cv0dex[dir] = span_index; + if (t == span_domain[0]) + { + quadrant = 1; + cv2dex[dir] = cv0dex[dir] + 2; + } + else if (t == span_domain[1]) + { + quadrant = (0 == dir) ? 2 : 4; + cv0dex[dir] += degree_dir; + cv2dex[dir] = cv0dex[dir] - 2; + } + else + { + cv2dex[dir] = cv0dex[dir] + 3; + quadrant = 0; + } + + if ( cv0dex[0] < 0 || cv0dex[0] >= m_cv_count[0] || cv0dex[1] < 0 || cv0dex[1] >= m_cv_count[1] ) + { + // bug in this code or invalid surface + ON_ERROR("cv0dex out of bounds"); + return false; + } + if ( cv2dex[0] < 0 || cv2dex[0] >= m_cv_count[0] || cv2dex[1] < 0 || cv2dex[1] >= m_cv_count[1] ) + { + // bug in this code or invalid surface + ON_ERROR("cv1dex out of bounds"); + return false; + } + + st[dir] = t; + hint[dir] = span_index; + hint[1 - dir] = 0; + for (int gi = gdex0; gi < gdex1; gi++) + { + st[1 - dir] = g[gi]; + if (false == domain[1 - dir].Includes(st[1 - dir])) + continue; + if (false == Ev2Der(st[0], st[1], P, D1[0], D1[1], D2[0], Duv, D2[1], quadrant, hint)) + return false; + cv0dex[1 - dir] = gi; + cv2dex[1 - dir] = gi; + if (false == GetCV(cv0dex[0], cv0dex[1], CV0)) + return false; + if (false == GetCV(cv2dex[0], cv2dex[1], CV2)) + return false; + const double d2 = D2[dir].Length(); + const double tol = CV0.DistanceTo(CV2)*1.0e-8; + if (false == (d2 <= tol)) + return false; + bIsNatural = true; + } + if (false == bIsNatural) + return false; + + if (-1 == side && t == span_domain[1] && t < domain[dir][1]) + { + if (degree_dir >= 3 && m_knot[dir][span_index + degree_dir] < m_knot[dir][span_index + degree_dir + 2]) + break; // other side evaluations are equal + continue; + } + break; + } + } + + return bIsNatural; +} + static void ConvertToCurve( const ON_NurbsSurface& srf, int dir, ON_NurbsCurve& crv ) { // DO NOT MAKE THIS FUNCTION PUBLIC - IT IS DELICATE AND DEDICATED TO USE IN THIS FILE @@ -2315,10 +2476,9 @@ static void ConvertFromCurve( ON_NurbsCurve& crv, int dir, ON_NurbsSurface& srf srf.m_knot[dir] = 0; srf.m_knot_capacity[dir] = 0; } - srf.m_knot[dir] = crv.m_knot; - srf.m_knot_capacity[dir] = crv.m_knot_capacity; - crv.m_knot = 0; - crv.m_knot_capacity = 0; + + // transfer crv.m_knot to srf.m_knot[dir] + crv.UnmanageKnotForExperts(srf.m_knot_capacity[dir], srf.m_knot[dir]); } } @@ -2359,10 +2519,12 @@ bool ON_NurbsSurface::InsertKnot( else { ON_NurbsCurve crv; - crv.m_knot = m_knot[dir]; - crv.m_knot_capacity = m_knot_capacity[dir]; - m_knot[dir] = 0; + + // transfer knot vector from srf.m_knot[dir] to crv + crv.ManageKnotForExperts(m_knot_capacity[dir], m_knot[dir]); + m_knot[dir] = nullptr; m_knot_capacity[dir] = 0; + crv.ReserveKnotCapacity(CVCount(dir)+knot_multiplicity); ConvertToCurve(*this,dir,crv); rc = crv.InsertKnot(knot_value,knot_multiplicity); @@ -2512,10 +2674,12 @@ bool ON_NurbsSurface::IncreaseDegree( else { ON_NurbsCurve crv; - crv.m_knot = m_knot[dir]; - crv.m_knot_capacity = m_knot_capacity[dir]; + + // transfer knot vector from srf.m_knot[dir] to crv + crv.ManageKnotForExperts(m_knot_capacity[dir], m_knot[dir]); m_knot[dir] = 0; m_knot_capacity[dir] = 0; + ConvertToCurve(*this,dir,crv); rc = crv.IncreaseDegree(desired_degree); ConvertFromCurve(crv,dir,*this); @@ -2857,9 +3021,9 @@ int ON_NurbsSurface::CreateRuledSurface( if ( m_knot[0] && m_knot_capacity[0] > 0 ) onfree(m_knot[0]); - m_knot[0] = nurbs_curveA.m_knot; - m_knot_capacity[0] = nurbs_curveA.m_knot_capacity; - nurbs_curveA.m_knot_capacity = 0; + + // transfer knot vector from nurbs_curveA to srf.m_knot[0] + nurbs_curveA.UnmanageKnotForExperts(m_knot_capacity[0], m_knot[0]); // Fill in linear knots ReserveKnotCapacity( 1, 2 ); @@ -3008,9 +3172,9 @@ int ON_NurbsSurface::CreateConeSurface( if ( m_knot[0] && m_knot_capacity[0] > 0 ) onfree(m_knot[0]); - m_knot[0] = nurbs_curve.m_knot; - m_knot_capacity[0] = nurbs_curve.m_knot_capacity; - nurbs_curve.m_knot_capacity = 0; + + // Transfer knot vector from nurbs_curve to srf.m_knot[0]. + nurbs_curve.UnmanageKnotForExperts(m_knot_capacity[0], m_knot[0]); // Fill in linear knots ReserveKnotCapacity( 1, 2 ); diff --git a/opennurbs_nurbssurface.h b/opennurbs_nurbssurface.h index d8ec6968..a13f734b 100644 --- a/opennurbs_nurbssurface.h +++ b/opennurbs_nurbssurface.h @@ -886,11 +886,56 @@ public: ); - bool IsClamped( // determine if knot vector is clamped + /* + Description: + Test the knot vector to see if it is clamped. + Parameters: + dir: + 0: first parameter + 1: second parameter + end: + 0: test start + 1: test end + 2: test start and end. + */ bool IsClamped( + int dir, + int end = 2 + ) const; + + /* + Description: + Test the side of a surface to see if it's natural (Zero 2nd derivative). + Parameters: + dir: + 0: first parameter + 1: second parameter + end: + 0: test start + 1: test end + 2: test start and end. + */ + bool IsNatural( // determine if knot vector is clamped int dir, // dir 0 = "s", 1 = "t" int end = 2 // end to check: 0 = start, 1 = end, 2 = start and end ) const; - + + /* + Description: + Test a surface to see if it's natural (Zero 2nd derivative) on an iso curve. + Parameters: + dir: + 0: first parameter + 1: second parameter + t_count - [in] + t - [in] + t[] is a list of parameters to check. + */ + bool IsNatural( // determine if knot vector is clamped + int dir, // dir 0 = "s", 1 = "t" + size_t t_count, + const double* t + ) const; + double SuperfluousKnot( int dir, // dir 0 = "s", 1 = "t" int end // 0 = start, 1 = end diff --git a/opennurbs_nurbsvolume.cpp b/opennurbs_nurbsvolume.cpp index f6171dc5..eade09b8 100644 --- a/opennurbs_nurbsvolume.cpp +++ b/opennurbs_nurbsvolume.cpp @@ -1361,8 +1361,8 @@ ON_NurbsSurface* ON_NurbsCage::IsoSurface( nurbs_curve.m_cv_count = nurbs_curve.m_order; nurbs_curve.ReserveCVCapacity(nurbs_curve.m_dim*nurbs_curve.m_cv_count); nurbs_curve.m_cv_stride = nurbs_curve.m_dim; - nurbs_curve.m_knot = m_knot[dir] + span_index; - nurbs_curve.m_knot_capacity = 0; + // nurbs_curve.m_knot[] shares memory with m_knot[dir] + span_index. nurbs_curve destructor does not free nurbs_curve.m_knot[] + nurbs_curve.ManageKnotForExperts(0, m_knot[dir] + span_index); int ii,jj,kk; /* diff --git a/opennurbs_object.cpp b/opennurbs_object.cpp index 8d4056d8..2cfb7247 100644 --- a/opennurbs_object.cpp +++ b/opennurbs_object.cpp @@ -46,6 +46,8 @@ unsigned int ON_IsRhinoApplicationId( return 5; if (ON_rhino6_id == id) return 6; + if (ON_rhino7_id == id) + return 7; return 0; } @@ -59,6 +61,8 @@ unsigned int ON_IsOpennurbsApplicationId( return 5; if (ON_opennurbs6_id == id) return 6; + if (ON_opennurbs7_id == id) + return 7; return 0; } diff --git a/opennurbs_objref.cpp b/opennurbs_objref.cpp index 5290d20f..ca348b4f 100644 --- a/opennurbs_objref.cpp +++ b/opennurbs_objref.cpp @@ -70,11 +70,19 @@ ON_COMPONENT_INDEX::TYPE ON_COMPONENT_INDEX::Type(int i) case ON_COMPONENT_INDEX::extrusion_cap_surface: t = ON_COMPONENT_INDEX::extrusion_cap_surface; break; case ON_COMPONENT_INDEX::extrusion_path: t = ON_COMPONENT_INDEX::extrusion_path; break; + case ON_COMPONENT_INDEX::subd_vertex: t = ON_COMPONENT_INDEX::subd_vertex; break; + case ON_COMPONENT_INDEX::subd_edge: t = ON_COMPONENT_INDEX::subd_edge; break; + case ON_COMPONENT_INDEX::subd_face: t = ON_COMPONENT_INDEX::subd_face; break; + + case ON_COMPONENT_INDEX::hatch_loop: t = ON_COMPONENT_INDEX::hatch_loop; break; + case ON_COMPONENT_INDEX::dim_linear_point: t = ON_COMPONENT_INDEX::dim_linear_point; break; case ON_COMPONENT_INDEX::dim_radial_point: t = ON_COMPONENT_INDEX::dim_radial_point; break; case ON_COMPONENT_INDEX::dim_angular_point: t = ON_COMPONENT_INDEX::dim_angular_point; break; case ON_COMPONENT_INDEX::dim_ordinate_point: t = ON_COMPONENT_INDEX::dim_ordinate_point; break; case ON_COMPONENT_INDEX::dim_text_point: t = ON_COMPONENT_INDEX::dim_text_point; break; + case ON_COMPONENT_INDEX::dim_centermark_point: t = ON_COMPONENT_INDEX::dim_centermark_point; break; + case ON_COMPONENT_INDEX::dim_leader_point: t = ON_COMPONENT_INDEX::dim_leader_point; break; } return t; } @@ -267,6 +275,11 @@ bool ON_COMPONENT_INDEX::IsPointCloudComponentIndex() const return ( ON_COMPONENT_INDEX::pointcloud_point == m_type && m_index >= 0 ); } +bool ON_COMPONENT_INDEX::IsHatchLoopComponentIndex() const +{ + return (ON_COMPONENT_INDEX::hatch_loop == m_type && m_index >= 0); +} + static void ToStringHelper( ON_COMPONENT_INDEX ci, char* buffer, size_t sizeof_buffer ) { char* str = buffer; @@ -323,11 +336,19 @@ static void ToStringHelper( ON_COMPONENT_INDEX ci, char* buffer, size_t sizeof_b case ON_COMPONENT_INDEX::extrusion_cap_surface: s = "ON_COMPONENT_INDEX::extrusion_cap_surface"; break; case ON_COMPONENT_INDEX::extrusion_path: s = "ON_COMPONENT_INDEX::extrusion_path"; break; + case ON_COMPONENT_INDEX::subd_vertex: s = "ON_COMPONENT_INDEX::subd_vertex"; break; + case ON_COMPONENT_INDEX::subd_edge: s = "ON_COMPONENT_INDEX::subd_edge"; break; + case ON_COMPONENT_INDEX::subd_face: s = "ON_COMPONENT_INDEX::subd_face"; break; + + case ON_COMPONENT_INDEX::hatch_loop: s = "ON_COMPONENT_INDEX::hatch_loop"; break; + case ON_COMPONENT_INDEX::dim_linear_point: s = "ON_COMPONENT_INDEX::dim_linear_point"; break; case ON_COMPONENT_INDEX::dim_radial_point: s = "ON_COMPONENT_INDEX::dim_radial_point"; break; case ON_COMPONENT_INDEX::dim_angular_point: s = "ON_COMPONENT_INDEX::dim_angular_point"; break; case ON_COMPONENT_INDEX::dim_ordinate_point: s = "ON_COMPONENT_INDEX::dim_ordinate_point"; break; case ON_COMPONENT_INDEX::dim_text_point: s = "ON_COMPONENT_INDEX::dim_text_point"; break; + case ON_COMPONENT_INDEX::dim_centermark_point: s = "ON_COMPONENT_INDEX::dim_centermark_point"; break; + case ON_COMPONENT_INDEX::dim_leader_point: s = "ON_COMPONENT_INDEX::dim_leader_point"; break; default: s = 0; break; } @@ -458,6 +479,16 @@ bool ON_COMPONENT_INDEX::IsSet() const case ON_COMPONENT_INDEX::subd_edge: case ON_COMPONENT_INDEX::subd_face: + case ON_COMPONENT_INDEX::hatch_loop: + + case ON_COMPONENT_INDEX::dim_linear_point: + case ON_COMPONENT_INDEX::dim_radial_point: + case ON_COMPONENT_INDEX::dim_angular_point: + case ON_COMPONENT_INDEX::dim_ordinate_point: + case ON_COMPONENT_INDEX::dim_text_point: + case ON_COMPONENT_INDEX::dim_centermark_point: + case ON_COMPONENT_INDEX::dim_leader_point: + rc = (m_index != -1); break; @@ -469,14 +500,32 @@ bool ON_COMPONENT_INDEX::IsSet() const } -int ON_COMPONENT_INDEX::Compare( const ON_COMPONENT_INDEX* a, const ON_COMPONENT_INDEX* b ) +int ON_COMPONENT_INDEX::CompareType( const ON_COMPONENT_INDEX* lhs, const ON_COMPONENT_INDEX* rhs ) { - int i = ((int)a->m_type) - ((int)b->m_type); - if ( 0 == i ) - { - i = a->m_index - b->m_index; - } - return i; + const int lhs_i = (int)lhs->m_type; + const int rhs_i = (int)rhs->m_type; + if (lhs_i < rhs_i) + return -1; + if (lhs_i > rhs_i) + return 1; + return 0; +} + + +int ON_COMPONENT_INDEX::Compare( const ON_COMPONENT_INDEX* lhs, const ON_COMPONENT_INDEX* rhs ) +{ + const int lhs_i = (int)lhs->m_type; + const int rhs_i = (int)rhs->m_type; + if (lhs_i < rhs_i) + return -1; + if (lhs_i > rhs_i) + return 1; + + if (lhs->m_index < rhs->m_index) + return -1; + if (lhs->m_index > rhs->m_index) + return 1; + return 0; } bool ON_COMPONENT_INDEX::operator==(const ON_COMPONENT_INDEX& other) const @@ -775,7 +824,7 @@ bool ON_ObjRef_IRefID::Read( ON_BinaryArchive& archive ) bool ON_ObjRef::Write( ON_BinaryArchive& archive ) const { - bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 2 ); + bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 3 ); if ( !rc ) return false; @@ -824,6 +873,10 @@ bool ON_ObjRef::Write( ON_BinaryArchive& archive ) const rc = archive.WriteInterval(m_evp.m_s[2]); if (!rc) break; + // 1.3 IO fields + rc = archive.WriteInt((int)m_osnap_mode); + if (!rc) break; + break; } @@ -884,6 +937,13 @@ bool ON_ObjRef::Read( ON_BinaryArchive& archive ) { rc = archive.ReadInterval(m_evp.m_s[2]); if (!rc) break; + if (minor_version >= 3) + { + int osmode = 0; + rc = archive.ReadInt(&osmode); + if (!rc) break; + m_osnap_mode = ON::OSnapMode(osmode); + } } } diff --git a/opennurbs_parse_settings.cpp b/opennurbs_parse_settings.cpp index 0a79eb82..d4ad01d7 100644 --- a/opennurbs_parse_settings.cpp +++ b/opennurbs_parse_settings.cpp @@ -1181,15 +1181,15 @@ bool ON_ParseSettings::IsDigitSeparator(ON__UINT32 c) const switch(c) { case 0x002C: // comma - return ParseCommaAsDigitSeparator(); + return ParseCommaAsDigitSeparator(); // default = true break; case 0x002E: // full stop (period) - return ParseFullStopAsDigitSeparator(); + return ParseFullStopAsDigitSeparator(); // default = false break; case 0x0020: // space - return ParseSpaceAsDigitSeparator(); + return ParseSpaceAsDigitSeparator(); // default = true break; case 0x00A0: // no-break space @@ -1205,6 +1205,13 @@ bool ON_ParseSettings::IsDigitSeparator(ON__UINT32 c) const case 0x202F: // narrow no-break return ParseNoBreakThinSpaceAsDigitSeparator(); break; + + case 0x066C: // UNICODE ARABIC THOUSANDS SEPARATOR + // Intended to be used with eastern arabic numerials, + // but its lexical function is always a thousands separator. + return false; + break; + } return false; @@ -1221,6 +1228,18 @@ bool ON_ParseSettings::IsDecimalPoint(ON__UINT32 c) const case 0x002E: // full stop (period) return ParseFullStopAsDecimalPoint(); break; + + case 0x2396: // UNICODE DECIMAL SEPARATOR KEY SYMBOL + // Intended to be used as a symbol on keyboards, + // but its lexical function is always a decimal point. + return true; + break; + + case 0x066B: // UNICODE ARABIC DECIMAL SEPARATOR + // Intended to be used with eastern arabic numerials, + // but its lexical function is always a decimal point. + return true; + break; } return false; } diff --git a/opennurbs_point.cpp b/opennurbs_point.cpp index a7320070..4c3ebfbf 100644 --- a/opennurbs_point.cpp +++ b/opennurbs_point.cpp @@ -176,31 +176,97 @@ static bool Internal_IsUnsetDouble( return false; } +static bool Internal_IsNanDouble( + size_t count, + const double* a +) +{ + const double * e = a + count; + while (a < e) + { + double x = *a++; + if (x == x) + continue; + return true; + } + return false; +} + bool ON_2dPoint::IsUnset() const { return Internal_IsUnsetDouble(2, &x); } +bool ON_2dPoint::IsNan() const +{ + return Internal_IsNanDouble(2, &x); +} + +bool ON_2dPoint::IsUnsetOrNan() const +{ + return Internal_IsUnsetDouble(2, &x) || Internal_IsNanDouble(2, &x); +} + bool ON_3dPoint::IsUnset() const { return Internal_IsUnsetDouble(3, &x); } +bool ON_3dPoint::IsNan() const +{ + return Internal_IsNanDouble(3, &x); +} + +bool ON_3dPoint::IsUnsetOrNan() const +{ + return Internal_IsUnsetDouble(3, &x) || Internal_IsNanDouble(3, &x); +} + bool ON_4dPoint::IsUnset() const { return Internal_IsUnsetDouble(4, &x); } +bool ON_4dPoint::IsNan() const +{ + return Internal_IsNanDouble(4, &x); +} + +bool ON_4dPoint::IsUnsetOrNan() const +{ + return Internal_IsUnsetDouble(4, &x) || Internal_IsNanDouble(4, &x); +} + bool ON_2dVector::IsUnset() const { return Internal_IsUnsetDouble(2, &x); } +bool ON_2dVector::IsNan() const +{ + return Internal_IsNanDouble(2, &x); +} + +bool ON_2dVector::IsUnsetOrNan() const +{ + return Internal_IsUnsetDouble(2, &x) || Internal_IsNanDouble(2, &x); +} + bool ON_3dVector::IsUnset() const { return Internal_IsUnsetDouble(3, &x); } +bool ON_3dVector::IsNan() const +{ + return Internal_IsNanDouble(3, &x); +} + +bool ON_3dVector::IsUnsetOrNan() const +{ + return Internal_IsUnsetDouble(3, &x) || Internal_IsNanDouble(3, &x); +} + int ON_2dVector::Compare( const ON_2dVector& lhs, const ON_2dVector& rhs @@ -517,26 +583,26 @@ ON_Interval::Length() const bool ON_Interval::IsIncreasing() const { - return ( m_t[0] < m_t[1] && ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) ? true : false; + return ( ON_UNSET_VALUE < m_t[0] && m_t[0] < m_t[1] && m_t[1] < ON_UNSET_POSITIVE_VALUE ) ? true : false; } bool ON_Interval::IsDecreasing() const { - return ( m_t[0] > m_t[1] && ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) ? true : false; + return (ON_UNSET_POSITIVE_VALUE > m_t[0] && m_t[0] > m_t[1] && m_t[1] > ON_UNSET_VALUE) ? true : false; } bool ON_Interval::IsInterval() const { - return ( m_t[0] != m_t[1] && ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) ? true : false; + return ( IsIncreasing() || IsDecreasing() ) ? true : false; } bool ON_Interval::IsSingleton() const { - return ( m_t[0] == m_t[1] && ON_IS_VALID(m_t[1]) ) ? true : false; + return (ON_UNSET_VALUE < m_t[0] && m_t[0] == m_t[1] && m_t[1] < ON_UNSET_POSITIVE_VALUE) ? true : false; } bool @@ -554,8 +620,7 @@ ON_Interval::IsEmptySet() const bool ON_Interval::IsValid() const { - // 05/29/2007 TimH. Changed 0 to 1. - return ( ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ); + return (ON_UNSET_VALUE < m_t[0] && m_t[0] < ON_UNSET_POSITIVE_VALUE && ON_UNSET_VALUE < m_t[1] && m_t[1] < ON_UNSET_POSITIVE_VALUE); } bool @@ -568,8 +633,6 @@ ON_Interval::MakeIncreasing() // returns true if resulting interval IsIncreasin return IsIncreasing(); } - - int ON_Interval::Compare(const ON_Interval& lhs, const ON_Interval& rhs) { return Internal_DoubleArrayCompare(2, lhs.m_t, rhs.m_t); @@ -2159,8 +2222,10 @@ bool ON_2fPoint::IsZero() const bool ON_2fPoint::IsNotZero() const { - // the && (x == x && y == y) insures no coordinate is a Nan. - return (x != 0.0f || y != 0.0f) && (x == x && y == y); + return + (x != 0.0f || y != 0.0f) + && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT) + && (x != ON_UNSET_POSITIVE_FLOAT && y != ON_UNSET_POSITIVE_FLOAT); } ON_2fPoint operator*(int d, const ON_2fPoint& p) @@ -2596,8 +2661,11 @@ bool ON_3fPoint::IsZero() const bool ON_3fPoint::IsNotZero() const { - // the && (x == x && y == y) insures no coordinate is a Nan. - return (x != 0.0f || y != 0.0f || z != 0.0f) && (x == x && y == y && z == z); + // the && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT && z != ON_UNSET_FLOAT) insures no coordinate is a Nan. + return + (x != 0.0f || y != 0.0f || z != 0.0f) + && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT && z != ON_UNSET_FLOAT) + && (x != ON_UNSET_POSITIVE_FLOAT && y != ON_UNSET_POSITIVE_FLOAT && z != ON_UNSET_POSITIVE_FLOAT); } ON_3fPoint operator*(int d, const ON_3fPoint& p) @@ -3398,8 +3466,11 @@ bool ON_2fVector::IsZero() const bool ON_2fVector::IsNotZero() const { - // the && (x == x && y == y) insures no coordinate is a Nan. - return (x != 0.0f || y != 0.0f) && (x == x && y == y); + // the && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT) insures no coordinate is a Nan. + return + (x != 0.0f || y != 0.0f) + && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT) + && (x != ON_UNSET_POSITIVE_FLOAT && y != ON_UNSET_POSITIVE_FLOAT); } // set this vector to be perpendicular to another vector @@ -3905,8 +3976,11 @@ bool ON_3fVector::IsZero() const bool ON_3fVector::IsNotZero() const { - // the && (x == x && y == y && z == z) insures no coordinate is a Nan. - return (x != 0.0f || y != 0.0f || z != 0.0f) && (x == x && y == y && z == z); + // the && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT && z != ON_UNSET_FLOAT) insures no coordinate is a Nan. + return + (x != 0.0f || y != 0.0f || z != 0.0f) + && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT && z != ON_UNSET_FLOAT) + && (x != ON_UNSET_POSITIVE_FLOAT && y != ON_UNSET_POSITIVE_FLOAT && z != ON_UNSET_POSITIVE_FLOAT); } ON_3fVector operator*(int d, const ON_3fVector& v) @@ -4371,8 +4445,12 @@ bool ON_2dPoint::IsZero() const bool ON_2dPoint::IsNotZero() const { - // the && (x == x && y == y) insures no coordinate is a Nan. - return (x != 0.0 || y != 0.0) && (x == x && y == y); + // the && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE) insures no coordinate is a Nan. + return + (x != 0.0 || y != 0.0) + && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE) + && (x != ON_UNSET_POSITIVE_VALUE && y != ON_UNSET_POSITIVE_VALUE) + ; } ON_2dPoint operator*(int i, const ON_2dPoint& p) @@ -4824,8 +4902,11 @@ bool ON_3dPoint::IsZero() const bool ON_3dPoint::IsNotZero() const { - // the && (x == x && y == y && z == z) insures no coordinate is a Nan. - return (x != 0.0 || y != 0.0 || z != 0.0) && (x == x && y == y && z == z); + // the && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE) insures no coordinate is a Nan. + return + (x != 0.0 || y != 0.0 || z != 0.0) + && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE) + && (x != ON_UNSET_POSITIVE_VALUE && y != ON_UNSET_POSITIVE_VALUE && z != ON_UNSET_POSITIVE_VALUE); } ON_3dPoint operator*(int i, const ON_3dPoint& p) @@ -5718,8 +5799,13 @@ bool ON_2dVector::IsZero() const bool ON_2dVector::IsNotZero() const { - // the && (x == x && y == y) insures no coordinate is a Nan. - return (x != 0.0 || y != 0.0) && (x == x && y == y); +{ + // the && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE) insures no coordinate is a Nan. + return + (x != 0.0 || y != 0.0) + && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE) + && (x != ON_UNSET_POSITIVE_VALUE && y != ON_UNSET_POSITIVE_VALUE); +} } bool ON_2dVector::IsUnitVector() const @@ -6371,8 +6457,11 @@ bool ON_3dVector::IsZero() const bool ON_3dVector::IsNotZero() const { - // the && (x == x && y == y && z == z) insures no coordinate is a Nan. - return (x != 0.0 || y != 0.0 || z != 0.0) && (x == x && y == y && z == z); + // the && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE) insures no coordinate is a Nan. + return + (x != 0.0 || y != 0.0 || z != 0.0) + && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE) + && (x != ON_UNSET_POSITIVE_VALUE && y != ON_UNSET_POSITIVE_VALUE && z != ON_UNSET_POSITIVE_VALUE); } bool ON_3dVector::IsUnitVector() const @@ -7509,7 +7598,30 @@ bool ON_PlaneEquation::operator!=( const ON_PlaneEquation& eq ) const return (x!=eq.x || y!=eq.y || z!=eq.z || d!=eq.d)?true:false; } - +// Find the maximum absolute value of a array of (possibly homogeneous) points +double ON_MaximumCoordinate(const double* data, int dim, bool is_rat, int count) +{ + double norm = 0; + if (is_rat) + { + for (int i = 0; i < count; i++) + { + double w = fabs(data[i*(dim+1) + dim ]); + double norm_i = 0; + for (int j = 0; j < dim; j++) + norm_i = ON_Max(norm_i, fabs(data[i*(dim+1) + j])); + if (norm_i > norm * w) + norm = norm_i / w; + } + } + else + { + int n = dim * count; + for (int i = 0; i < n; i++) + norm = ON_Max(norm, fabs(data[i])); + } + return norm; +} int ON_Get3dConvexHull( const ON_SimpleArray& points, diff --git a/opennurbs_point.h b/opennurbs_point.h index 733ca47f..2c8b7abb 100644 --- a/opennurbs_point.h +++ b/opennurbs_point.h @@ -417,6 +417,18 @@ public: */ bool IsUnset() const; + /* + Returns: + True if any coordinate is a nan. + */ + bool IsNan() const; + + /* + Returns: + True if any coordinate is ON_UNSET_VALUE, ON_UNSET_POSITIVE_VALUE, or a nan + */ + bool IsUnsetOrNan() const; + // set 2d point value void Set(double x,double y); @@ -440,7 +452,7 @@ public: /* Returns: - true if at lease one coordinate is not zero and no coordinates are nans. + true if at lease one coordinate is not zero and no coordinates are unset or nans. */ bool IsNotZero() const; @@ -603,6 +615,18 @@ public: */ bool IsUnset() const; + /* + Returns: + True if any coordinate is a nan. +*/ + bool IsNan() const; + + /* + Returns: + True if any coordinate is ON_UNSET_VALUE, ON_UNSET_POSITIVE_VALUE, or a nan + */ + bool IsUnsetOrNan() const; + // set 3d point value void Set(double x,double y,double z); @@ -631,7 +655,7 @@ public: /* Returns: - true if at lease one coordinate is not zero and no coordinates are nans. + true if at lease one coordinate is not zero and no coordinates are unset or nans. */ bool IsNotZero() const; @@ -838,6 +862,18 @@ public: */ bool IsUnset() const; + /* + Returns: + True if any coordinate is a nan. +*/ + bool IsNan() const; + + /* + Returns: + True if any coordinate is ON_UNSET_VALUE, ON_UNSET_POSITIVE_VALUE, or a nan + */ + bool IsUnsetOrNan() const; + // set 4d point value void Set(double x,double y,double z,double w); @@ -999,6 +1035,18 @@ public: */ bool IsUnset() const; + /* + Returns: + True if any coordinate is a nan. +*/ + bool IsNan() const; + + /* + Returns: + True if any coordinate is ON_UNSET_VALUE, ON_UNSET_POSITIVE_VALUE, or a nan + */ + bool IsUnsetOrNan() const; + // set 2d vector value void Set(double x,double y); @@ -1081,7 +1129,7 @@ public: /* Returns: - true if at lease one coordinate is not zero and no coordinates are nans. + true if at lease one coordinate is not zero and no coordinates are unset or nans. */ bool IsNotZero() const; @@ -1318,6 +1366,18 @@ public: */ bool IsUnset() const; + /* + Returns: + True if any coordinate is a nan. + */ + bool IsNan() const; + + /* + Returns: + True if any coordinate is ON_UNSET_VALUE, ON_UNSET_POSITIVE_VALUE, or a nan + */ + bool IsUnsetOrNan() const; + // set 3d vector value void Set(double x,double y,double z); @@ -1406,7 +1466,7 @@ public: /* Returns: - true if at lease one coordinate is not zero and no coordinates are nans. + true if at lease one coordinate is not zero and no coordinates are unset or nans. */ bool IsNotZero() const; @@ -1948,6 +2008,10 @@ ON_IsRightHandFrame( // true if X, Y, Z are orthonormal and right handed const ON_3dVector& // Z ); +// Find the largest absolute value of coordinates from an array of points (possibly homogeneous). +ON_DECL +double ON_MaximumCoordinate(const double* data, int dim, bool is_rat, int count); + /////////////////////////////////////////////////////////////// // // common points and vectors diff --git a/opennurbs_pointcloud.cpp b/opennurbs_pointcloud.cpp index 6f7773ff..bb418f05 100644 --- a/opennurbs_pointcloud.cpp +++ b/opennurbs_pointcloud.cpp @@ -78,6 +78,16 @@ ON_PointCloud& ON_PointCloud::operator=( const ON_PointCloud& src ) return *this; } +ON_PointCloud::ON_PointCloud(const ON_3dPoint* P0, int count) : m_P(count) +{ + m_P.Create(3, false, count, 3, P0[0]); +} + +ON_PointCloud::ON_PointCloud(const double* P0, int dim, bool is_rat, int count) : m_P(count) +{ + m_P.Create(dim, is_rat, count, dim + is_rat, P0); +} + ON_PointCloud::~ON_PointCloud() { Destroy(); diff --git a/opennurbs_pointcloud.h b/opennurbs_pointcloud.h index 6b326021..50251c43 100644 --- a/opennurbs_pointcloud.h +++ b/opennurbs_pointcloud.h @@ -34,6 +34,11 @@ public: ); ON_PointCloud( const ON_PointCloud& ); ~ON_PointCloud(); + + ON_PointCloud(const ON_3dPoint* P0, int count); + + // 0PointAtStart(); // Note: The point compare test should be the same // as the one used in ON_Curve::IsClosed(). + // + // June 2019 - sometime in the past decade ON_PolyCurve::HasGap() + // changed and the test here is different from ON_Curve::IsClosed(). + // The initial "Note" no longer applies becaue it's no longer + // clear why the current ON_PolyCurve::HasGap() elimiated the "b c" + // test that remained in ON_Curve::IsClosed(). + if ( false == ON_PointsAreCoincident( 3, false, &P0.x, &P1.x ) ) { // To fix RR 13325 I allow a little more leeway for arcs. diff --git a/opennurbs_polyline.cpp b/opennurbs_polyline.cpp index 87cc3adc..87e9eff4 100644 --- a/opennurbs_polyline.cpp +++ b/opennurbs_polyline.cpp @@ -171,6 +171,21 @@ double ON_Polyline::Length() const return d; } +ON_Line ON_Polyline::Segment(int segment_index) const +{ + ON_Line line; + if (segment_index >= 0 && segment_index < m_count - 1) + { + line.from = m_a[segment_index]; + line.to = m_a[segment_index + 1]; + } + else + { + line = ON_Line::ZeroLine; + } + return line; +} + ON_3dVector ON_Polyline::SegmentDirection( int segment_index ) const { ON_3dVector v; @@ -376,3 +391,100 @@ bool ON_Polyline::CreateStarPolygon( return rc; } +bool ON_IsConvexPolyline( + size_t point_dim, + size_t point_count, + const double* points, + size_t point_stride, + bool bStrictlyConvex +) +{ + if (point_dim < 2 || point_dim > 3 || point_count < 3 || nullptr == points || point_stride < point_dim) + return false; + + const double* p; + ON_3dPoint P[2]; + + p = points + (point_stride*(point_count - 1)); + P[0] = ON_3dPoint(p[0], p[1], (3 == point_dim) ? p[2] : 0.0); + + p = points; + P[1] = ON_3dPoint(points[0], p[1], (3 == point_dim) ? p[2] : 0.0); + + if (P[0] == P[1]) + { + --point_count; + if (point_count < 3) + return false; + p = points + (point_stride*(point_count - 1)); + P[0] = ON_3dPoint(p[0], p[1], (3 == point_dim) ? p[2] : 0.0); + } + + ON_3dVector D[2] = { ON_3dVector::NanVector, P[1]-P[0]}; + if (false == D[1].IsNotZero()) + return false; + ON_SimpleArray C(point_count); + ON_3dVector maxN = ON_3dVector::ZeroVector; + double maxNlen = 0.0; + for (size_t i = 0; i < point_count; ++i) + { + p = points + (point_stride*((i+1)%point_count)); + P[0] = P[1]; + P[1] = ON_3dPoint(p[0], p[1], (3 == point_dim) ? p[2] : 0.0); + D[0] = D[1]; + D[1] = P[1] - P[0]; + if (false == D[1].IsNotZero()) + return false; + const ON_3dVector N = ON_CrossProduct(D[0], D[1]); + const double Nlen = N.Length(); + if (Nlen > maxNlen) + { + maxNlen = Nlen; + maxN = N; + } + else if (false == (Nlen > 0.0)) + { + if ( bStrictlyConvex || false == (D[0]*D[1] > 0.0) ) + return false; + } + C.Append(N.UnitVector()); + } + + maxN = maxN.UnitVector(); + for (size_t i = 0; i < point_count; ++i) + { +#if defined(ON_RUNTIME_ANDROID) || defined(ON_RUNTIME_LINUX) + double d = maxN * C[(unsigned int)i]; +#else + double d = maxN * C[i]; +#endif + if ( false == ((bStrictlyConvex) ? (d > 0.0) : (d >= 0.0)) ) + return false; + } + + return true; +} + +bool ON_IsConvexPolyline( + const ON_SimpleArray& points, + bool bStrictlyConvex +) +{ + return ON_IsConvexPolyline( + 3, + points.UnsignedCount(), + (const double*)(points.Array()), + 3, + bStrictlyConvex + ); +} + + +bool ON_Polyline::IsConvexLoop(bool bStrictlyConvex) const +{ + if (false == IsClosed()) + return false; + const ON_SimpleArray& points = *this; + return ON_IsConvexPolyline(points, bStrictlyConvex); +} + diff --git a/opennurbs_polyline.h b/opennurbs_polyline.h index 9ebf7706..3a403636 100644 --- a/opennurbs_polyline.h +++ b/opennurbs_polyline.h @@ -122,11 +122,34 @@ public: double tolerance = 0.0 ) const; + /* + Description: + Determine if a polyline is convex. + Parameters: + bStrictlyConvex - [in] + If false, colinear segments are considered convex. + Returns + True if the polyline is a closed, convex loop. + */ + bool IsConvexLoop( + bool bStrictlyConvex + ) const; + // Returns: // Length of the polyline. double Length() const; + + // Parameters: + // segment_index - [in] zero based segment index + // Returns: + // line = point[segment_index] -> point[segment_index+1] + ON_Line Segment( + int segment_index + ) const; + + // Parameters: // segment_index - [in] zero based segment index // Returns: @@ -220,7 +243,8 @@ Parameters: InPlines - [in] Array of polylines to be joined (not modified) OutPlines - [out] Resulting joined polylines and copies of polylines that were not joined to anything are appended. - join_tol - [in] Distance tolerance used to decide if endpoints are close enough + join_tol - [in] Distance tolerance used to decide if endpoints are close enough. Curves or segments with length + less than join_tol are NOT collapsed and can cause problems when endpoints do not match exactly. kink_tol - [in] Angle in radians. If > 0.0, then curves within join_tol will only be joined if the angle between them is less than kink_tol. If <= 0, then the angle will be ignored and only join_tol will be used. bUseTanAngle - [in] If true, choose the best match using angle between tangents. diff --git a/opennurbs_public.vcxproj b/opennurbs_public.vcxproj index 7d59f3ca..98fa894b 100644 --- a/opennurbs_public.vcxproj +++ b/opennurbs_public.vcxproj @@ -160,6 +160,7 @@ + @@ -318,6 +319,7 @@ + @@ -435,6 +437,7 @@ + diff --git a/opennurbs_public.xcodeproj/project.pbxproj b/opennurbs_public.xcodeproj/project.pbxproj index a9ab6bb8..3e3bf542 100644 --- a/opennurbs_public.xcodeproj/project.pbxproj +++ b/opennurbs_public.xcodeproj/project.pbxproj @@ -337,6 +337,7 @@ 1DC319EC1ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319921ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp */; }; 1DC319ED1ED6534E00DE6D26 /* opennurbs_zlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319931ED6534E00DE6D26 /* opennurbs_zlib.cpp */; }; 1DC319EE1ED6534E00DE6D26 /* opennurbs_zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319941ED6534E00DE6D26 /* opennurbs_zlib.h */; }; + BE95E7A4235BCFA900A57145 /* opennurbs_subd_texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE95E7A3235BCFA800A57145 /* opennurbs_subd_texture.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -672,6 +673,7 @@ 1DC319921ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_zlib_memory.cpp; sourceTree = ""; }; 1DC319931ED6534E00DE6D26 /* opennurbs_zlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_zlib.cpp; sourceTree = ""; }; 1DC319941ED6534E00DE6D26 /* opennurbs_zlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_zlib.h; sourceTree = ""; }; + BE95E7A3235BCFA800A57145 /* opennurbs_subd_texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_texture.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1016,6 +1018,7 @@ 1DC319521ED6534E00DE6D26 /* opennurbs_subd_ref.cpp */, 1DC319531ED6534E00DE6D26 /* opennurbs_subd_ring.cpp */, 1DC319541ED6534E00DE6D26 /* opennurbs_subd_sector.cpp */, + BE95E7A3235BCFA800A57145 /* opennurbs_subd_texture.cpp */, 1DC319551ED6534E00DE6D26 /* opennurbs_subd.cpp */, 1DC319571ED6534E00DE6D26 /* opennurbs_sum.cpp */, 1DC319581ED6534E00DE6D26 /* opennurbs_sumsurface.cpp */, @@ -1427,6 +1430,7 @@ 1DC319BF1ED6534E00DE6D26 /* opennurbs_text_style.cpp in Sources */, 1DC318071ED652B800DE6D26 /* opennurbs_cylinder.cpp in Sources */, 1DC319001ED652F800DE6D26 /* opennurbs_planesurface.cpp in Sources */, + BE95E7A4235BCFA900A57145 /* opennurbs_subd_texture.cpp in Sources */, 1DC318F41ED652F800DE6D26 /* opennurbs_optimize.cpp in Sources */, 1DC317D31ED652B800DE6D26 /* opennurbs_arccurve.cpp in Sources */, 1DC317ED1ED652B800DE6D26 /* opennurbs_brep_isvalid.cpp in Sources */, diff --git a/opennurbs_public_staticlib.vcxproj b/opennurbs_public_staticlib.vcxproj index 7b095d82..042b70ab 100644 --- a/opennurbs_public_staticlib.vcxproj +++ b/opennurbs_public_staticlib.vcxproj @@ -161,6 +161,7 @@ + @@ -318,6 +319,7 @@ + @@ -434,6 +436,7 @@ + diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index cdc0503c..55d195f7 100644 --- a/opennurbs_public_version.h +++ b/opennurbs_public_version.h @@ -5,8 +5,8 @@ // at your disposal. // To update version numbers, edit ..\build\build_dates.msbuild -#define RMA_VERSION_MAJOR 6 -#define RMA_VERSION_MINOR 15 +#define RMA_VERSION_MAJOR 7 +#define RMA_VERSION_MINOR 0 //////////////////////////////////////////////////////////////// // @@ -14,10 +14,10 @@ // first step in each build. // #define RMA_VERSION_YEAR 2019 -#define RMA_VERSION_MONTH 6 -#define RMA_VERSION_DATE 7 -#define RMA_VERSION_HOUR 19 -#define RMA_VERSION_MINUTE 45 +#define RMA_VERSION_MONTH 11 +#define RMA_VERSION_DATE 5 +#define RMA_VERSION_HOUR 17 +#define RMA_VERSION_MINUTE 46 //////////////////////////////////////////////////////////////// // @@ -35,19 +35,20 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 6,15,19158,19450 -#define VERSION_WITH_PERIODS 6.15.19158.19450 +#define VERSION_WITH_COMMAS 7,0,19309,17460 +#define VERSION_WITH_PERIODS 7.0.19309.17460 #define COPYRIGHT "Copyright (C) 1993-2019, Robert McNeel & Associates. All Rights Reserved." #define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library." -#define RMA_VERSION_NUMBER_MAJOR_STRING "6" -#define RMA_VERSION_NUMBER_MAJOR_WSTRING L"6" +#define RMA_VERSION_NUMBER_MAJOR_STRING "7" +#define RMA_VERSION_NUMBER_MAJOR_WSTRING L"7" +#define RMA_PREVIOUS_VERSION_NUMBER_MAJOR_WSTRING L"6" -#define RMA_VERSION_NUMBER_SR_STRING "SR15" -#define RMA_VERSION_NUMBER_SR_WSTRING L"SR15" +#define RMA_VERSION_NUMBER_SR_STRING "SR0" +#define RMA_VERSION_NUMBER_SR_WSTRING L"SR0" -#define RMA_VERSION_WITH_PERIODS_STRING "6.15.19158.19450" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"6.15.19158.19450" +#define RMA_VERSION_WITH_PERIODS_STRING "7.0.19309.17460" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"7.0.19309.17460" diff --git a/opennurbs_revsurface.cpp b/opennurbs_revsurface.cpp index cdd9694a..6593dbde 100644 --- a/opennurbs_revsurface.cpp +++ b/opennurbs_revsurface.cpp @@ -779,6 +779,50 @@ bool ON_RevSurface::GetNurbFormParameterFromSurfaceParameter( return rc; } +ON_Arc ON_RevSurface::IsoArc( + double curve_parameter +) const +{ + for (;;) + { + if (nullptr == m_curve) + break; + // 8 December 2003 Chuck - fix iso extraction bug + // when m_angle[0] != 0. + ON_Circle circle; + ON_3dPoint P = m_curve->PointAt(curve_parameter); + if (false == P.IsValid()) + break; + circle.plane.origin = m_axis.ClosestPointTo(P); + circle.plane.zaxis = m_axis.Tangent(); + circle.plane.xaxis = P - circle.plane.origin; + circle.radius = circle.plane.xaxis.Length(); + if (!circle.plane.xaxis.Unitize()) + { + // 8 December 2003 Dale Lear - get valid zero radius + // arc/circle when revolute hits the axis. + // First: try middle of revolute for x-axis + P = m_curve->PointAt(m_curve->Domain().ParameterAt(0.5)); + ON_3dPoint Q = m_axis.ClosestPointTo(P); + circle.plane.xaxis = P - Q; + if (!circle.plane.xaxis.Unitize()) + { + // Then: just use a vector perp to zaxis + circle.plane.xaxis.PerpendicularTo(circle.plane.zaxis); + } + } + circle.plane.yaxis = ON_CrossProduct(circle.plane.zaxis, circle.plane.xaxis); + circle.plane.yaxis.Unitize(); + circle.plane.UpdateEquation(); + ON_Arc arc(circle, m_angle); + return arc; + } + ON_Arc arc; + arc.plane = ON_Plane::NanPlane; + arc.radius = ON_DBL_QNAN; + return arc; +} + ON_Curve* ON_RevSurface::IsoCurve( int dir, double c ) const { diff --git a/opennurbs_revsurface.h b/opennurbs_revsurface.h index 141a46af..723a1915 100644 --- a/opennurbs_revsurface.h +++ b/opennurbs_revsurface.h @@ -406,6 +406,11 @@ public: double c ) const override; + + ON_Arc IsoArc( + double curve_parameter + ) const; + /* Description: Removes the portions of the surface outside of the specified interval. diff --git a/opennurbs_rtree.cpp b/opennurbs_rtree.cpp index fa7ac00e..3ec14c38 100644 --- a/opennurbs_rtree.cpp +++ b/opennurbs_rtree.cpp @@ -2675,8 +2675,9 @@ double CalcRectVolumeHelper(const ON_RTreeBBox* a_rect) r += d * d; d = (a_rect->m_max[2] - a_rect->m_min[2]); r += d * d; - r = sqrt(r*0.5); // r = sqrt((dx^2 + dy^2 + dz^2)/2); - return (r * r * r * 4.1887902047863909846168578443727); // 4/3 pi r^3 + //r = sqrt(r*0.5); // r = sqrt((dx^2 + dy^2 + dz^2)/2); + //return (r * r * r * 4.1887902047863909846168578443727); // 4/3 pi r^3 + return r; #elif ( 2 == ON_RTree_NODE_DIM ) // 2d bounding circle volume d = (a_rect->m_max[0] - a_rect->m_min[0]); diff --git a/opennurbs_sleeplock.cpp b/opennurbs_sleeplock.cpp index c5c783f3..cb370583 100644 --- a/opennurbs_sleeplock.cpp +++ b/opennurbs_sleeplock.cpp @@ -177,6 +177,11 @@ ON_SleepLockGuard::ON_SleepLockGuard(class ON_SleepLock& sleep_lock) m_bIsManagingLock = m_sleep_lock.GetLock(); } +ON_SleepLockGuard::ON_SleepLockGuard(class ON_FixedSizePool& fsp) + : m_sleep_lock(fsp.m_sleep_lock) +{ + m_bIsManagingLock = m_sleep_lock.GetLock(); +} ON_SleepLockGuard::ON_SleepLockGuard( class ON_SleepLock& sleep_lock, diff --git a/opennurbs_sleeplock.h b/opennurbs_sleeplock.h index 82dc331a..90aad61b 100644 --- a/opennurbs_sleeplock.h +++ b/opennurbs_sleeplock.h @@ -168,6 +168,15 @@ public: */ ON_SleepLockGuard(class ON_SleepLock& sleep_lock); + // Used by The ThreadSafe...() functions and for expert users + // to use when managing memory controlled by this pool. Best + // to ingnore this unless you have a very clear idea of what + // you are doing, why you are doing it, and when you are doing it. + // Otherwise, you'll find yourself waiting forever on a nested + // access request. + ON_SleepLockGuard(class ON_FixedSizePool& fsp); + + /* Description: Calls sleep_lock.GetLock(interval_wait_msecs,max_wait_msecs). diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp index d509f53a..9b379a06 100644 --- a/opennurbs_statics.cpp +++ b/opennurbs_statics.cpp @@ -93,6 +93,15 @@ ON__UINT64 ON_TestClass::internal_CtorSerialNumberGenerator = 0; ON__UINT64 ON_TestClass::internal_PopulationCounter = 0; #endif +ON__UINT64 ON_NextContentSerialNumber() +{ + static ON__UINT64 serial_number = 0; + // If it's ever an issue with multiple threads, + // this value can be made atomic, but that will slow down performance. + return (0 != ++serial_number) ? serial_number : ++serial_number; +} + + // It is critical that ON_ModelComponent::Internal_RuntimeSerialNumberGenerator // be constructed before any instance of a class derived from ON_ModelComponent. // That is why it is above the ClassId stuff in this .cpp file. @@ -100,6 +109,11 @@ std::atomic ON_ModelComponent::Internal_RuntimeSerialNumberGenerator std::atomic ON_SubDimple::Internal_RuntimeSerialNumberGenerator; +ON_SubDComponentLocation ON_SubD::DefaultSubDAppearance = ON_SubDComponentLocation::Surface; + +const double ON_SubDSectorType::MinimumCornerAngleRadians = (2.0*ON_PI)/((double)(ON_SubDSectorType::MaximumCornerAngleIndex)); +const double ON_SubDSectorType::MaximumCornerAngleRadians = 2.0*ON_PI - ON_SubDSectorType::MinimumCornerAngleRadians; + ON_ClassId* ON_ClassId::m_p0 = 0; // static pointer to first id in list ON_ClassId* ON_ClassId::m_p1 = 0; // static pointer to last id in list int ON_ClassId::m_mark0 = 0; @@ -165,11 +179,15 @@ const ON_UUID ON_rhino5_id = { 0x60515f84, 0x8f7f, 0x41da,{ 0x80, 0x1d, 0x1c, 0x // {06BB1079-5A56-47A1-AD6D-0B45183D894B} const ON_UUID ON_rhino6_id = { 0x6bb1079, 0x5a56, 0x47a1,{ 0xad, 0x6d, 0xb, 0x45, 0x18, 0x3d, 0x89, 0x4b } }; +// {78464C2C-9AEB-456E-8C27-865A524F5CA0} +const ON_UUID ON_rhino7_id = { 0x78464c2c, 0x9aeb, 0x456e,{ 0x8c, 0x27, 0x86, 0x5a, 0x52, 0x4f, 0x5c, 0xa0 } }; + + // ON_rhino_id is always set to the value for the current version // of Rhino. ON_rhino_id is the id that should be used as the // userdata application id for userdata class definitions that are // in the core Rhino executable. -const ON_UUID ON_rhino_id = ON_rhino6_id; +const ON_UUID ON_rhino_id = ON_rhino7_id; // Used to identifiy userdata read from V2 files // which were written before userdata had application ids. @@ -196,12 +214,15 @@ const ON_UUID ON_opennurbs5_id = { 0xc8cda597, 0xd957, 0x4625,{ 0xa4, 0xb3, 0xa0 // {7B0B585D-7A31-45D0-925E-BDD7DDF3E4E3} const ON_UUID ON_opennurbs6_id = { 0x7b0b585d, 0x7a31, 0x45d0,{ 0x92, 0x5e, 0xbd, 0xd7, 0xdd, 0xf3, 0xe4, 0xe3 } }; +// {523bfe6e-ef49-4b75-a8d6-253faf5044d3} +const ON_UUID ON_opennurbs7_id = { 0x523bfe6e, 0xef49, 0x4b75,{ 0xa8, 0xd6, 0x25, 0x3f, 0xaf, 0x50, 0x44, 0xd3 } }; + // ON_opennurbs_id is always set to the value for the current version // of opennurbs. ON_opennurbs_id is the id that should be used as // the userdata application id for userdata classes definitions that // are in the opennurbs library. -const ON_UUID ON_opennurbs_id = ON_opennurbs6_id; +const ON_UUID ON_opennurbs_id = ON_opennurbs7_id; const ON_UuidPairList ON_UuidPairList::EmptyList; @@ -491,6 +512,8 @@ const wchar_t ON_wString::Backslash = (wchar_t)ON_UnicodeCodePoint::ON_Backslash const wchar_t ON_wString::Underscore = (char)ON_UnicodeCodePoint::ON_Underscore; const wchar_t ON_wString::Pipe = (wchar_t)ON_UnicodeCodePoint::ON_Pipe; const wchar_t ON_wString::Tilde = (wchar_t)ON_UnicodeCodePoint::ON_Tilde; +const wchar_t ON_wString::DecimalAsPeriod = (wchar_t)ON_UnicodeCodePoint::ON_Period; +const wchar_t ON_wString::DecimalAsComma = (wchar_t)ON_UnicodeCodePoint::ON_Comma; #if defined(ON_SIZEOF_WCHAR_T) && ON_SIZEOF_WCHAR_T >= 2 // ON_wString is UTF-16 encoded when sizeof(wchar_t) = 2 @@ -812,6 +835,8 @@ const ON_Color ON_Color::Gray160(160, 160, 160); const ON_Color ON_Color::Gray230(230, 230, 230); const ON_Color ON_Color::Gray250(250, 250, 250); +const ON_4fColor ON_4fColor::Unset; + const ON_UuidIndex ON_UuidIndex::NilIndex = ON_UuidIndex(); const ON_UuidPtr ON_UuidPtr::NilPtr = ON_UuidPtr(); @@ -853,6 +878,12 @@ static ON_Plane ON_Plane_NanPlane() } const ON_Plane ON_Plane::NanPlane(ON_Plane_NanPlane()); +// ON_SubDDisplayParameters statics before ON_MeshParamters statics +const ON_SubDDisplayParameters ON_SubDDisplayParameters::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDDisplayParameters); +const ON_SubDDisplayParameters ON_SubDDisplayParameters::Course = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::CourseDensity); +const ON_SubDDisplayParameters ON_SubDDisplayParameters::Default = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::DefaultDensity); + + // {F15F67AA-4AF9-4B25-A3B8-517CEDDAB134} const ON_UUID ON_MeshParameters::RhinoLegacyMesherId = { 0xf15f67aa, 0x4af9, 0x4b25,{ 0xa3, 0xb8, 0x51, 0x7c, 0xed, 0xda, 0xb1, 0x34 } }; @@ -1447,6 +1478,9 @@ static ON_TextureMapping SurfaceParameterTextureMappingInitializer() } const ON_TextureMapping ON_TextureMapping::SurfaceParameterTextureMapping(SurfaceParameterTextureMappingInitializer()); +const ON_MappingTag ON_MappingTag::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_MappingTag); +const ON_MappingTag ON_MappingTag::SurfaceParameterMapping(ON_TextureMapping::SurfaceParameterTextureMapping,nullptr); + const ON_LinetypeSegment ON_LinetypeSegment::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_LinetypeSegment); const ON_LinetypeSegment ON_LinetypeSegment::OneMillimeterLine(1.0, ON_LinetypeSegment::eSegType::stLine); @@ -2283,20 +2317,25 @@ const ON_HatchPattern ON_HatchPattern::Grid60(Internal_LineHatchPatternInit(-7)) const ON_HatchPattern ON_HatchPattern::Plus(Internal_LineHatchPatternInit(-8)); // index = -8, id set, unique and persistent const ON_HatchPattern ON_HatchPattern::Squares(Internal_LineHatchPatternInit(-9)); // index = -9, id set, unique and persistent -#if defined(OPENNURBS_SUBD_WIP) + +const ON_Mesh ON_Mesh::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Mesh); +const ON_MeshRef ON_MeshRef::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_MeshRef); unsigned int ON_SubD::ErrorCount = 0; -const ON_SubDComponentPtr ON_SubDComponentPtr::Null = { 0 }; +const ON_SubDVertexEdgeProperties ON_SubDVertexEdgeProperties::Zero ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDVertexEdgeProperties); + const ON_SubDVertexPtr ON_SubDVertexPtr::Null = { 0 }; const ON_SubDEdgePtr ON_SubDEdgePtr::Null = { 0 }; const ON_SubDFacePtr ON_SubDFacePtr::Null = { 0 }; +const ON_SubDComponentPtr ON_SubDComponentPtr::Null = { 0 }; +const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::Null = ON_SubDComponentPtrPair::Create(ON_SubDComponentPtr::Null,ON_SubDComponentPtr::Null); const ON_SubDEdgeChain ON_SubDEdgeChain::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDEdgeChain); -static ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint_Init(double x) +static ON_SubDSectorSurfacePoint ON_SubDSectorLimitPoint_Init(double x) { - ON_SubDSectorLimitPoint lp; + ON_SubDSectorSurfacePoint lp; memset(&lp, 0, sizeof(lp)); lp.m_limitP[0] = x; @@ -2318,13 +2357,14 @@ static ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint_Init(double x) return lp; } -const ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint::Unset = ON_SubDSectorLimitPoint_Init(ON_UNSET_VALUE); -const ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint::Nan = ON_SubDSectorLimitPoint_Init(ON_DBL_QNAN); -const ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint::Zero = ON_SubDSectorLimitPoint_Init(0.0); +const ON_SubDSectorSurfacePoint ON_SubDSectorSurfacePoint::Unset = ON_SubDSectorLimitPoint_Init(ON_UNSET_VALUE); +const ON_SubDSectorSurfacePoint ON_SubDSectorSurfacePoint::Nan = ON_SubDSectorLimitPoint_Init(ON_DBL_QNAN); +const ON_SubDSectorSurfacePoint ON_SubDSectorSurfacePoint::Zero = ON_SubDSectorLimitPoint_Init(0.0); -const unsigned int ON_SubDLimitMeshFragment::MaximumSideSegmentCount = (1U << ON_SubDLimitMesh::MaximumDisplayDensity); // = 2^ON_SubDLimitMesh::MaximumDisplayDensity +const ON_SubDVertexSurfacePointCoefficient ON_SubDVertexSurfacePointCoefficient::Zero = ON_SubDVertexSurfacePointCoefficient::Create(nullptr,nullptr,0.0); +const ON_SubDVertexSurfacePointCoefficient ON_SubDVertexSurfacePointCoefficient::Nan = ON_SubDVertexSurfacePointCoefficient::Create(nullptr,nullptr,ON_DBL_QNAN); +const ON_SubDVertexSurfacePointCoefficient ON_SubDVertexSurfacePointCoefficient::Unset = ON_SubDVertexSurfacePointCoefficient::Create(nullptr,nullptr,ON_UNSET_VALUE); -const unsigned int ON_SubDSectorType::MaximumAngleIndex = 72; const double ON_SubDSectorType::IgnoredCornerSectorAngle = 0.0; const double ON_SubDSectorType::UnsetCornerSectorAngle = -8881.0; const double ON_SubDSectorType::ErrorCornerSectorAngle = -9991.0; @@ -2337,10 +2377,6 @@ const double ON_SubDSectorType::IgnoredSectorWeight = 0.0; const double ON_SubDSectorType::UnsetSectorWeight = -8883.0; const double ON_SubDSectorType::ErrorSectorWeight = -9993.0; -const unsigned int ON_SubDVertex::MaximumEdgeCount = 0xFFF0U; -const unsigned int ON_SubDVertex::MaximumFaceCount = 0xFFF0U; -const unsigned int ON_SubDEdge::MaximumFaceCount = 0xFFF0U; -const unsigned int ON_SubDFace::MaximumEdgeCount = 0xFFF0U; const ON_SubDComponentRegionIndex ON_SubDComponentRegionIndex::Zero ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDComponentRegionIndex); @@ -2396,24 +2432,27 @@ const ON_AggregateComponentStatus ON_AggregateComponentStatus::NotCurrent = ON_A const ON_SubDComponentPoint ON_SubDComponentPoint::Unset = ON_SubDComponentPoint(); -static ON_SubDLimitMeshFragmentGrid EmptyLimitMeshFragmentGridInit() +static ON_SubDMeshFragmentGrid EmptyLimitMeshFragmentGridInit() { - ON_SubDLimitMeshFragmentGrid empty; + ON_SubDMeshFragmentGrid empty; memset(&empty, 0, sizeof(empty)); return empty; } -static ON_SubDLimitMeshFragment EmptyLimitMeshFragmentInit() +static ON_SubDMeshFragment EmptyLimitMeshFragmentInit() { - ON_SubDLimitMeshFragment empty; + ON_SubDMeshFragment empty; memset(&empty, 0, sizeof(empty)); return empty; } -const ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Empty = EmptyLimitMeshFragmentGridInit(); -const ON_SubDLimitMeshFragment ON_SubDLimitMeshFragment::Empty = EmptyLimitMeshFragmentInit(); +const ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::Empty = EmptyLimitMeshFragmentGridInit(); +const ON_SubDMeshFragment ON_SubDMeshFragment::Empty = EmptyLimitMeshFragmentInit(); + +const ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::OneQuadGrid = ON_SubDMeshFragmentGrid::QuadGridFromSideSegmentCount(1, 0); + static ON_SubDComponentBase UnsetComponentBaseInit() { // For efficiency, ON_SubDComponentBase() does not waste time @@ -2459,38 +2498,33 @@ const ON_SubDVertex ON_SubDVertex::Empty = EmptyVertexInit(); const ON_SubDEdge ON_SubDEdge::Empty = EmptyEdgeInit(); const ON_SubDFace ON_SubDFace::Empty = EmptyFaceInit(); -const ON_SubDDisplayParameters ON_SubDDisplayParameters::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDDisplayParameters); -const ON_SubDDisplayParameters ON_SubDDisplayParameters::DefaultDisplayMeshParameters = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDLimitMesh::DefaultDisplayDensity); - const ON_SubD ON_SubD::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubD); const ON_SubDRef ON_SubDRef::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDRef); -const ON_SubDLimitMesh ON_SubDLimitMesh::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDLimitMesh); +const ON_SubDMesh ON_SubDMesh::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDMesh); const ON_SubDSectorType ON_SubDSectorType::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDSectorType); const ON_SubDMatrix ON_SubDMatrix::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDMatrix); const ON_SubDComponentRef ON_SubDComponentRef::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDComponentRef); -static ON_SubDFromMeshOptions ON_SubDCreaseParameters_CreaseAt( - ON_SubDFromMeshOptions::InteriorCreaseOption crease_type +static ON_ToSubDParameters ON_SubDCreaseParameters_CreaseAt( + ON_ToSubDParameters::InteriorCreaseOption crease_type ) { - ON_SubDFromMeshOptions cp; + ON_ToSubDParameters cp; cp.SetInteriorCreaseOption(crease_type); return cp; } -static ON_SubDFromMeshOptions ON_SubDCreaseParameters_ConvexCorners() +static ON_ToSubDParameters ON_SubDCreaseParameters_ConvexCorners() { - ON_SubDFromMeshOptions cp; - cp.SetConvexCornerOption(ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner); + ON_ToSubDParameters cp; + cp.SetConvexCornerOption(ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner); return cp; } -const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::Smooth ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDFromMeshOptions); -const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::InteriorCreaseAtMeshCrease = ON_SubDCreaseParameters_CreaseAt(ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease); -const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::InteriorCreaseAtMeshEdge = ON_SubDCreaseParameters_CreaseAt(ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge); -const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::ConvexCornerAtMeshCorner = ON_SubDCreaseParameters_ConvexCorners(); - -#endif +const ON_ToSubDParameters ON_ToSubDParameters::Smooth ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_ToSubDParameters); +const ON_ToSubDParameters ON_ToSubDParameters::InteriorCreaseAtMeshCrease = ON_SubDCreaseParameters_CreaseAt(ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease); +const ON_ToSubDParameters ON_ToSubDParameters::InteriorCreaseAtMeshEdge = ON_SubDCreaseParameters_CreaseAt(ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge); +const ON_ToSubDParameters ON_ToSubDParameters::ConvexCornerAtMeshCorner = ON_SubDCreaseParameters_ConvexCorners(); unsigned int ON_ModelComponent::Internal_SystemComponentHelper() { diff --git a/opennurbs_string.h b/opennurbs_string.h index 2dd6d156..ed0ec4c3 100644 --- a/opennurbs_string.h +++ b/opennurbs_string.h @@ -2152,6 +2152,8 @@ public: static const wchar_t Underscore; // Unicode LOW LINE U+005F static const wchar_t Pipe; // Unicode VERTICAL LINE U+007C static const wchar_t Tilde; // Unicode TILDE U+007E + static const wchar_t DecimalAsPeriod; // Unicode PERIOD U+002E + static const wchar_t DecimalAsComma; // Unicode COMMA U+002C #if defined(ON_SIZEOF_WCHAR_T) && ON_SIZEOF_WCHAR_T >= 2 // Never cast these values as "char" diff --git a/opennurbs_string_compare.cpp b/opennurbs_string_compare.cpp index af389fcc..0014984a 100644 --- a/opennurbs_string_compare.cpp +++ b/opennurbs_string_compare.cpp @@ -8,7 +8,7 @@ // 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 . // //////////////////////////////////////////////////////////////// @@ -19,7 +19,7 @@ #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 +// 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 @@ -60,7 +60,7 @@ static ON__UINT32 MapCodePointOrdinal( ON_StringMapOrdinalType map_type ) { - // Converts ordinal "char" and "wchar_t" element values in the + // Converts ordinal "char" and "wchar_t" element values in the // range 0x00 to maximum_singleton_value to "ingore case" ordinal equivalents. // The returned value is always <= input value. // @@ -173,13 +173,13 @@ static ON__UINT32 MapCodePointOrdinal( { case 0x0130U: // LATIN CAPITAL LETTER I WITH DOT ABOVE if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::LowerOrdinal == map_type) - return 0x0069U; // 0x0069U:LATIN SMALL LETTER i + return 0x0069U; // 0x0069U: LATIN SMALL LETTER i return unicode_code_point; break; case 0x0131U: // LATIN SMALL LETTER DOTLESS I if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) - return 0x0049U; // 0x0049ULATIN CAPITAL LETTER I + return 0x0049U; // 0x0049U LATIN CAPITAL LETTER I return unicode_code_point; break; @@ -190,7 +190,7 @@ static ON__UINT32 MapCodePointOrdinal( case 0x0149U: // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) - return 0x004EU; // 0x004EULATIN CAPITAL LETTER N + return 0x004EU; // 0x004EU LATIN CAPITAL LETTER N return unicode_code_point; break; @@ -287,7 +287,7 @@ static ON__UINT32 MapCodePointOrdinal( return unicode_code_point; } - + if (unicode_code_point < 0x0370) @@ -555,7 +555,7 @@ int ON_StringCompareOrdinalUTF8( unsigned int c1, c2; int i; - const int element_count + const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; @@ -584,7 +584,7 @@ int ON_StringCompareOrdinalUTF8( return 1; } } - + for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*string1++)) @@ -612,7 +612,7 @@ int ON_StringCompareOrdinalUTF16( unsigned int c1, c2; int i; - const int element_count + const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; @@ -641,7 +641,7 @@ int ON_StringCompareOrdinalUTF16( return 1; } } - + for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*string1++)) @@ -669,7 +669,7 @@ int ON_StringCompareOrdinalUTF32( unsigned int c1, c2; int i; - const int element_count + const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; @@ -698,7 +698,7 @@ int ON_StringCompareOrdinalUTF32( return 1; } } - + for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*string1++)) @@ -828,7 +828,7 @@ int ON_String::ComparePath( } count2++; } - + if (count1 > 0 || count2 > 0) { // TODO @@ -875,9 +875,9 @@ bool ON_String::EqualPath( CHAR_STRING_EQUAL_PREAMBLE(path1,element_count1,path2,element_count2); unsigned int c1=0, c2=0; - const int element_count - = (element_count1 <= element_count2) - ? element_count1 + const int element_count + = (element_count1 <= element_count2) + ? element_count1 : element_count2; int i; const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase(); @@ -902,7 +902,7 @@ bool ON_String::EqualPath( return false; } } - + for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*path1++)) @@ -986,7 +986,7 @@ int ON_wString::ComparePath( } count2++; } - + if (count1 > 0 || count2 > 0) { // TODO @@ -1037,9 +1037,9 @@ bool ON_wString::EqualPath( WIDE_STRING_EQUAL_PREAMBLE(path1,element_count1,path2,element_count2); unsigned int c1=0, c2=0; - const int element_count - = (element_count1 <= element_count2) - ? element_count1 + const int element_count + = (element_count1 <= element_count2) + ? element_count1 : element_count2; int i; const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase(); @@ -1064,7 +1064,7 @@ bool ON_wString::EqualPath( return false; } } - + for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*path1++)) @@ -1159,7 +1159,7 @@ static unsigned int Internal_NameAttributeWideCharRank( // 43 - (hyphen) // 44 A // 45 B - // 46 C + // 46 C // ... // 69 Z // 70 a @@ -1285,7 +1285,7 @@ static unsigned int Internal_NameAttributeWideCharRank( // 32 to 41 numerals 0 to 9 handled before this switch - case 0x27: // 42 ' (apostrophe) + case 0x27: // 42 ' (apostrophe) rank = 42; break; case 0x2D: // 43 - (hyphen) @@ -1301,7 +1301,7 @@ static unsigned int Internal_NameAttributeWideCharRank( break; } - return rank; + return rank; } int ON_wString::CompareAttributeName( @@ -1400,29 +1400,29 @@ int ON_String::CompareNoCase( const unsigned char* s) const bool ON_String::Equal( const ON_String& other_string, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) const { return ON_String::Equal( - static_cast(*this), - this->Length(), - static_cast(other_string), - other_string.Length(), + static_cast(*this), + this->Length(), + static_cast(other_string), + other_string.Length(), locale, bIgnoreCase ); } bool ON_String::Equal( const char* other_string, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) const { return ON_String::Equal( - static_cast(*this), - this->Length(), - other_string, + static_cast(*this), + this->Length(), + other_string, -1, locale, bIgnoreCase ); @@ -1431,7 +1431,7 @@ bool ON_String::Equal( bool ON_String::Equal( const char* string1, const char* string2, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) { @@ -1443,7 +1443,7 @@ bool ON_String::Equal( int element_count1, const char* string2, int element_count2, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) { @@ -1467,16 +1467,16 @@ bool ON_String::Equal( int ON_String::Compare( const ON_String& other_string, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) const { - return ON_String::Compare( + return ON_String::Compare( static_cast< const char* >(*this), this->Length(), static_cast< const char* >(other_string), other_string.Length(), - locale, + locale, bIgnoreCase ); } @@ -1484,16 +1484,16 @@ int ON_String::Compare( int ON_String::Compare( const char* other_string, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) const { - return ON_String::Compare( + return ON_String::Compare( static_cast< const char* >(*this), this->Length(), other_string, -1, - locale, + locale, bIgnoreCase ); } @@ -1501,7 +1501,7 @@ int ON_String::Compare( int ON_String::Compare( const char* string1, const char* string2, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) { @@ -1513,7 +1513,7 @@ int ON_String::Compare( int element_count1, const char* string2, int element_count2, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) { @@ -1522,10 +1522,10 @@ int ON_String::Compare( // TODO // Actually do an invariant culture compare // This involves NFC normalization and then using the correct CE to compare values. - // + // return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase ); //int n = (element_count1 <= element_count2) ? element_count1 : element_count2; - //int rc + //int rc // = bIgnoreCase // ? _strnicmp_l(string1, string2, (size_t)n, locale.StringCollateAndMapLocalePtr()) // : _strncmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr()); @@ -1541,11 +1541,11 @@ int ON_String::Compare( // TODO // Actually do an invariant culture compare // This involves NFC normalization and then using the correct CE to compare values. - // + // return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase ); //int n = (element_count1 <= element_count2) ? element_count1 : element_count2; - //int rc + //int rc // = bIgnoreCase // ? strncasecmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr()) // : strncmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr()); @@ -1564,11 +1564,11 @@ int ON_String::Compare( bool ON_wString::Equal( const ON_wString& other_string, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) const { - return ON_wString::Equal( + return ON_wString::Equal( static_cast< const wchar_t* >(*this), this->Length(), static_cast< const wchar_t* >(other_string), @@ -1580,11 +1580,11 @@ bool ON_wString::Equal( bool ON_wString::Equal( const wchar_t* other_string, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) const { - return ON_wString::Equal( + return ON_wString::Equal( static_cast< const wchar_t* >(*this), this->Length(), other_string, @@ -1597,7 +1597,7 @@ bool ON_wString::Equal( bool ON_wString::Equal( const wchar_t* string1, const wchar_t* string2, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) { @@ -1609,7 +1609,7 @@ bool ON_wString::Equal( int element_count1, const wchar_t* string2, int element_count2, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) { @@ -1627,11 +1627,11 @@ bool ON_wString::Equal( int ON_wString::Compare( const ON_wString& other_string, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) const { - return ON_wString::Compare( + return ON_wString::Compare( static_cast< const wchar_t* >(*this), this->Length(), static_cast< const wchar_t* >(other_string), @@ -1643,11 +1643,11 @@ int ON_wString::Compare( int ON_wString::Compare( const wchar_t* other_string, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) const { - return ON_wString::Compare( + return ON_wString::Compare( static_cast< const wchar_t* >(*this), this->Length(), other_string, @@ -1660,7 +1660,7 @@ int ON_wString::Compare( int ON_wString::Compare( const wchar_t* string1, const wchar_t* string2, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) { @@ -1672,7 +1672,7 @@ int ON_wString::Compare( int element_count1, const wchar_t* string2, int element_count2, - const class ON_Locale& locale, + const class ON_Locale& locale, bool bIgnoreCase ) { @@ -1683,13 +1683,13 @@ int ON_wString::Compare( #if defined(ON_RUNTIME_WIN) const bool bIsIsInvariantCulture = locale.IsInvariantCulture(); - wchar_t buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY]; - const wchar_t* locale_name - = bIsIsInvariantCulture + wchar_t buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY]; + const wchar_t* locale_name + = bIsIsInvariantCulture ? LOCALE_NAME_INVARIANT : locale.GetWindowsLocaleName(buffer,sizeof(buffer)/sizeof(buffer[0])); - - const DWORD flags = ( bIgnoreCase ) + + const DWORD flags = ( bIgnoreCase ) ? ((bIsIsInvariantCulture) ? NORM_IGNORECASE : LINGUISTIC_IGNORECASE) : 0; @@ -1711,20 +1711,20 @@ int ON_wString::Compare( if (rc == CSTR_GREATER_THAN) return 1; - + #elif defined(ON_RUNTIME_APPLE) - // I need a tool that is similar to + // I need a tool that is similar to // // wcscoll_l(const wchar_t*, const wchar_t*, locale_t ) // // but it needs to take a count (n) and I need one that ignores case. // // It appears wcsncasecmp_l() does use locale to map case, but then does an ordinal compare. - // - // + // + // //int n = (element_count1 <= element_count2) ? element_count1 : element_count2; - //int rc + //int rc // = bIgnoreCase // ? wcsncasecmp_l(string1, string2, (size_t)n, locale.StringCollateAndMapLocalePtr()) // : wcsncmp(string1, string2, (size_t)n); @@ -1754,7 +1754,7 @@ int ON_wString::Compare( bool operator==( const ON_String& lhs, const ON_String& rhs ) { const int length = lhs.Length(); - return + return (length == rhs.Length()) && ON_String::EqualOrdinal( static_cast(lhs), @@ -2041,14 +2041,14 @@ bool ON_String::EqualOrdinal( ) const { const int length = Length(); - return - (length == other_string.Length()) + return + (length == other_string.Length()) && ON_String::EqualOrdinal( static_cast< const char* >(*this), length, static_cast< const char* >(other_string), length, - bOrdinalIgnoreCase + bOrdinalIgnoreCase ); } @@ -2064,7 +2064,7 @@ bool ON_String::EqualOrdinal( length, other_string, length, - bOrdinalIgnoreCase + bOrdinalIgnoreCase ); } @@ -2135,14 +2135,14 @@ bool ON_wString::EqualOrdinal( ) const { const int length = Length(); - return + return (length == other_string.Length()) && ON_wString::EqualOrdinal( static_cast< const wchar_t* >(*this), length, static_cast< const wchar_t* >(other_string), length, - bOrdinalIgnoreCase + bOrdinalIgnoreCase ); } @@ -2158,7 +2158,7 @@ bool ON_wString::EqualOrdinal( length, other_string, length, - bOrdinalIgnoreCase + bOrdinalIgnoreCase ); } @@ -2202,7 +2202,7 @@ bool ON_wString::EqualOrdinal( return false; } } - + for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*string1++)) @@ -2388,7 +2388,7 @@ int ON_String::MapStringOrdinal( } else if ( 0 == mapped_string_capacity ) return element_count; // no +1 here - + if ( element_count > mapped_string_capacity ) return 0; @@ -2479,7 +2479,7 @@ int ON_wString::MapStringOrdinal( } else if ( 0 == mapped_string_capacity ) return element_count; // no +1 here - + if ( element_count > mapped_string_capacity ) return 0; @@ -2527,7 +2527,7 @@ ON_String ON_String::MapString( int element_count ) { - bool bMappingNullTerminator; + bool bMappingNullTerminator; if (element_count < 0) { @@ -2538,16 +2538,16 @@ ON_String ON_String::MapString( } else { - bMappingNullTerminator - = element_count > 0 - && nullptr != string - && 0 == string[element_count-1] + bMappingNullTerminator + = element_count > 0 + && nullptr != string + && 0 == string[element_count-1] && (1 == element_count || 0 != string[element_count-2]) ; } int mapped_string_capacity = ON_String::MapString(locale,map_type,string,element_count,nullptr,0); - + if (mapped_string_capacity > 0) { ON_String mapped_string; @@ -2557,8 +2557,8 @@ ON_String ON_String::MapString( mapped_string.ReserveArray(mapped_string_capacity); // Set mapped_string ON_wString header length value to mapped_length. - int mapped_string_length - = ( bMappingNullTerminator ) + int mapped_string_length + = ( bMappingNullTerminator ) ? (mapped_string_capacity-1) : mapped_string_capacity; mapped_string.SetLength(mapped_string_length); @@ -2578,7 +2578,7 @@ ON_wString ON_wString::MapString( int element_count ) { - bool bMappingNullTerminator; + bool bMappingNullTerminator; if (element_count < 0) { @@ -2589,16 +2589,16 @@ ON_wString ON_wString::MapString( } else { - bMappingNullTerminator - = element_count > 0 - && nullptr != string - && 0 == string[element_count-1] + bMappingNullTerminator + = element_count > 0 + && nullptr != string + && 0 == string[element_count-1] && (1 == element_count || 0 != string[element_count-2]) ; } int mapped_string_capacity = ON_wString::MapString(locale,map_type,string,element_count,nullptr,0); - + if (mapped_string_capacity > 0) { ON_wString mapped_string; @@ -2608,8 +2608,8 @@ ON_wString ON_wString::MapString( mapped_string.ReserveArray(mapped_string_capacity); // Set mapped_string ON_wString header length value to mapped_length. - int mapped_string_length - = ( bMappingNullTerminator ) + int mapped_string_length + = ( bMappingNullTerminator ) ? (mapped_string_capacity-1) : mapped_string_capacity; mapped_string.SetLength(mapped_string_length); diff --git a/opennurbs_string_format.cpp b/opennurbs_string_format.cpp index 805be8d7..304b351e 100644 --- a/opennurbs_string_format.cpp +++ b/opennurbs_string_format.cpp @@ -1224,6 +1224,9 @@ int ON_wString::FormatVargsOutputCount( return -1; #else #if defined(ON_COMPILER_GNU) + // 31 May 2019 S. Baer (RH-52038) + // TODO: The following code needs to be tested. This was added by request from a user that needed + // a GCC compile. This is obviously a cut and paste of the above clang code wchar_t stack_buffer[1024]; ON_wStringBuffer buffer(stack_buffer, sizeof(stack_buffer) / sizeof(stack_buffer[0])); size_t buffer_capacity = buffer.m_buffer_capacity; diff --git a/opennurbs_string_scan.cpp b/opennurbs_string_scan.cpp index 539d95d4..eff4b506 100644 --- a/opennurbs_string_scan.cpp +++ b/opennurbs_string_scan.cpp @@ -148,20 +148,21 @@ int ON_wString::ScanBufferVargs( va_list args ) { -#if defined(ON_COMPILER_CLANG) || defined(ON_RUNTIME_LINUX) -#if defined(ON_RUNTIME_ANDROID) || defined(ON_RUNTIME_LINUX) if (nullptr == buffer || nullptr == format) return -1; - return swscanf(buffer, format, args); -#else - if (nullptr == buffer || nullptr == format || nullptr == args) - return -1; - return swscanf_l(buffer, ON_Locale::InvariantCulture.NumericLocalePtr(), format, args); -#endif -#else - if (nullptr == buffer || nullptr == format || nullptr == args) + +#if defined(ON_RUNTIME_WIN) + if (nullptr == args) return -1; return _vswscanf_s_l(buffer, format, ON_Locale::InvariantCulture.NumericLocalePtr(), args); +#elif defined(ON_RUNTIME_APPLE) + if (nullptr == args) + return -1; + return swscanf_l(buffer, _c_locale, format, args); +#elif defined(ON_RUNTIME_ANDROID) || defined(ON_RUNTIME_LINUX) + return swscanf(buffer, format, args); +#else + return swscanf(buffer, format, args); #endif } diff --git a/opennurbs_string_values.cpp b/opennurbs_string_values.cpp index 82e42a6d..a247c08c 100644 --- a/opennurbs_string_values.cpp +++ b/opennurbs_string_values.cpp @@ -264,6 +264,20 @@ double ON_CleanNumber( return value; } +const ON_wString& ON_NTimesPowerOf10AsStringFail(const ON_wString& g_format, bool bLogError) +{ + // The number being formatted might be bogus. + // This can occur when values are not properly initialized. + if (bLogError) // <- good place for debugging breakpoint + { + ON_ERROR("Unexpected result."); + } + + // g_format is a valid string representation of the input value, so + // the user is seeing something that is correct, but may have + // scientific notation or lots of digits. + return g_format; +} static const ON_wString ON_NTimesPowerOf10AsString( @@ -278,18 +292,29 @@ const ON_wString ON_NTimesPowerOf10AsString( if (nullptr != clean_value) *clean_value = value; - // returns n*(10^e) as a pretty string. - //const ON__UINT64 ten = 10; + if ( false == ON_IsValid(value) ) + return ON_NTimesPowerOf10AsStringFail(g_format,false); // value is a nan or UNSET value. + // ON__UINT64 range is 0 to 18,446,744,073,709,551,615 = 1.8...e19 + const double max_pretty_value = 1.0e18; // must be <= 18,446,744,073,709,551,615 ( 20 decimal digits ) + const double min_pretty_value = 1.0 / max_pretty_value; + if (fabs(value) >= max_pretty_value || fabs(value) <= min_pretty_value) + return ON_NTimesPowerOf10AsStringFail(g_format,false); // value is too big or too small for a "pretty" format. + + + // returns n*(10^e) as a "pretty" string - no exponential notation that disturbs users. ON__UINT64 q = 1; ON__UINT64 i = 0; ON__UINT64 f = 0; - if (e >= 0) { for (int ie = 0; ie < e; ie++) q *= 10; + + if (fabs((double)n)*fabs((double)q) >= max_pretty_value) + return ON_NTimesPowerOf10AsStringFail(g_format,false); + i = n*q; f = 0; } @@ -298,6 +323,9 @@ const ON_wString ON_NTimesPowerOf10AsString( // e is negative for (int ie = 0; ie > e; ie--) q *= 10; + if (fabs((double)n) <= min_pretty_value*fabs((double)q)) + return ON_NTimesPowerOf10AsStringFail(g_format,false); + i = n / q; f = n % q; } @@ -311,7 +339,7 @@ const ON_wString ON_NTimesPowerOf10AsString( *clean_value = x; return ON_wString::FormatToString(L"%llu", i); } - wchar_t sf[32] = { 0 }; + wchar_t sf[32] = { 0 }; // 32 is more than enough to hold the maximum of 20 digits size_t sf_capacity = sizeof(sf) / sizeof(sf[i]); size_t sfi = 0; for (ON__UINT64 r = q / 10; r > 0 && sfi < sf_capacity; r /= 10) @@ -329,8 +357,7 @@ const ON_wString ON_NTimesPowerOf10AsString( } } - ON_ERROR("Unexpected result."); - return g_format; + return ON_NTimesPowerOf10AsStringFail(g_format,false); } diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp index 1674b96a..49f73126 100644 --- a/opennurbs_subd.cpp +++ b/opennurbs_subd.cpp @@ -26,13 +26,6 @@ //////////////////////////////////////////////////////////////// */ -#if defined(OPENNURBS_SUBD_WIP) - -void ON_SubDIncrementErrorCount() -{ - ON_SubD::ErrorCount++; -} - ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentPtrTypeFromUnsigned( unsigned int element_pointer_type_as_unsigned ) @@ -85,8 +78,8 @@ ON_SubD::EdgeTag ON_SubD::EdgeTagFromUnsigned( ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Smooth); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Crease); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Sharp); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::X); + //ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Sharp); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::SmoothX); } return ON_SUBD_RETURN_ERROR(ON_SubD::EdgeTag::Unset); } @@ -98,37 +91,11 @@ bool ON_SubD::EdgeTagIsSet( return ( ON_SubD::EdgeTag::Smooth == edge_tag || ON_SubD::EdgeTag::Crease == edge_tag - || ON_SubD::EdgeTag::Sharp == edge_tag - || ON_SubD::EdgeTag::X == edge_tag + //|| ON_SubD::EdgeTag::Sharp == edge_tag + || ON_SubD::EdgeTag::SmoothX == edge_tag ); } -ON_SubD::FacetType ON_SubD::FacetTypeFromUnsigned( - unsigned int facet_type_as_unsigned - ) -{ - switch (facet_type_as_unsigned) - { - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::FacetType::Unset); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::FacetType::Tri); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::FacetType::Quad); - } - return ON_SUBD_RETURN_ERROR(ON_SubD::FacetType::Unset); -} - -//ON_SubD::VertexEdgeOrder ON_SubD::VertexEdgeOrderFromUnsigned( -// unsigned int vertex_edge_order_as_unsigned -// ) -//{ -// switch (vertex_edge_order_as_unsigned) -// { -// ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexEdgeOrder::unset); -// ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexEdgeOrder::radial); -// ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexEdgeOrder::notradial); -// } -// return ON_SUBD_RETURN_ERROR(ON_SubD::VertexEdgeOrder::unset); -//} - ON_SubD::VertexFacetType ON_SubD::VertexFacetTypeFromUnsigned( unsigned int vertex_facet_type_as_unsigned ) @@ -144,53 +111,7 @@ ON_SubD::VertexFacetType ON_SubD::VertexFacetTypeFromUnsigned( return ON_SUBD_RETURN_ERROR(ON_SubD::VertexFacetType::Unset); } -ON_SubD::SubDType ON_SubD::SubDTypeFromUnsigned( - unsigned int subd_type_as_unsigned - ) -{ - switch (subd_type_as_unsigned) - { - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::Unset); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::Custom); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::TriLoopWarren); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::QuadCatmullClark); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::CustomTri); - ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::CustomQuad); - } - return ON_SUBD_RETURN_ERROR(ON_SubD::SubDType::Unset); -} - -ON_SubD::SubDType ON_SubD::DefaultSubDType() -{ - return ON_SubD::SubDType::QuadCatmullClark; -} - - -unsigned int ON_SubD::FacetEdgeCount( - ON_SubD::FacetType facet_type - ) -{ - if (ON_SubD::FacetType::Quad == facet_type) - return 4; - if (ON_SubD::FacetType::Tri == facet_type) - return 3; - return 0; -} - -unsigned int ON_SubD::FacetEdgeCount( - ON_SubD::SubDType subdivision_type - ) -{ - if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) - return 4; - if (ON_SubD::SubDType::TriLoopWarren == subdivision_type) - return 3; - return 0; - -} - unsigned int ON_SubDSectorType::SectorPointRingCountFromEdgeCount( - ON_SubD::SubDType subd_type, ON_SubD::VertexTag vertex_tag, unsigned int sector_edge_count ) @@ -200,33 +121,26 @@ unsigned int ON_SubDSectorType::SectorPointRingCountFromEdgeCount( if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) { // interior vertex - if (ON_SubD::SubDType::QuadCatmullClark == subd_type) - return (2 * sector_edge_count + 1); - if (ON_SubD::SubDType::TriLoopWarren == subd_type) - return (sector_edge_count + 1); + return (2 * sector_edge_count + 1); } if (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag) { // boundary vertex - if (ON_SubD::SubDType::QuadCatmullClark == subd_type) - return (2 * sector_edge_count); - if (ON_SubD::SubDType::TriLoopWarren == subd_type) - return (sector_edge_count + 1); + return (2 * sector_edge_count); } } return ON_SUBD_RETURN_ERROR(0); } unsigned int ON_SubDSectorType::SectorPointRingCountFromFaceCount( - ON_SubD::SubDType subd_type, ON_SubD::VertexTag vertex_tag, unsigned int sector_face_count ) { unsigned int sector_edge_count = ON_SubDSectorType::SectorEdgeCountFromFaceCount(vertex_tag,sector_face_count); return (sector_edge_count > 0) - ? ON_SubDSectorType::SectorPointRingCountFromEdgeCount(subd_type,vertex_tag,sector_edge_count) + ? ON_SubDSectorType::SectorPointRingCountFromEdgeCount(vertex_tag,sector_edge_count) : ON_SUBD_RETURN_ERROR(0); } @@ -277,13 +191,45 @@ unsigned int ON_SubDSectorType::MinimumSectorFaceCount( ON_SubD::VertexTag vertex_tag ) { - if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) - return 3; // can be reduced to 2 after calculating special case matrix and eigenvalues - if (ON_SubD::VertexTag::Corner == vertex_tag) - return 1; - if (ON_SubD::VertexTag::Crease == vertex_tag) - return 1; - return ON_UNSET_UINT_INDEX; + unsigned int minimum_sector_face_count; + switch (vertex_tag) + { + case ON_SubD::VertexTag::Unset: + ON_SUBD_ERROR("Unset tag."); + minimum_sector_face_count = ON_UNSET_UINT_INDEX; + break; + + case ON_SubD::VertexTag::Smooth: + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth + // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() + // for more details on how this case is handled. + minimum_sector_face_count = 2; // 3 without special case handling + break; + + case ON_SubD::VertexTag::Crease: + // A "wire" crease can have zero faces - this is the minimum when faces exist + minimum_sector_face_count = 1; + break; + + case ON_SubD::VertexTag::Corner: + // A "wire" corner can have zero faces - this is the minimum when faces exist + minimum_sector_face_count = 1; + break; + + case ON_SubD::VertexTag::Dart: + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart + // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() + // for more details on how this case is handled. + minimum_sector_face_count = 2; // 3 without special case handling + break; + + default: + ON_SUBD_ERROR("Invalid tag."); + minimum_sector_face_count = ON_UNSET_UINT_INDEX; + break; + } + + return minimum_sector_face_count; } bool ON_SubD::IsValidSectorEdgeCount( @@ -302,42 +248,6 @@ bool ON_SubD::IsValidSectorFaceCount( return (sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag) && sector_face_count <= ON_SubDVertex::MaximumFaceCount); } -bool ON_SubD::IsQuadOrTriFacetType( - ON_SubD::FacetType facet_type - ) -{ - return (ON_SubD::FacetType::Quad == facet_type || ON_SubD::FacetType::Tri == facet_type); -} - -bool ON_SubD::IsQuadOrTriSubDType( - ON_SubD::SubDType subdivision_type - ) -{ - if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type || ON_SubD::SubDType::CustomQuad == subdivision_type) - return true; - if (ON_SubD::SubDType::TriLoopWarren == subdivision_type || ON_SubD::SubDType::CustomTri == subdivision_type) - return true; - return false; -} - -ON_SubD::FacetType ON_SubD::FacetTypeFromSubDType( - ON_SubD::SubDType subdivision_type - ) -{ - if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type || ON_SubD::SubDType::CustomQuad == subdivision_type) - return ON_SubD::FacetType::Quad; - if (ON_SubD::SubDType::TriLoopWarren == subdivision_type || ON_SubD::SubDType::CustomTri == subdivision_type) - return ON_SubD::FacetType::Tri; - return ON_SubD::FacetType::Unset; -} - -bool ON_SubD::PointRingHasFacePoints( - ON_SubD::SubDType subdivision_type - ) -{ - return (ON_SubD::SubDType::QuadCatmullClark == subdivision_type || ON_SubD::SubDType::CustomQuad == subdivision_type); -} - ////////////////////////////////////////////////////////////////////////// // // ON_SubDVertexPtr @@ -348,43 +258,48 @@ bool ON_SubDVertexPtr::IsNull() const return (nullptr == ON_SUBD_VERTEX_POINTER(m_ptr)); } +bool ON_SubDVertexPtr::IsNotNull() const +{ + return (nullptr != ON_SUBD_VERTEX_POINTER(m_ptr)); +} + class ON_SubDVertex* ON_SubDVertexPtr::Vertex() const { return ON_SUBD_VERTEX_POINTER(m_ptr); } -ON__UINT_PTR ON_SubDVertexPtr::VertexPtrMark() const +ON__UINT_PTR ON_SubDVertexPtr::VertexDirection() const { - return ON_SUBD_VERTEX_MARK(m_ptr); + return ON_SUBD_VERTEX_DIRECTION(m_ptr); } -ON_ComponentStatus ON_SubDVertexPtr::Status() const +const ON_ComponentStatus ON_SubDVertexPtr::Status() const { const ON_SubDVertex* vertex = ON_SUBD_VERTEX_POINTER(m_ptr); return (nullptr == vertex) ? ON_ComponentStatus::NoneSet : vertex->m_status; } -class ON_SubDVertexPtr ON_SubDVertexPtr::Create( +const ON_SubDVertexPtr ON_SubDVertexPtr::Create( const class ON_SubDVertex* vertex ) { return ON_SubDVertexPtr::Create(vertex,0); } -class ON_SubDVertexPtr ON_SubDVertexPtr::Create( +const ON_SubDVertexPtr ON_SubDVertexPtr::Create( const class ON_SubDVertex* vertex, ON__UINT_PTR vertex_mark ) { - ON_SubDVertexPtr vptr = { (ON__UINT_PTR)vertex | (vertex_mark & ON_SUBD_ELEMENT_MARK_MASK) }; + ON_SubDVertexPtr vptr = { (ON__UINT_PTR)vertex | (vertex_mark & ON_SUBD_COMPONENT_DIRECTION_MASK) }; return vptr; } -class ON_SubDVertexPtr ON_SubDVertexPtr::Create( +const ON_SubDVertexPtr ON_SubDVertexPtr::Create( const class ON_SubDComponentPtr& vertex_element ) { - return ON_SubDVertexPtr::Create(vertex_element.Vertex(), vertex_element.ComponentMark()); + return ON_SubDVertexPtr::Create(vertex_element.Vertex(), vertex_element.ComponentDirection()); } ////////////////////////////////////////////////////////////////////////// @@ -398,11 +313,59 @@ bool ON_SubDEdgePtr::IsNull() const return (nullptr == ON_SUBD_EDGE_POINTER(m_ptr)); } +bool ON_SubDEdgePtr::IsNotNull() const +{ + return (nullptr != ON_SUBD_EDGE_POINTER(m_ptr)); +} + +bool ON_SubDEdgePtr::IsNotNullAndVerticesAreNotNull() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e && nullptr != e->m_vertex[0] && nullptr != e->m_vertex[1]); +} + + class ON_SubDEdge* ON_SubDEdgePtr::Edge() const { return ON_SUBD_EDGE_POINTER(m_ptr); } +unsigned int ON_SubDEdgePtr::EdgeId() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->m_id : 0U; +} + +unsigned int ON_SubDEdgePtr::EdgeFaceCount() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? ((unsigned int)e->m_face_count) : 0U; +} + +bool ON_SubDEdgePtr::EdgeIsSmooth() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->IsSmooth() : false; +} + +bool ON_SubDEdgePtr::EdgeIsCrease() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->IsCrease() : false; +} + +bool ON_SubDEdgePtr::EdgeIsHardCrease() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->IsHardCrease() : false; +} + +bool ON_SubDEdgePtr::EdgeIsDartCrease() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->IsDartCrease() : false; +} + ON__UINT_PTR ON_SubDEdgePtr::EdgeDirection() const { return ON_SUBD_EDGE_DIRECTION(m_ptr); @@ -426,31 +389,56 @@ const class ON_SubDVertex* ON_SubDEdgePtr::RelativeVertex( return nullptr; } -ON_ComponentStatus ON_SubDEdgePtr::Status() const +const ON_3dVector ON_SubDEdgePtr::RelativeDirection() const +{ + for (;;) + { + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); + if (nullptr == edge) + break; + if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1]) + break; + const int i0 = (0 != ON_SUBD_EDGE_DIRECTION(m_ptr)) ? 1 : 0; + const ON_3dPoint P0(edge->m_vertex[i0]->m_P); + const ON_3dPoint P1(edge->m_vertex[1-i0]->m_P); + return (P1 - P0); + } + return ON_3dVector::NanVector; +} + +const ON_ComponentStatus ON_SubDEdgePtr::Status() const { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); return (nullptr == edge) ? ON_ComponentStatus::NoneSet : edge->m_status; } -ON_SubDEdgePtr ON_SubDEdgePtr::Reversed() const +const ON_SubDEdgePtr ON_SubDEdgePtr::Reversed() const { return ON_SubDEdgePtr::Create(ON_SUBD_EDGE_POINTER(m_ptr), 1 - (m_ptr & 1)); } -class ON_SubDEdgePtr ON_SubDEdgePtr::Create( +const ON_SubDEdgePtr ON_SubDEdgePtr::Create( + const class ON_SubDEdge* edge + ) +{ + ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge }; + return eptr; +} + +const ON_SubDEdgePtr ON_SubDEdgePtr::Create( const class ON_SubDEdge* edge, ON__UINT_PTR direction ) { - ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge | (direction & ON_SUBD_ELEMENT_MARK_MASK) }; + ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge | (direction & ON_SUBD_COMPONENT_DIRECTION_MASK) }; return eptr; } -ON_SubDEdgePtr ON_SubDEdgePtr::Create( +const ON_SubDEdgePtr ON_SubDEdgePtr::Create( const class ON_SubDComponentPtr& edge_element ) { - return ON_SubDEdgePtr::Create(edge_element.Edge(), edge_element.ComponentMark()); + return ON_SubDEdgePtr::Create(edge_element.Edge(), edge_element.ComponentDirection()); } ////////////////////////////////////////////////////////////////////////// @@ -463,39 +451,95 @@ bool ON_SubDFacePtr::IsNull() const return (nullptr == ON_SUBD_FACE_POINTER(m_ptr)); } +bool ON_SubDFacePtr::IsNotNull() const +{ + return (nullptr != ON_SUBD_FACE_POINTER(m_ptr)); +} + ON_SubDFace* ON_SubDFacePtr::Face() const { return ON_SUBD_FACE_POINTER(m_ptr); } +unsigned int ON_SubDFacePtr::FaceId() const +{ + const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr); + return (nullptr != f) ? f->m_id : 0U; +} + +unsigned int ON_SubDFacePtr::FaceEdgeCount() const +{ + const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr); + return (nullptr != f) ? ((unsigned int)f->m_edge_count) : 0U; +} + ON__UINT_PTR ON_SubDFacePtr::FaceDirection() const { return ON_SUBD_FACE_DIRECTION(m_ptr); } -ON_ComponentStatus ON_SubDFacePtr::Status() const +const ON_ComponentStatus ON_SubDFacePtr::Status() const { const ON_SubDFace* face = ON_SUBD_FACE_POINTER(m_ptr); return (nullptr == face) ? ON_ComponentStatus::NoneSet : face->m_status; } -ON_SubDFacePtr ON_SubDFacePtr::Create( +const ON_SubDFacePtr ON_SubDFacePtr::Create( const class ON_SubDFace* face, ON__UINT_PTR direction ) { - ON_SubDFacePtr fptr = { (ON__UINT_PTR)face | (direction & ON_SUBD_ELEMENT_MARK_MASK) }; + ON_SubDFacePtr fptr = { (ON__UINT_PTR)face | (direction & ON_SUBD_COMPONENT_DIRECTION_MASK) }; return fptr; } -ON_SubDFacePtr ON_SubDFacePtr::Create( +const ON_SubDFacePtr ON_SubDFacePtr::Create( const class ON_SubDComponentPtr& face_element ) { - return ON_SubDFacePtr::Create(face_element.Face(), face_element.ComponentMark()); + return ON_SubDFacePtr::Create(face_element.Face(), face_element.ComponentDirection()); } +int ON_SubDFacePtr::Compare( + const ON_SubDFacePtr* lhs, + const ON_SubDFacePtr* rhs +) +{ + if ( nullptr == lhs ) + return 1; + if ( nullptr == rhs ) + return -1; + + if (lhs->m_ptr < rhs->m_ptr) + return -1; + if (lhs->m_ptr > rhs->m_ptr) + return 1; + + return 0; +} + +int ON_SubDFacePtr::CompareFacePointer( + const ON_SubDFacePtr* lhs, + const ON_SubDFacePtr* rhs +) +{ + if (lhs == rhs) + return 0; + if ( nullptr == lhs ) + return 1; + if ( nullptr == rhs ) + return -1; + + const ON__UINT_PTR lhs_ptr = (lhs->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); + const ON__UINT_PTR rhs_ptr = (rhs->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); + if (lhs_ptr < rhs_ptr) + return -1; + if (lhs_ptr > rhs_ptr) + return 1; + return 0; +} + ////////////////////////////////////////////////////////////////////////// // // ON_SubDComponentPtr @@ -503,45 +547,122 @@ ON_SubDFacePtr ON_SubDFacePtr::Create( bool ON_SubDComponentPtr::IsNull() const { - return (0 == (ON_SUBD_ELEMENT_POINTER_MASK && m_ptr)); + return (0 == (ON_SUBD_COMPONENT_POINTER_MASK && m_ptr)); } bool ON_SubDComponentPtr::IsNotNull() const { - if (nullptr != ON_SUBD_EDGE_POINTER(m_ptr)) + if (nullptr != ON_SUBD_COMPONENT_POINTER(m_ptr)) { - switch (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr) + switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr) { - case ON_SUBD_ELEMENT_TYPE_VERTEX: - case ON_SUBD_ELEMENT_TYPE_EDGE: - case ON_SUBD_ELEMENT_TYPE_FACE: + case ON_SUBD_COMPONENT_TYPE_VERTEX: + case ON_SUBD_COMPONENT_TYPE_EDGE: + case ON_SUBD_COMPONENT_TYPE_FACE: return true; } } return false; } +unsigned int ON_SubDComponentPtr::ComponentId() const +{ + const ON_SubDComponentBase* c = this->ComponentBase(); + return (nullptr != c) ? c->m_id : 0U; +} + +const ON_3dPoint ON_SubDComponentPtr::ControlNetCenterPoint() const +{ + switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr) + { + case ON_SUBD_COMPONENT_TYPE_VERTEX: + { + const ON_SubDVertex* v = Vertex(); + if (nullptr != v) + return v->ControlNetPoint(); + } + break; + case ON_SUBD_COMPONENT_TYPE_EDGE: + { + const ON_SubDEdge* e = Edge(); + if (nullptr != e) + return e->ControlNetCenterPoint(); + } + break; + case ON_SUBD_COMPONENT_TYPE_FACE: + { + const ON_SubDFace* f = Face(); + if (nullptr != f) + return f->ControlNetCenterPoint(); + } + break; + } + return ON_3dPoint::NanPoint; +} + +const ON_BoundingBox ON_SubDComponentPtr::ControlNetBoundingBox() const +{ + switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr) + { + case ON_SUBD_COMPONENT_TYPE_VERTEX: + { + const ON_SubDVertex* v = Vertex(); + if (nullptr != v) + return v->ControlNetBoundingBox(); + } + break; + case ON_SUBD_COMPONENT_TYPE_EDGE: + { + const ON_SubDEdge* e = Edge(); + if (nullptr != e) + return e->ControlNetBoundingBox(); + } + break; + case ON_SUBD_COMPONENT_TYPE_FACE: + { + const ON_SubDFace* f = Face(); + if (nullptr != f) + return f->ControlNetBoundingBox(); + } + break; + } + return ON_BoundingBox::NanBoundingBox; +} + + + +ON__UINT16 ON_SubDComponentPtr::Hash16FromTypeAndId() const +{ + const ON_SubDComponentBase* c = ComponentBase(); + return (0 != c) + ? ON_CRC16((ON__UINT16)(ON_SUBD_COMPONENT_TYPE_MASK & m_ptr), sizeof(c->m_id), &(c->m_id)) + : ((ON__UINT16)0U) + ; +} + +ON__UINT32 ON_SubDComponentPtr::Hash32FromPointer() const +{ + const ON__UINT_PTR ptr = (ON__UINT_PTR)ComponentBase(); + return ON_CRC32((ON__UINT32)(ON_SUBD_COMPONENT_TYPE_MASK & m_ptr), sizeof(ptr), &ptr); +} + + ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentType() const { - switch (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr) + switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr) { - case ON_SUBD_ELEMENT_TYPE_VERTEX: + case ON_SUBD_COMPONENT_TYPE_VERTEX: return ON_SubDComponentPtr::Type::Vertex; - case ON_SUBD_ELEMENT_TYPE_EDGE: + case ON_SUBD_COMPONENT_TYPE_EDGE: return ON_SubDComponentPtr::Type::Edge; - case ON_SUBD_ELEMENT_TYPE_FACE: + case ON_SUBD_COMPONENT_TYPE_FACE: return ON_SubDComponentPtr::Type::Face; } return ON_SubDComponentPtr::Type::Unset; } -ON__UINT_PTR ON_SubDComponentPtr::ComponentMark() const -{ - return ON_SUBD_ELEMENT_MARK(m_ptr); -} - -ON_ComponentStatus ON_SubDComponentPtr::Status() const +const ON_ComponentStatus ON_SubDComponentPtr::Status() const { switch (ComponentType()) { @@ -665,29 +786,151 @@ unsigned int ON_SubDComponentPtr::ClearStates( return ON_SUBD_RETURN_ERROR(0); } +bool ON_SubDComponentPtr::Mark() const +{ + const ON_SubDComponentBase* c = this->ComponentBase(); + return (nullptr != c) ? c->m_status.RuntimeMark() : false; +} -ON_SubDComponentPtr ON_SubDComponentPtr::ClearMark() const +bool ON_SubDComponentPtr::ClearMark() const +{ + const ON_SubDComponentBase* c = this->ComponentBase(); + return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false; +} + +bool ON_SubDComponentPtr::SetMark() const +{ + const ON_SubDComponentBase* c = this->ComponentBase(); + return (nullptr != c) ? c->m_status.SetRuntimeMark() : false; +} + +bool ON_SubDComponentPtr::SetMark( + bool bMark +) const +{ + const ON_SubDComponentBase* c = this->ComponentBase(); + return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false; +} + +bool ON_SubDVertexPtr::Mark() const +{ + const ON_SubDVertex* c = this->Vertex(); + return (nullptr != c) ? c->m_status.RuntimeMark() : false; +} + +bool ON_SubDVertexPtr::ClearMark() const +{ + const ON_SubDVertex* c = this->Vertex(); + return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false; +} + +bool ON_SubDVertexPtr::SetMark() const +{ + const ON_SubDVertex* c = this->Vertex(); + return (nullptr != c) ? c->m_status.SetRuntimeMark() : false; +} + +bool ON_SubDVertexPtr::SetMark( + bool bMark +) const +{ + const ON_SubDVertex* c = this->Vertex(); + return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false; +} + + +bool ON_SubDEdgePtr::Mark() const +{ + const ON_SubDEdge* c = this->Edge(); + return (nullptr != c) ? c->m_status.RuntimeMark() : false; +} + +bool ON_SubDEdgePtr::ClearMark() const +{ + const ON_SubDEdge* c = this->Edge(); + return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false; +} + +bool ON_SubDEdgePtr::SetMark() const +{ + const ON_SubDEdge* c = this->Edge(); + return (nullptr != c) ? c->m_status.SetRuntimeMark() : false; +} + +bool ON_SubDEdgePtr::SetMark( + bool bMark +) const +{ + const ON_SubDEdge* c = this->Edge(); + return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false; +} + + +bool ON_SubDFacePtr::Mark() const +{ + const ON_SubDFace* c = this->Face(); + return (nullptr != c) ? c->m_status.RuntimeMark() : false; +} + +bool ON_SubDFacePtr::ClearMark() const +{ + const ON_SubDFace* c = this->Face(); + return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false; +} + +bool ON_SubDFacePtr::SetMark() const +{ + const ON_SubDFace* c = this->Face(); + return (nullptr != c) ? c->m_status.SetRuntimeMark() : false; +} + +bool ON_SubDFacePtr::SetMark( + bool bMark +) const +{ + const ON_SubDFace* c = this->Face(); + return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false; +} + +ON__UINT_PTR ON_SubDComponentPtr::ComponentDirection() const +{ + return ON_SUBD_COMPONENT_DIRECTION(m_ptr); +} + +const ON_SubDComponentPtr ON_SubDComponentPtr::ClearComponentDirection() const { ON_SubDComponentPtr component_ptr = *this; - component_ptr.m_ptr &= (ON_SUBD_ELEMENT_POINTER_MASK | ON_SUBD_ELEMENT_TYPE_MASK); + component_ptr.m_ptr &= (ON_SUBD_COMPONENT_POINTER_MASK | ON_SUBD_COMPONENT_TYPE_MASK); return component_ptr; } -ON_SubDComponentPtr ON_SubDComponentPtr::SetMark() const +const ON_SubDComponentPtr ON_SubDComponentPtr::SetComponentDirection() const { ON_SubDComponentPtr component_ptr = *this; - component_ptr.m_ptr |= ON_SUBD_ELEMENT_MARK_MASK; + component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK; return component_ptr; } -ON_SubDComponentPtr ON_SubDComponentPtr::ToggleMark() const +const ON_SubDComponentPtr ON_SubDComponentPtr::SetComponentDirection(ON__UINT_PTR dir) const { - return (0 != (m_ptr & ON_SUBD_ELEMENT_MARK_MASK)) ? ClearMark() : SetMark(); + ON_SubDComponentPtr component_ptr = *this; + if (0 == dir) + component_ptr.m_ptr &= ~((ON__UINT_PTR)ON_SUBD_COMPONENT_DIRECTION_MASK); + else if (1 == dir) + component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK; + else + ON_SUBD_ERROR("Invalid dir parameter"); + return component_ptr; +} + +const ON_SubDComponentPtr ON_SubDComponentPtr::ToggleComponentDirection() const +{ + return (0 != (m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK)) ? ClearComponentDirection() : SetComponentDirection(); } const ON_SubDComponentPtr ON_SubDComponentPtr::CreateNull( ON_SubDComponentPtr::Type component_type, - bool bMark + ON__UINT_PTR component_direction ) { ON_SubDComponentPtr component_ptr; @@ -697,32 +940,32 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::CreateNull( component_ptr.m_ptr = 0; break; case ON_SubDComponentPtr::Type::Vertex: - component_ptr.m_ptr = ON_SUBD_ELEMENT_TYPE_VERTEX; + component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_VERTEX; break; case ON_SubDComponentPtr::Type::Edge: - component_ptr.m_ptr = ON_SUBD_ELEMENT_TYPE_EDGE; + component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_EDGE; break; case ON_SubDComponentPtr::Type::Face: - component_ptr.m_ptr = ON_SUBD_ELEMENT_TYPE_FACE; + component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_FACE; break; default: component_ptr.m_ptr = 0; break; } - if (bMark) - component_ptr.m_ptr |= ON_SUBD_ELEMENT_MARK_MASK; + if (1 == component_direction) + component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK; return component_ptr; } class ON_SubDComponentBase* ON_SubDComponentPtr::ComponentBase() const { - switch ((ON_SUBD_ELEMENT_TYPE_MASK & m_ptr)) + switch ((ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)) { - case ON_SUBD_ELEMENT_TYPE_VERTEX: - case ON_SUBD_ELEMENT_TYPE_EDGE: - case ON_SUBD_ELEMENT_TYPE_FACE: - return ((class ON_SubDComponentBase*)ON_SUBD_ELEMENT_POINTER(m_ptr)); + case ON_SUBD_COMPONENT_TYPE_VERTEX: + case ON_SUBD_COMPONENT_TYPE_EDGE: + case ON_SUBD_COMPONENT_TYPE_FACE: + return ((class ON_SubDComponentBase*)ON_SUBD_COMPONENT_POINTER(m_ptr)); } return nullptr; } @@ -730,30 +973,30 @@ class ON_SubDComponentBase* ON_SubDComponentPtr::ComponentBase() const class ON_SubDVertex* ON_SubDComponentPtr::Vertex() const { - if (ON_SUBD_ELEMENT_TYPE_VERTEX == (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr)) + if (ON_SUBD_COMPONENT_TYPE_VERTEX == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)) return ON_SUBD_VERTEX_POINTER(m_ptr); return nullptr; } class ON_SubDEdge* ON_SubDComponentPtr::Edge() const { - if (ON_SUBD_ELEMENT_TYPE_EDGE == (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr)) + if (ON_SUBD_COMPONENT_TYPE_EDGE == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)) return ON_SUBD_EDGE_POINTER(m_ptr); return nullptr; } class ON_SubDFace* ON_SubDComponentPtr::Face() const { - if (ON_SUBD_ELEMENT_TYPE_FACE == (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr)) + if (ON_SUBD_COMPONENT_TYPE_FACE == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)) return ON_SUBD_FACE_POINTER(m_ptr); return nullptr; } -ON_SubDVertexPtr ON_SubDComponentPtr::VertexPtr() const +const ON_SubDVertexPtr ON_SubDComponentPtr::VertexPtr() const { - ON__UINT_PTR element_type = ON_SUBD_ELEMENT_TYPE(m_ptr); - if ( ON_SUBD_ELEMENT_TYPE_VERTEX == element_type) - return ON_SubDVertexPtr::Create(Vertex(), ComponentMark()); + ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr); + if ( ON_SUBD_COMPONENT_TYPE_VERTEX == element_type) + return ON_SubDVertexPtr::Create(Vertex(), ComponentDirection()); if ( 0 == element_type ) return ON_SubDVertexPtr::Null; @@ -761,11 +1004,11 @@ ON_SubDVertexPtr ON_SubDComponentPtr::VertexPtr() const return ON_SUBD_RETURN_ERROR(ON_SubDVertexPtr::Null); } -ON_SubDEdgePtr ON_SubDComponentPtr::EdgePtr() const +const ON_SubDEdgePtr ON_SubDComponentPtr::EdgePtr() const { - ON__UINT_PTR element_type = ON_SUBD_ELEMENT_TYPE(m_ptr); - if ( ON_SUBD_ELEMENT_TYPE_EDGE == element_type) - return ON_SubDEdgePtr::Create(Edge(), ComponentMark()); + ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr); + if ( ON_SUBD_COMPONENT_TYPE_EDGE == element_type) + return ON_SubDEdgePtr::Create(Edge(), ComponentDirection()); if ( 0 == element_type ) return ON_SubDEdgePtr::Null; @@ -773,11 +1016,11 @@ ON_SubDEdgePtr ON_SubDComponentPtr::EdgePtr() const return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); } -ON_SubDFacePtr ON_SubDComponentPtr::FacePtr() const +const ON_SubDFacePtr ON_SubDComponentPtr::FacePtr() const { - ON__UINT_PTR element_type = ON_SUBD_ELEMENT_TYPE(m_ptr); - if ( ON_SUBD_ELEMENT_TYPE_FACE == element_type) - return ON_SubDFacePtr::Create(Face(), ComponentMark()); + ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr); + if ( ON_SUBD_COMPONENT_TYPE_FACE == element_type) + return ON_SubDFacePtr::Create(Face(), ComponentDirection()); if ( 0 == element_type ) return ON_SubDFacePtr::Null; @@ -791,7 +1034,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create( { if (nullptr != vertex) { - ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_ELEMENT_TYPE_VERTEX }; + ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_COMPONENT_TYPE_VERTEX }; return vptr; } return ON_SubDComponentPtr::Null; @@ -803,7 +1046,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create( { if (nullptr != edge) { - ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | ON_SUBD_ELEMENT_TYPE_EDGE }; + ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | ON_SUBD_COMPONENT_TYPE_EDGE }; return eptr; } return ON_SubDComponentPtr::Null; @@ -816,7 +1059,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create( { if (nullptr != face) { - ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | ON_SUBD_ELEMENT_TYPE_FACE }; + ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | ON_SUBD_COMPONENT_TYPE_FACE }; return fptr; } return ON_SubDComponentPtr::Null; @@ -829,7 +1072,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create( { if (nullptr != vertex) { - ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_ELEMENT_TYPE_VERTEX | (vertex_direction & ON_SUBD_ELEMENT_MARK_MASK) }; + ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_COMPONENT_TYPE_VERTEX | (vertex_direction & ON_SUBD_COMPONENT_DIRECTION_MASK) }; return vptr; } return ON_SubDComponentPtr::Null; @@ -842,7 +1085,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create( { if (nullptr != edge) { - ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | (ON_SUBD_ELEMENT_TYPE_EDGE | (edge_direction & ON_SUBD_ELEMENT_MARK_MASK)) }; + ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | (ON_SUBD_COMPONENT_TYPE_EDGE | (edge_direction & ON_SUBD_COMPONENT_DIRECTION_MASK)) }; return eptr; } return ON_SubDComponentPtr::Null; @@ -855,7 +1098,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create( { if (nullptr != face) { - ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | (ON_SUBD_ELEMENT_TYPE_FACE | (face_direction & ON_SUBD_ELEMENT_MARK_MASK)) }; + ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | (ON_SUBD_COMPONENT_TYPE_FACE | (face_direction & ON_SUBD_COMPONENT_DIRECTION_MASK)) }; return fptr; } return ON_SubDComponentPtr::Null; @@ -865,7 +1108,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create( ON_SubDVertexPtr vertexptr ) { - return Create(vertexptr.Vertex(), vertexptr.VertexPtrMark()); + return Create(vertexptr.Vertex(), vertexptr.VertexDirection()); } const ON_SubDComponentPtr ON_SubDComponentPtr::Create( @@ -920,98 +1163,215 @@ int ON_SubDComponentPtr::CompareType( return ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType()); } -int ON_SubDComponentPtr::Compare( + +int ON_SubDComponentPtr::CompareComponent( const ON_SubDComponentPtr* a, const ON_SubDComponentPtr* b - ) +) { - if ( a == b ) + if (a == b) return 0; - if ( nullptr == a ) - return 1; - if ( nullptr == b ) - return -1; - int rc = ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType()); + const int rc = ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType()); if (0 == rc) { - if ( a->m_ptr < b->m_ptr ) + const ON__UINT_PTR x = a->m_ptr; + const ON__UINT_PTR y = b->m_ptr; + if (x < y) return -1; - if ( a->m_ptr > b->m_ptr ) + if (x > y) + return 1; + } + return rc; +} + +int ON_SubDComponentPtr::CompareComponentAndDirection( + const ON_SubDComponentPtr* a, + const ON_SubDComponentPtr* b +) +{ + if (a == b) + return 0; + const int rc = ON_SubDComponentPtr::CompareComponent(a, b); + if (0 == rc) + { + const ON__UINT_PTR x = (a->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); + const ON__UINT_PTR y = (b->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); + if (x < y) + return -1; + if (x > y) return 1; } return rc; } -int ON_SubDComponentPoint::CompareComponentPtr( +int ON_SubDComponentPoint::CompareComponentAndDirection( const ON_SubDComponentPoint* a, const ON_SubDComponentPoint* b ) { if ( a == b) - return 0; - + return 0; if ( nullptr == a) return 1; // nullptr > non-null pointer. - if ( nullptr == b) return -1; // nullptr > non-null pointer. + // 1st: compare component type // unset < vertex < edge < face - ON__UINT_PTR x = (ON_SUBD_ELEMENT_TYPE_MASK & a->m_component_ptr.m_ptr); - ON__UINT_PTR y = (ON_SUBD_ELEMENT_TYPE_MASK & b->m_component_ptr.m_ptr); + ON__UINT_PTR x = (ON_SUBD_COMPONENT_TYPE_MASK & a->m_component_ptr.m_ptr); + ON__UINT_PTR y = (ON_SUBD_COMPONENT_TYPE_MASK & b->m_component_ptr.m_ptr); if ( x < y ) return -1; if ( x > y ) return 1; - if ( a->m_component_ptr.m_ptr < b->m_component_ptr.m_ptr ) + // 2nd: compare component pointer + x = (a->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); + y = (b->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_POINTER_MASK); + if (x < y) return -1; - if ( a->m_component_ptr.m_ptr > b->m_component_ptr.m_ptr ) + if (x > y) + return 1; + + // 3rd: compare component direction flag + x = (a->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK); + y = (b->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK); + if (x < y) + return -1; + if (x > y) return 1; return 0; } + +const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::Create(ON_SubDComponentPtr first_ptr, ON_SubDComponentPtr second_ptr) +{ + ON_SubDComponentPtrPair p; + p.m_pair[0] = first_ptr; + p.m_pair[1] = second_ptr; + return p; +} + +int ON_SubDComponentPtrPair::CompareComponent( + const ON_SubDComponentPtrPair* lhs, + const ON_SubDComponentPtrPair* rhs +) +{ + if (lhs == rhs) + return 0; + // nulls sort to end. + if (nullptr == rhs) + return -1; + if (nullptr == lhs) + return 1; + int rc = ON_SubDComponentPtr::CompareComponent(&lhs->m_pair[0], &rhs->m_pair[0]); + if (0 == rc) + rc = ON_SubDComponentPtr::CompareComponent(&lhs->m_pair[1], &rhs->m_pair[1]); + return rc; +} + +int ON_SubDComponentPtrPair::CompareComponentAndDirection( + const ON_SubDComponentPtrPair* lhs, + const ON_SubDComponentPtrPair* rhs +) +{ + if (lhs == rhs) + return 0; + // nulls sort to end. + if (nullptr == rhs) + return -1; + if (nullptr == lhs) + return 1; + int rc = ON_SubDComponentPtr::CompareComponentAndDirection(&lhs->m_pair[0], &rhs->m_pair[0]); + if (0 == rc) + rc = ON_SubDComponentPtr::CompareComponentAndDirection(&lhs->m_pair[1], &rhs->m_pair[1]); + return rc; +} + + +int ON_SubDComponentPtrPair::CompareFirstPointer( + const ON_SubDComponentPtrPair* lhs, + const ON_SubDComponentPtrPair* rhs +) +{ + if (lhs == rhs) + return 0; + // nulls sort to end. + if (nullptr == rhs) + return -1; + if (nullptr == lhs) + return 1; + const ON__UINT_PTR lhs_ptr = (ON_SUBD_COMPONENT_POINTER_MASK & lhs->m_pair[0].m_ptr); + const ON__UINT_PTR rhs_ptr = (ON_SUBD_COMPONENT_POINTER_MASK & rhs->m_pair[0].m_ptr); + if (lhs_ptr < rhs_ptr) + return -1; + if (lhs_ptr > rhs_ptr) + return 1; + return 0; +} + +ON_SubDComponentPtr::Type ON_SubDComponentPtrPair::ComponentType() const +{ + const ON_SubDComponentPtr::Type type = m_pair[0].ComponentType(); + return (type == m_pair[1].ComponentType()) ? type : ON_SubDComponentPtr::Type::Unset; +} + +const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::SwapPair() const +{ + return ON_SubDComponentPtrPair::Create(m_pair[1], m_pair[0]); +} + +const ON_SubDComponentPtr ON_SubDComponentPtrPair::First() const +{ + return m_pair[0]; +} + +const ON_SubDComponentPtr ON_SubDComponentPtrPair::Second() const +{ + return m_pair[1]; +} + ////////////////////////////////////////////////////////////////////////// // -// ON_SubDFromMeshOptions +// ON_ToSubDParameters // -ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::ConvexCornerOptionFromUnsigned( +ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::ConvexCornerOptionFromUnsigned( unsigned int convex_corner_option_as_unsigned ) { switch (convex_corner_option_as_unsigned) { - case (unsigned int)ON_SubDFromMeshOptions::ConvexCornerOption::Unset: - return ON_SubDFromMeshOptions::ConvexCornerOption::Unset; - case (unsigned int)ON_SubDFromMeshOptions::ConvexCornerOption::None: - return ON_SubDFromMeshOptions::ConvexCornerOption::None; - case (unsigned int)ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner: - return ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner; + case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::Unset: + return ON_ToSubDParameters::ConvexCornerOption::Unset; + case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::None: + return ON_ToSubDParameters::ConvexCornerOption::None; + case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner: + return ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner; default: break; } - return ON_SubDFromMeshOptions::ConvexCornerOption::Unset; + return ON_ToSubDParameters::ConvexCornerOption::Unset; } -void ON_SubDFromMeshOptions::SetConvexCornerOption( - ON_SubDFromMeshOptions::ConvexCornerOption convex_corner_option +void ON_ToSubDParameters::SetConvexCornerOption( + ON_ToSubDParameters::ConvexCornerOption convex_corner_option ) { - m_convex_corner_option = ON_SubDFromMeshOptions::ConvexCornerOptionFromUnsigned((unsigned int)convex_corner_option); + m_convex_corner_option = ON_ToSubDParameters::ConvexCornerOptionFromUnsigned((unsigned int)convex_corner_option); } -ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::ConvexCornerTest() const +ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::ConvexCornerTest() const { switch (m_convex_corner_option) { - case ON_SubDFromMeshOptions::ConvexCornerOption::Unset: - case ON_SubDFromMeshOptions::ConvexCornerOption::None: + case ON_ToSubDParameters::ConvexCornerOption::Unset: + case ON_ToSubDParameters::ConvexCornerOption::None: return m_convex_corner_option; - case ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner: + case ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner: if ( m_maximum_convex_corner_edge_count >= 2 && m_maximum_convex_corner_edge_count <= ON_SubDVertex::MaximumEdgeCount && m_maximum_convex_corner_angle_radians >= 0.0 @@ -1021,10 +1381,10 @@ ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::ConvexCornerT break; } - return ON_SubDFromMeshOptions::ConvexCornerOption::Unset; + return ON_ToSubDParameters::ConvexCornerOption::Unset; } -void ON_SubDFromMeshOptions::SetMaximumConvexCornerEdgeCount( +void ON_ToSubDParameters::SetMaximumConvexCornerEdgeCount( unsigned int maximum_convex_corner_edge_count ) { @@ -1032,12 +1392,12 @@ void ON_SubDFromMeshOptions::SetMaximumConvexCornerEdgeCount( m_maximum_convex_corner_edge_count = (unsigned short)maximum_convex_corner_edge_count; } -unsigned int ON_SubDFromMeshOptions::MaximumConvexCornerEdgeCount() const +unsigned int ON_ToSubDParameters::MaximumConvexCornerEdgeCount() const { return m_maximum_convex_corner_edge_count; } -void ON_SubDFromMeshOptions::SetMaximumConvexCornerAngleRadians( +void ON_ToSubDParameters::SetMaximumConvexCornerAngleRadians( double maximum_convex_corner_angle_radians ) { @@ -1045,13 +1405,13 @@ void ON_SubDFromMeshOptions::SetMaximumConvexCornerAngleRadians( m_maximum_convex_corner_angle_radians = maximum_convex_corner_angle_radians; } -double ON_SubDFromMeshOptions::MaximumConvexCornerAngleRadians() const +double ON_ToSubDParameters::MaximumConvexCornerAngleRadians() const { return m_maximum_convex_corner_angle_radians; } -ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::CopyConvexCornerTest( - ON_SubDFromMeshOptions source_parameters +ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::CopyConvexCornerTest( + ON_ToSubDParameters source_parameters ) { SetConvexCornerOption(source_parameters.ConvexCornerTest()); @@ -1060,19 +1420,67 @@ ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::CopyConvexCor return ConvexCornerTest(); } -void ON_SubDFromMeshOptions::SetInteriorCreaseOption( - ON_SubDFromMeshOptions::InteriorCreaseOption interior_crease_option - ) +bool ON_ToSubDParameters::InterpolateMeshVertices() const { - m_interior_crease_option = ON_SubDFromMeshOptions::InteriorCreaseOptionFromUnsigned((unsigned int)interior_crease_option); + return m_bInterpolateMeshVertices; } -ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::InteriorCreaseTest() const +void ON_ToSubDParameters::SetInterpolateMeshVertices( + bool bInterpolateMeshVertices +) +{ + // Not supported in free opennurbs + m_bInterpolateMeshVertices = false; +} + +bool ON_ToSubDParameters::MergeColinearBoundaryEdges() const +{ + // clear bit means true, set bit means false + return (0 == (ON_ToSubDParameters::MergeColinearBoundaryEdgesMask & m_merge_edges_bits)); +} + +void ON_ToSubDParameters::SetMergeColinearBoundaryEdges( + bool bAllowColinearBoundaryEdges +) +{ + const unsigned char mask = ON_ToSubDParameters::MergeColinearBoundaryEdgesMask; + if (false == bAllowColinearBoundaryEdges) + m_merge_edges_bits |= mask; // set bit + else + m_merge_edges_bits &= ~mask; // clear bit +} + +bool ON_ToSubDParameters::MergeColinearInteriorEdges() const +{ + // clear bit means true, set bit means false + return (0 == (ON_ToSubDParameters::MergeColinearInteriorEdgesMask & m_merge_edges_bits)); +} + +void ON_ToSubDParameters::SetMergeColinearInteriorEdges( + bool bAllowColinearInteriorEdges +) +{ + const unsigned char mask = ON_ToSubDParameters::MergeColinearInteriorEdgesMask; + if (false == bAllowColinearInteriorEdges) + m_merge_edges_bits |= mask; // set bit + else + m_merge_edges_bits &= ~mask; // clear bit +} + + +void ON_ToSubDParameters::SetInteriorCreaseOption( + ON_ToSubDParameters::InteriorCreaseOption interior_crease_option + ) +{ + m_interior_crease_option = ON_ToSubDParameters::InteriorCreaseOptionFromUnsigned((unsigned int)interior_crease_option); +} + +ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::InteriorCreaseTest() const { return m_interior_crease_option; } -void ON_SubDFromMeshOptions::SetMinimumCreaseAngleRadians( +void ON_ToSubDParameters::SetMinimumCreaseAngleRadians( double minimum_crease_angle_radians ) { @@ -1081,13 +1489,13 @@ void ON_SubDFromMeshOptions::SetMinimumCreaseAngleRadians( } -double ON_SubDFromMeshOptions::MinimumCreaseAngleRadians() const +double ON_ToSubDParameters::MinimumCreaseAngleRadians() const { return m_minimum_crease_angle_radians; } -ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::CopyInteriorCreaseTest( - ON_SubDFromMeshOptions source_parameters +ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::CopyInteriorCreaseTest( + ON_ToSubDParameters source_parameters ) { SetInteriorCreaseOption(source_parameters.InteriorCreaseTest()); @@ -1095,48 +1503,31 @@ ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::CopyInterio return InteriorCreaseTest(); } -ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::InteriorCreaseOptionFromUnsigned( +ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::InteriorCreaseOptionFromUnsigned( unsigned int interior_crease_option_as_unsigned ) { switch (interior_crease_option_as_unsigned) { - case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::Unset: - return ON_SubDFromMeshOptions::InteriorCreaseOption::Unset; + case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::Unset: + return ON_ToSubDParameters::InteriorCreaseOption::Unset; break; - case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::None: - return ON_SubDFromMeshOptions::InteriorCreaseOption::None; + case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::None: + return ON_ToSubDParameters::InteriorCreaseOption::None; break; - case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease: - return ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease; + case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease: + return ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease; break; - case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge: - return ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge; + case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge: + return ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge; break; default: break; } - return ON_SubDFromMeshOptions::InteriorCreaseOption::Unset; + return ON_ToSubDParameters::InteriorCreaseOption::Unset; } -ON_SubD::SubDType ON_SubDFromMeshOptions::SubDType() const -{ - return - (ON_SubD::SubDType::Unset == m_subd_type) - ? ON_SubD::DefaultSubDType() - : m_subd_type; -} - -void ON_SubDFromMeshOptions::SetSubDType( - ON_SubD::SubDType subd_type - ) -{ - if (subd_type == ON_SubD::SubDTypeFromUnsigned((unsigned int)subd_type) ) - { - m_subd_type = subd_type; - } -} ////////////////////////////////////////////////////////////////////////// // // ON_SubDVertex @@ -1210,6 +1601,62 @@ unsigned int ON_SubDVertex::EdgeArrayIndex( return ON_UNSET_UINT_INDEX; } +unsigned int ON_SubDVertex::MarkedEdgeCount() const +{ + unsigned int mark_count = 0; + for (unsigned short vei = 0; vei < m_edge_count; ++vei) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); + if (nullptr != e && e->m_status.RuntimeMark()) + ++mark_count; + } + return mark_count; +} + +unsigned int ON_SubDVertex::MarkedFaceCount() const +{ + unsigned int mark_count = 0; + for (unsigned short vfi = 0; vfi < m_face_count; ++vfi) + { + const ON_SubDFace* f = m_faces[vfi]; + if (nullptr != f && f->m_status.RuntimeMark()) + ++mark_count; + } + return mark_count; +} + + +unsigned int ON_SubDEdge::MarkedVertexCount() const +{ + unsigned int mark_count = 0; + for (unsigned evi = 0; evi < 2; ++evi) + { + const ON_SubDVertex* v = m_vertex[evi]; + if (nullptr != v && v->m_status.RuntimeMark()) + ++mark_count; + } + return mark_count; +} + +unsigned int ON_SubDEdge::MarkedFaceCount() const +{ + unsigned int mark_count = 0; + const ON_SubDFacePtr* fptr = m_face2; + for (unsigned short efi = 0; efi < m_face_count; ++efi, ++fptr) + { + if (2 == efi) + { + fptr = m_facex; + if (nullptr == fptr) + break; + } + const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr->m_ptr); + if (nullptr != f && f->m_status.RuntimeMark()) + ++mark_count; + } + return mark_count; +} + unsigned int ON_SubDVertex::FaceCount() const { return m_face_count; @@ -1245,101 +1692,29 @@ unsigned int ON_SubDVertex::FaceArrayIndex( return ON_UNSET_UINT_INDEX; } - -ON_SubD::FacetType ON_SubDVertex::FirstFaceFacetType() const +unsigned int ON_SubDVertex::ReplaceFaceInArray(const ON_SubDFace * old_face, const ON_SubDFace * new_face) { - if (0 == m_face_count) - return ON_SubD::FacetType::Unset; - - if (nullptr == m_faces) - return ON_SubD::FacetType::Unset; - - if (nullptr == m_faces[0]) - return ON_SubD::FacetType::Unset; - - if (3 == m_faces[0]->m_edge_count) - return ON_SubD::FacetType::Tri; - - if (4 == m_faces[0]->m_edge_count) - return ON_SubD::FacetType::Quad; - - return ON_SubD::FacetType::Unset; + unsigned vfi = (nullptr != old_face && old_face != new_face) ? FaceArrayIndex(old_face) : ON_UNSET_UINT_INDEX; + if (ON_UNSET_UINT_INDEX == vfi) + return ON_UNSET_UINT_INDEX; + if (nullptr != new_face) + { + m_faces[vfi] = new_face; + } + else + { + const unsigned c = (unsigned)(m_face_count--); + while (++vfi < c) + m_faces[vfi - 1] = m_faces[vfi]; + } + return vfi; } - -bool ON_SubDVertex::IsTagged() const +const ON_3dPoint ON_SubDVertex::ControlNetPoint() const { - return (ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag); + return ON_3dPoint(m_P); } -//static bool AllFacesHaveCorrectEdgeCount( -// unsigned short correct_edge_count, -// unsigned int face_count, -// const ON_SubDFace*const* vertex_faces -// ) -//{ -// if (nullptr == vertex_faces) -// return false; -// for (unsigned int vfi = 0; vfi < face_count; vfi++) -// { -// const ON_SubDFace* f = vertex_faces[vfi]; -// if (nullptr == f) -// return false; -// if (correct_edge_count != f->m_edge_count) -// return false; -// } -// return true; -//} - -//bool ON_SubDVertex::IsOrdinary( -// ON_SubD::SubDType subdivision_type, -// ON_SubD::VertexTag vertex_tag_filter, -// bool bTestFaces -// ) const -//{ -// if (ON_SubD::VertexTag::Unset == vertex_tag_filter || vertex_tag_filter == m_vertex_tag) -// { -// if (ON_SubD::VertexTag::Smooth == m_vertex_tag) -// { -// if (m_edge_count == m_face_count) -// { -// if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) -// { -// if (4 == m_edge_count) -// return bTestFaces ? AllFacesHaveCorrectEdgeCount(4, m_face_count, m_faces) : true; -// } -// else if (ON_SubD::SubDType::TriLoopWarren == subdivision_type) -// { -// if (6 == m_edge_count) -// return bTestFaces ? AllFacesHaveCorrectEdgeCount(3, m_face_count, m_faces) : true; -// } -// } -// } -// else if (ON_SubD::VertexTag::Crease == m_vertex_tag) -// { -// if (m_edge_count == m_face_count + 1) -// { -// if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) -// { -// if (3 == m_edge_count && 2 == EdgeCount(ON_SubD::EdgeTag::Crease)) -// { -// return bTestFaces ? AllFacesHaveCorrectEdgeCount(4, m_face_count, m_faces) : true; -// } -// } -// else if (ON_SubD::SubDType::TriLoopWarren == subdivision_type) -// { -// if (4 == m_edge_count && 2 == EdgeCount(ON_SubD::EdgeTag::Crease)) -// { -// return bTestFaces ? AllFacesHaveCorrectEdgeCount(3, m_face_count, m_faces) : true; -// } -// } -// } -// } -// } -// -// return false; -//} - bool ON_SubDVertex::IsSmooth() const { return (ON_SubD::VertexTag::Smooth == m_vertex_tag); @@ -1365,12 +1740,12 @@ bool ON_SubDVertex::IsCreaseOrCorner() const return (ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag); } -bool ON_SubDVertex::IsCreaseOrCornerOrDart() const +bool ON_SubDVertex::IsDartOrCreaseOrCorner() const { return ( - ON_SubD::VertexTag::Crease == m_vertex_tag + ON_SubD::VertexTag::Dart == m_vertex_tag + || ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag - || ON_SubD::VertexTag::Dart == m_vertex_tag ); } @@ -1386,6 +1761,54 @@ bool ON_SubDVertex::IsSmoothOrCrease() const return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Crease == m_vertex_tag); } +const ON_SubDVertexEdgeProperties ON_SubDVertex::EdgeProperties() const +{ + ON_SubDVertexEdgeProperties ep; + + bool bFirstEdge = true; + const unsigned short edge_count = m_edge_count; + for (unsigned short vei = 0; vei < edge_count; vei++) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); + if (nullptr == e) + { + ep.m_null_edge_count++; + continue; + } + + if (e->IsCrease()) + ep.m_crease_edge_count++; + else if (e->IsSmooth()) + ep.m_smooth_edge_count++; + else + ep.m_unset_edge_count++; + + const unsigned short edge_face_count = e->m_face_count; + if (bFirstEdge) + { + bFirstEdge = false; + ep.m_min_edge_face_count = edge_face_count; + ep.m_max_edge_face_count = edge_face_count; + } + else if (edge_face_count < ep.m_min_edge_face_count) + ep.m_min_edge_face_count = edge_face_count; + else if (edge_face_count > ep.m_max_edge_face_count) + ep.m_max_edge_face_count = edge_face_count; + + if (0 == edge_face_count) + ep.m_wire_edge_count++; + else if (1 == edge_face_count) + ep.m_boundary_edge_count++; + else if (2 == edge_face_count) + ep.m_interior_edge_count++; + else + ep.m_nonmanifold_edge_count++; + + } + return ep; +} + + bool ON_SubDEdge::IsCrease() const { return (ON_SubD::EdgeTag::Crease == m_edge_tag) ? true : false; @@ -1426,7 +1849,7 @@ unsigned int ON_SubDEdge::DartCount() const bool ON_SubDEdge::IsSmooth() const { - return (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::X == m_edge_tag) ? true : false; + return (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::SmoothX == m_edge_tag) ? true : false; } bool ON_SubDEdge::IsSmoothNotX() const @@ -1436,12 +1859,122 @@ bool ON_SubDEdge::IsSmoothNotX() const bool ON_SubDEdge::IsSmoothX() const { - return (ON_SubD::EdgeTag::X == m_edge_tag) ? true : false; + return (ON_SubD::EdgeTag::SmoothX == m_edge_tag) ? true : false; } -bool ON_SubDVertex::IsStandard( - ON_SubD::SubDType subdivision_type - ) const +bool ON_SubDVertex::IsSingleSectorVertex() const +{ + const bool bIsCreaseOrCorner = IsCreaseOrCorner(); + if (bIsCreaseOrCorner) + { + if (m_face_count < 1 || m_face_count + 1 != m_edge_count) + return false; + } + else if (IsSmoothOrDart()) + { + if (m_face_count < 2 || m_edge_count!= m_face_count) + return false; + } + else + return false; + + + unsigned short boundary_crease_count = 0; + unsigned short interior_crease_count = 0; + unsigned short interior_smooth_count = 0; + for (unsigned short vei = 0; vei < m_edge_count; ++vei) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(this->m_edges[vei].m_ptr); + if (nullptr == e) + return false; + + if (e->IsSmooth()) + { + if (2 == e->m_face_count) + { + ++interior_smooth_count; + continue; + } + } + else if (ON_SubD::EdgeTag::Crease == e->m_edge_tag) + { + if (2 == e->m_face_count) + { + ++interior_crease_count; + if (ON_SubD::VertexTag::Dart == m_vertex_tag && 1 == interior_crease_count) + continue; + } + else if (1 == e->m_face_count) + { + ++boundary_crease_count; + if (bIsCreaseOrCorner && boundary_crease_count <= 2) + continue; + } + } + + return false; + } + + if (bIsCreaseOrCorner) + { + if (2 == boundary_crease_count && 2+interior_smooth_count == m_edge_count) + return true; + } + else if (ON_SubD::VertexTag::Dart == m_vertex_tag) + { + if (1 == interior_crease_count && 1+interior_smooth_count == m_edge_count) + return true; + } + else if (ON_SubD::VertexTag::Smooth == m_vertex_tag) + { + if (interior_smooth_count == m_edge_count) + return true; + } + + return false; +} + +bool ON_SubDVertex::IsManifoldBoundaryVertex() const +{ + return IsCreaseOrCorner() && IsSingleSectorVertex(); +} + + +bool ON_SubDVertex::HasInteriorVertexTopology() const +{ + if (m_edge_count >= 2 && m_edge_count == m_face_count && nullptr != m_edges && nullptr != m_faces) + { + for (unsigned short vei = 0; vei < m_edge_count; ++vei) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); + if (nullptr == e || 2 != e->m_face_count) + return false; + } + return true; + } + return false; +} + +bool ON_SubDVertex::HasBoundaryVertexTopology() const +{ + if (m_edge_count >= 2 && m_edge_count == m_face_count+1 && nullptr != m_edges && nullptr != m_faces) + { + unsigned boundary_count = 0; + for (unsigned short vei = 0; vei < m_edge_count; ++vei) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); + if (nullptr == e || 0 == e->m_face_count || e->m_face_count > 2) + return false; + if (1 == e->m_face_count) + ++boundary_count; + } + if (2 == boundary_count) + return true; + } + return false; +} + +bool ON_SubDVertex::IsStandard() const { if (nullptr == m_edges) return false; @@ -1457,12 +1990,7 @@ bool ON_SubDVertex::IsStandard( if (face_count != ON_SubDSectorType::SectorFaceCountFromEdgeCount(m_vertex_tag, edge_count)) return false; - const unsigned short f_edge_count - = (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) - ? 4 - : ((ON_SubD::SubDType::TriLoopWarren == subdivision_type) ? 3 : 0); - if (0 == f_edge_count) - return false; + const unsigned short f_edge_count = 4; unsigned int crease_edge_face_count = ON_UNSET_UINT_INDEX; bool bTaggedVertex = false; @@ -1569,7 +2097,7 @@ bool ON_SubDVertex::IsStandard( creased_edge_count++; if (creased_edge_count > 2) return false; - if (false == other_vertex->IsCreaseOrCornerOrDart()) + if (false == other_vertex->IsDartOrCreaseOrCorner()) return false; } else @@ -1602,53 +2130,101 @@ bool ON_SubDVertex::IsStandard( } -unsigned int ON_SubDEdge::EdgeFlags() const +unsigned int ON_SubDEdge::EdgeAttributes() const { + unsigned int edge_topology_attributes = 0U; - if (nullptr == m_vertex[0] || nullptr == m_vertex[1] || m_vertex[0] == m_vertex[1]) - return ON_ComponentAttributes::EdgeFlags::Damaged; + if (nullptr == m_vertex[0] || nullptr == m_vertex[1]) + { + edge_topology_attributes |= ON_ComponentAttributes::Damaged; + } + else + { + const double* P[2] = { m_vertex[0]->m_P, m_vertex[1]->m_P }; + if ( + fabs(P[0][0]) < ON_UNSET_POSITIVE_VALUE + && fabs(P[0][1]) < ON_UNSET_POSITIVE_VALUE + && fabs(P[0][2]) < ON_UNSET_POSITIVE_VALUE + && fabs(P[1][0]) < ON_UNSET_POSITIVE_VALUE + && fabs(P[1][1]) < ON_UNSET_POSITIVE_VALUE + && fabs(P[1][2]) < ON_UNSET_POSITIVE_VALUE + ) + { + if (P[0][0] == P[1][0] && P[0][1] == P[1][1] && P[0][2] == P[1][2]) + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::ZeroLength; + else + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::NonzeroLength; + } + if (m_vertex[0] != m_vertex[1]) + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Open; + else + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Closed; + } - unsigned int edge_topology_attributes = ON_ComponentAttributes::EdgeFlags::Open; - - const double* P[2] = { m_vertex[0]->m_P, m_vertex[1]->m_P }; - if (P[0][0] == P[1][0] && P[0][1] == P[1][1] && P[0][2] == P[1][2]) - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Degenerate; - + const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr),ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) }; switch (m_face_count) { case 0: - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Wire; + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Wire; break; + case 1: - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Boundary; + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Boundary; + if ( nullptr == f[0]) + edge_topology_attributes |= ON_ComponentAttributes::Damaged; break; + case 2: - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Interior; + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Interior; + + if (IsSmooth()) + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSmooth; + else if (IsCrease()) + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorCrease; + + if (nullptr != f[0] && nullptr != f[1]) { - const ON_SubDFace* face[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) }; - if (nullptr == face[0] || nullptr == face[1] || face[0] == face[1]) - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Damaged; + if (ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr)) + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorNotOriented; + else + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorOriented; + if (f[0] != f[1]) + { + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorTwoFaced; + } else { - if (IsSmooth()) - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Smooth; - else if ( m_vertex[0]->IsDart() || m_vertex[1]->IsDart() ) - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Dart; - else - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Crease; - - ON__UINT_PTR dir[2] = { ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr), ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr) }; - if (dir[0] == dir[1]) - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::NotOriented; - else - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Oriented; + const unsigned int fecount = f[0]->EdgeCount(); + const unsigned int fei0 = f[0]->EdgeArrayIndex(this); + if (fecount > 2 && fei0 < fecount) + { + for (unsigned int fei1 = fei0 + 1; fei1 < fecount; ++fei1) + { + if (this == f[0]->Edge(fei1)) + { + if (f[0]->EdgeDirection(fei0) != f[0]->EdgeDirection(fei1)) + { + if ( fei1+1 == fei0 || (0 == fei0 && fei1+1 == fecount)) + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSlit; + else + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSeam; + } + break; + } + } + } } } + else + { + edge_topology_attributes |= ON_ComponentAttributes::Damaged; + } break; + default: - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Nonmanifold; - if ( nullptr == m_facex ) - edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Damaged; + edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Nonmanifold; + if ( nullptr == f[0] || nullptr == f[1] || nullptr == m_facex ) + edge_topology_attributes |= ON_ComponentAttributes::Damaged; break; } @@ -1764,7 +2340,7 @@ void ON_SubDComponentBase::CopyBaseFrom( *this = *src; m_subd_point1 = nullptr; - ON_SUBD_CACHE_CLEAR_LIMIT_FLAG(m_saved_points_flags); + Internal_ClearSurfacePointFlag(); } void ON_SubDEdge::CopyFrom( @@ -1820,14 +2396,14 @@ unsigned int ON_SubDEdge::TaggedEndIndex() const for (unsigned int evi = 0; evi < 2; evi++) { const ON_SubDVertex* v = m_vertex[evi]; - if (nullptr == v || false == v->IsTagged()) + if (nullptr == v || false == v->IsDartOrCreaseOrCorner()) continue; tagged_end_index = (3 == tagged_end_index) ? evi : 2; } return tagged_end_index; } -ON_SubDFacePtr ON_SubDEdge::FacePtr( +const ON_SubDFacePtr ON_SubDEdge::FacePtr( unsigned int i ) const { @@ -1840,6 +2416,22 @@ unsigned int ON_SubDEdge::FaceCount() const return m_face_count; } +bool ON_SubDEdge::IsInteriorEdge() const +{ + for (;;) + { + if (2 != m_face_count) + break; + if (ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr)) + break; + if (nullptr == ON_SUBD_FACE_POINTER(m_face2[0].m_ptr)) + break; + if (nullptr == ON_SUBD_FACE_POINTER(m_face2[1].m_ptr)) + break; + } + return false; +} + const class ON_SubDFace* ON_SubDEdge::Face( unsigned int i ) const @@ -1854,7 +2446,7 @@ ON__UINT_PTR ON_SubDEdge::FaceDirection( return (i < 2) ? ON_SUBD_FACE_DIRECTION(m_face2[i].m_ptr) : ((i < m_face_count) ? ON_SUBD_FACE_DIRECTION(m_facex[i - 2].m_ptr) : 0); } -ON_SubDFacePtr ON_SubDEdge::FacePtr( +const ON_SubDFacePtr ON_SubDEdge::FacePtrFromFace( const class ON_SubDFace* f ) const { @@ -1907,6 +2499,45 @@ unsigned int ON_SubDEdge::FaceArrayIndex( return ON_UNSET_UINT_INDEX; } +unsigned int ON_SubDEdge::ReplaceFaceInArray(const ON_SubDFace * old_face, const ON_SubDFace * new_face) +{ + unsigned efi = (nullptr != old_face && old_face != new_face) ? FaceArrayIndex(old_face) : ON_UNSET_UINT_INDEX; + if (ON_UNSET_UINT_INDEX == efi) + return ON_UNSET_UINT_INDEX; + ON_SubDFacePtr* fptr = (efi < 2) ? &m_face2[efi] : &m_facex[efi - 2]; + if (nullptr != new_face) + { + *fptr = ON_SubDFacePtr::Create(new_face, fptr->FaceDirection()); + } + else + { + unsigned efi1 = efi + 1; + ON_SubDFacePtr* fptr1 = (efi1 < 2) ? &m_face2[efi1] : &m_facex[efi1 - 2]; + for (const unsigned c = (unsigned)(m_face_count--); efi1 < c; ++efi, ++efi1) + { + if (2 == efi) + fptr = m_facex; + else if (2 == efi1) + fptr1 = m_facex; + *fptr++ = *fptr1++; + } + } + return efi; +} + + + +unsigned int ON_SubDEdge::VertexArrayIndex(const ON_SubDVertex * v) const +{ + if (nullptr == v || m_vertex[0] == m_vertex[1]) + return ON_UNSET_UINT_INDEX; + if (v == m_vertex[0]) + return 0; + if (v == m_vertex[1]) + return 1; + return ON_UNSET_UINT_INDEX; +} + const ON_SubDFace* ON_SubDEdge::NeighborFace( const ON_SubDFace* face, @@ -1960,6 +2591,44 @@ const ON_SubDFacePtr ON_SubDEdge::NeighborFacePtr( return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null); } +const ON_SubDEdgePtr ON_SubDEdge::AdjacentEdgePtr( + unsigned int edge_vertex_index, + unsigned int i +) const +{ + if ( edge_vertex_index >= 2 ) + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + const ON_SubDFacePtr fptr = FacePtr(i); + const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr.m_ptr); + if (nullptr == f) + return ON_SubDEdgePtr::Null; + const unsigned int fe_count = f->m_edge_count; + if ( fe_count < 3 || fe_count > ON_SubDFace::MaximumEdgeCount) + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + const unsigned int fei = f->EdgeArrayIndex(this); + if( fei >= fe_count) + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + const ON_SubDEdgePtr eptr = f->EdgePtr(fei); + if ( this != ON_SUBD_EDGE_POINTER(eptr.m_ptr)) + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + const ON__UINT_PTR dir = ON_SUBD_FACE_DIRECTION(fptr.m_ptr); + if (dir != ON_SUBD_EDGE_DIRECTION(eptr.m_ptr)) + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + const unsigned int fei1 + = (dir == ((ON__UINT_PTR)edge_vertex_index)) + ? ((fei + fe_count - 1) % fe_count) + : ((fei + 1) % fe_count) + ; + return f->EdgePtr(fei1); +} + +const ON_SubDEdge* ON_SubDEdge::AdjacentEdge( + unsigned int edge_vertex_index, + unsigned int i + ) const +{ + return ON_SUBD_EDGE_POINTER(AdjacentEdgePtr(edge_vertex_index, i).m_ptr); +} const class ON_SubDVertex* ON_SubDEdge::Vertex( unsigned int i @@ -1986,18 +2655,62 @@ const ON_SubDVertex* ON_SubDEdge::OtherEndVertex( return nullptr; } -const ON_3dPoint ON_SubDEdge::EndPoint( unsigned int i) const +const ON_SubDEdgePtr ON_SubDEdge::FromVertices( + const ON_SubDVertex* vertex0, + const ON_SubDVertex* vertex1 +) +{ + if (nullptr != vertex0 && nullptr != vertex1 && vertex0 != vertex1 && nullptr != vertex0->m_edges) + { + for (unsigned short vei = 0; vei < vertex0->m_edge_count; vei++) + { + const ON_SubDEdgePtr eptr = vertex0->m_edges[vei]; + if (vertex1 == eptr.RelativeVertex(1) && vertex0 == eptr.RelativeVertex(0)) + return eptr; + } + } + return ON_SubDEdgePtr::Null; +} + +const ON_SubDEdge* ON_SubDEdge::FromVertices( + const ON_SubDVertex* vertex0, + const ON_SubDVertex* vertex1, + bool bIgnoreOrientation +) +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(ON_SubDEdge::FromVertices(vertex0, vertex1).m_ptr); + if ( nullptr != e && false == bIgnoreOrientation && vertex0 != e->m_vertex[0] ) + e = nullptr; + return e; +} + +const ON_3dPoint ON_SubDEdge::ControlNetPoint( unsigned int i) const { if (i >= 2 || nullptr == m_vertex[i]) return ON_3dPoint::NanPoint; return (ON_3dPoint(m_vertex[i]->m_P)); } -const ON_3dVector ON_SubDEdge::Direction() const +const ON_3dVector ON_SubDEdge::ControlNetDirection() const { if (nullptr == m_vertex[0] || nullptr == m_vertex[1]) return ON_3dVector::NanVector; - return (ON_3dPoint(m_vertex[1]->m_P) - ON_3dPoint(m_vertex[0]->m_P)); + const ON_3dPoint P[2] = { ON_3dPoint(m_vertex[0]->m_P) ,ON_3dPoint(m_vertex[1]->m_P) }; + return (P[0].IsValid() && P[1].IsValid()) ? (P[1] - P[0]) : ON_3dVector::NanVector; +} + +const ON_3dVector ON_SubDEdge::ControlNetDirectionFrom( + const ON_SubDVertex* v +) const +{ + if (nullptr != v) + { + if (v == m_vertex[0] && nullptr != m_vertex[1]) + return ControlNetDirection(); + if (v == m_vertex[1] && nullptr != m_vertex[0]) + return -ControlNetDirection(); + } + return ON_3dVector::NanVector; } @@ -2075,7 +2788,7 @@ void ON_SubDFace::CopyFrom( } } -ON_SubDEdgePtr ON_SubDFace::EdgePtr( +const ON_SubDEdgePtr ON_SubDFace::EdgePtr( unsigned int i ) const { @@ -2087,6 +2800,112 @@ unsigned int ON_SubDFace::EdgeCount() const return m_edge_count; } +unsigned int ON_SubDFace::MarkedEdgeCount() const +{ + unsigned int marked_edge_count = 0; + const ON_SubDEdgePtr* eptr = m_edge4; + for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr) + { + if (4 == fei) + { + eptr = m_edgex; + if (nullptr == eptr) + break; + } + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); + if (nullptr != e && e->m_status.RuntimeMark()) + ++marked_edge_count; + } + return marked_edge_count; +} + + +unsigned int ON_SubDFace::SetEdgeMarks(bool bMark) const +{ + bMark = bMark ? true : false; // so exact compare can be used + unsigned int changed_mark_count = 0; + const ON_SubDEdgePtr* eptr = m_edge4; + for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr) + { + if (4 == fei) + { + eptr = m_edgex; + if (nullptr == eptr) + break; + } + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); + if (nullptr != e && bMark != e->m_status.RuntimeMark()) + { + e->m_status.SetRuntimeMark(bMark); + ++changed_mark_count; + } + } + return changed_mark_count; +} + +unsigned int ON_SubDFace::GetEdgeArray( + ON_SimpleArray< ON_SubDEdgePtr >& face_edge_array +) const +{ + face_edge_array.SetCount(0); + const unsigned edge_count = m_edge_count; + face_edge_array.Reserve(edge_count); + face_edge_array.Append(edge_count <= 4 ? edge_count : 4U, m_edge4); + if (edge_count > 4) + { + if ( nullptr != m_edgex ) + face_edge_array.Append(edge_count-4U, m_edgex); + else + { + for (unsigned fei = 4; fei < edge_count; ++fei) + face_edge_array.Append(ON_SubDEdgePtr::Null); + } + } + return edge_count; +} + +unsigned int ON_SubDFace::SetVertexMarks(bool bMark) const +{ + bMark = bMark ? true : false; // so exact compare can be used + unsigned int changed_mark_count = 0; + const ON_SubDEdgePtr* eptr = m_edge4; + for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr) + { + if (4 == fei) + { + eptr = m_edgex; + if (nullptr == eptr) + break; + } + const ON_SubDVertex* v = eptr->RelativeVertex(0); + if (nullptr != v && bMark != v->m_status.RuntimeMark()) + { + v->m_status.SetRuntimeMark(bMark); + ++changed_mark_count; + } + } + return changed_mark_count; +} + +unsigned int ON_SubDFace::MarkedVertexCount() const +{ + unsigned int marked_vertex_count = 0; + const ON_SubDEdgePtr* eptr = m_edge4; + for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr) + { + if (4 == fei) + { + eptr = m_edgex; + if (nullptr == eptr) + break; + } + const ON_SubDVertex* v = eptr->RelativeVertex(0); + if (nullptr != v && v->m_status.RuntimeMark()) + ++marked_vertex_count; + } + return marked_vertex_count; +} + const class ON_SubDVertex* ON_SubDFace::Vertex( unsigned int i ) const @@ -2096,6 +2915,14 @@ const class ON_SubDVertex* ON_SubDFace::Vertex( return (nullptr != (e = ON_SUBD_EDGE_POINTER(edge_ptr))) ? e->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)] : nullptr; } +const ON_3dPoint ON_SubDFace::ControlNetPoint(unsigned int i) const +{ + const ON_SubDEdge* e; + const ON__UINT_PTR edge_ptr = (i < 4) ? m_edge4[i].m_ptr : ((i < m_edge_count) ? m_edgex[i - 4].m_ptr : 0); + const ON_SubDVertex* v = (nullptr != (e = ON_SUBD_EDGE_POINTER(edge_ptr))) ? e->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)] : nullptr; + return (nullptr != v) ? ON_3dPoint(v->m_P) : ON_3dPoint::NanPoint; +} + const ON_SubDVertex* ON_SubDFace::QuadOppositeVertex( const ON_SubDVertex* vertex ) const @@ -2174,7 +3001,7 @@ ON__UINT_PTR ON_SubDFace::EdgeDirection( } -ON_SubDEdgePtr ON_SubDFace::EdgePtr( +const ON_SubDEdgePtr ON_SubDFace::EdgePtrFromEdge( const class ON_SubDEdge* e ) const { @@ -2263,6 +3090,42 @@ unsigned int ON_SubDFace::NextEdgeArrayIndex( return (edge_array_index < edge_count) ? ((edge_array_index + 1) % edge_count) : ON_UNSET_UINT_INDEX; } +bool ON_SubDVertex::RemoveEdgeFromArray(const ON_SubDEdge * e) +{ + if (nullptr == e || 0 == m_edge_count || nullptr == m_edges) + return ON_SUBD_RETURN_ERROR(false); + unsigned short new_count = 0; + for (unsigned short vei = 0; vei < m_edge_count; ++vei) + { + const ON_SubDEdgePtr eptr = m_edges[vei]; + if (e == ON_SUBD_EDGE_POINTER(eptr.m_ptr)) + continue; + m_edges[new_count++] = eptr; + } + if (new_count == m_edge_count) + return false; + m_edge_count = new_count; + return true; +} + +bool ON_SubDVertex::RemoveFaceFromArray(const ON_SubDFace * f) +{ + if (nullptr == f || 0 == m_face_count || nullptr == m_faces) + return ON_SUBD_RETURN_ERROR(false); + unsigned short new_count = 0; + for (unsigned short vfi = 0; vfi < m_face_count; ++vfi) + { + const ON_SubDFace* vf = m_faces[vfi]; + if (f == vf) + continue; + m_faces[new_count++] = vf; + } + if (new_count == m_face_count) + return false; + m_face_count = new_count; + return true; +} + bool ON_SubDEdge::RemoveFaceFromArray( const ON_SubDFace* f ) @@ -2443,6 +3306,56 @@ bool ON_SubDFace::RemoveEdgeFromArray( return true; } +bool ON_SubDFace::RotateEdgeArray( + unsigned int fei0 +) +{ + if (0 == fei0) + return true; + + const unsigned int edge_count = m_edge_count; + if (edge_count < 2 || edge_count > ON_SubDFace::MaximumEdgeCount || fei0 >= edge_count) + return false; + + ON_SubDEdgePtr stack_eptr[8]; + ON_SubDEdgePtr* eptr + = (edge_count*sizeof(stack_eptr[0]) > sizeof(stack_eptr)) + ? ((ON_SubDEdgePtr*)onmalloc(edge_count * sizeof(eptr[0]))) + : stack_eptr; + if (nullptr == eptr) + return false; + + ON_SubDEdgePtr* feptr = m_edge4; + for (unsigned int fei = 0; fei < edge_count; fei++) + { + if (4 == fei) + { + feptr = m_edgex; + if (nullptr == feptr) + { + if ( eptr != stack_eptr ) + onfree(eptr); + return false; + } + } + eptr[fei] = *feptr++; + } + + feptr = m_edge4; + for (unsigned int fei = 0; fei < edge_count; fei++) + { + if (4 == fei) + feptr = m_edgex; + *feptr++ = eptr[(fei + fei0) % edge_count]; + } + if ( eptr != stack_eptr ) + onfree(eptr); + + return true; +} + + + bool ON_SubDFace::RemoveEdgeFromArray( const ON_SubDEdge* e @@ -2461,6 +3374,7 @@ bool ON_SubDFace::RemoveEdgeFromArray( for (i++; i < m_edge_count; i++) m_edge4[i - 1] = m_edge4[i]; m_edge_count--; + m_edge4[m_edge_count] = ON_SubDEdgePtr::Null; return true; } } @@ -2477,6 +3391,7 @@ bool ON_SubDFace::RemoveEdgeFromArray( for (i = 5; i < m_edge_count; i++) m_edgex[i - 5] = m_edgex[i - 4]; m_edge_count--; + m_edgex[m_edge_count-4] = ON_SubDEdgePtr::Null; return true; } } @@ -2487,6 +3402,7 @@ bool ON_SubDFace::RemoveEdgeFromArray( for (i++; i < m_edge_count; i++) m_edgex[i - 5] = m_edgex[i - 4]; m_edge_count--; + m_edgex[m_edge_count-4] = ON_SubDEdgePtr::Null; return true; } } @@ -2571,6 +3487,77 @@ ON__UINT64 ON_SubD::RuntimeSerialNumber() const } +ON__UINT64 ON_SubD::ContentSerialNumber() const +{ + ON_SubDimple* subdimple = m_subdimple_sp.get(); + return (nullptr != subdimple) ? subdimple->ContentSerialNumber() : 0; +} + +ON__UINT64 ON_SubD::ChangeContentSerialNumberForExperts() +{ + if (this == &ON_SubD::Empty) + return 0; + ON_SubDimple* subdimple = m_subdimple_sp.get(); + return (nullptr != subdimple) ? subdimple->ChangeContentSerialNumber() : 0; +} + +ON_SubDComponentLocation ON_SubD::ToggleSubDAppearanceValue(ON_SubDComponentLocation subd_appearance) +{ + if (ON_SubDComponentLocation::Surface == subd_appearance) + return ON_SubDComponentLocation::ControlNet; + if (ON_SubDComponentLocation::ControlNet == subd_appearance) + return ON_SubDComponentLocation::Surface; + return subd_appearance; +} + +ON_SubDComponentLocation ON_SubDMeshFragmentIterator::SubDAppearance() const +{ + return + (ON_SubDComponentLocation::Surface == m_subd_appearance_override || ON_SubDComponentLocation::ControlNet == m_subd_appearance_override) + ? m_subd_appearance_override + : SubD().SubDAppearance(); +} + +void ON_SubDMeshFragmentIterator::SetSubDAppearanceOverride(ON_SubDComponentLocation subd_appearance_override) +{ + if (m_subd_appearance_override != subd_appearance_override ) + m_subd_appearance_override = subd_appearance_override; +} + +ON_SubDComponentLocation ON_SubD::SubDAppearance() const +{ + const ON_SubDimple* subdimple = this->SubDimple(); + return (nullptr != subdimple) ? subdimple->SubDAppearance() : ON_SubD::DefaultSubDAppearance; +} + +ON_SubDComponentLocation ON_SubDimple::SubDAppearance() const +{ + return m_subd_appearance; +} + +void ON_SubD::SetSubDAppearance(ON_SubDComponentLocation subd_appearance) const +{ + if ( + subd_appearance != SubDAppearance() + && (ON_SubDComponentLocation::Surface == subd_appearance || ON_SubDComponentLocation::ControlNet == subd_appearance) + ) + { + const ON_SubDimple* subdimple = const_cast(this)->SubDimple(true); + if (nullptr != subdimple) + subdimple->SetSubDAppearance(subd_appearance); + } +} + +void ON_SubDimple::SetSubDAppearance(ON_SubDComponentLocation subd_appearance) const +{ + if ( + subd_appearance != m_subd_appearance + && (ON_SubDComponentLocation::Surface == subd_appearance || ON_SubDComponentLocation::ControlNet == subd_appearance) + ) + { + m_subd_appearance = subd_appearance; + } +} ////////////////////////////////////////////////////////////////////////// // @@ -2597,14 +3584,13 @@ static bool EdgeVertexWeightIsSet( static bool EdgeSectorWeightIsValid( double edge_vertex_weight, - ON_SubD::SubDType subdivision_type, const ON_SubDEdge* edge ) { if (0.0 <= edge_vertex_weight && edge_vertex_weight < 1.0) return true; - if (ON_SubDSectorType::UnsetSectorWeight == edge_vertex_weight && ON_SubD::SubDType::Unset == subdivision_type && nullptr != edge && 0 == edge->m_level) + if (ON_SubDSectorType::UnsetSectorWeight == edge_vertex_weight && nullptr != edge && 0 == edge->SubdivisionLevel()) return true; return false; @@ -2614,7 +3600,6 @@ static bool IsValidVertexEdgeLink( const ON_SubDVertex* vertex, const ON_SubDEdge* edge, ON__UINT_PTR end_index, - ON_SubD::SubDType subdivision_type, bool bSilentError ) { @@ -2627,19 +3612,15 @@ static bool IsValidVertexEdgeLink( if (edge->m_vertex[end_index] != vertex) return ON_SubDIsNotValid(bSilentError); - if (vertex->m_level != edge->m_level) + if (vertex->SubdivisionLevel() != edge->SubdivisionLevel()) return ON_SubDIsNotValid(bSilentError); - if (false == EdgeSectorWeightIsValid(edge->m_sector_coefficient[end_index],subdivision_type,edge)) + if (false == EdgeSectorWeightIsValid(edge->m_sector_coefficient[end_index],edge)) return ON_SubDIsNotValid(bSilentError); - //const ON__UINT_PTR corner_type = edge->m_vertex[end_index].CornerType(); - //if (ON_SubDVertexPtr::NoCorner != corner_type && ON_SubD::VertexTag::Corner != vertex->m_vertex_tag) - // return ON_SubDIsNotValid(bSilentError); - if ( edge->IsSmooth() ) { - // edge->m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::X + // edge->m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag) { if (false == (0.0 == edge->m_sector_coefficient[end_index])) @@ -2648,7 +3629,7 @@ static bool IsValidVertexEdgeLink( else { const unsigned int tagged_end_index = edge->TaggedEndIndex(); - if (ON_SubD::EdgeTag::X == edge->m_edge_tag) + if (ON_SubD::EdgeTag::SmoothX == edge->m_edge_tag) { if (2 != tagged_end_index) return ON_SubDIsNotValid(bSilentError); @@ -2658,25 +3639,17 @@ static bool IsValidVertexEdgeLink( if (tagged_end_index != (unsigned int)end_index) return ON_SubDIsNotValid(bSilentError); } + + ON_SubDSectorType st = ON_SubDSectorType::Create(edge,(unsigned int)end_index); + if (!st.IsValid()) + return ON_SubDIsNotValid(bSilentError); - if (ON_SubD::SubDType::Unset == subdivision_type) - { - if (false == (ON_SubDSectorType::UnsetSectorWeight == edge->m_sector_coefficient[end_index])) - return ON_SubDIsNotValid(bSilentError); - } - else - { - ON_SubDSectorType st = ON_SubDSectorType::Create(subdivision_type,edge,(unsigned int)end_index); - if (!st.IsValid()) - return ON_SubDIsNotValid(bSilentError); + const double expected_vertex_weight = st.SectorWeight(); + if (false == (expected_vertex_weight == edge->m_sector_coefficient[end_index])) + return ON_SubDIsNotValid(bSilentError); - const double expected_vertex_weight = st.SectorWeight(); - if (false == (expected_vertex_weight == edge->m_sector_coefficient[end_index])) - return ON_SubDIsNotValid(bSilentError); - - if (false == EdgeVertexWeightIsSet(expected_vertex_weight)) - return ON_SubDIsNotValid(bSilentError); - } + if (false == EdgeVertexWeightIsSet(expected_vertex_weight)) + return ON_SubDIsNotValid(bSilentError); } } else if(ON_SubD::EdgeTag::Crease == edge->m_edge_tag) @@ -2710,7 +3683,7 @@ static bool IsValidVertexFaceLink( if (nullptr == vertex || nullptr == face) return ON_SubDIsNotValid(bSilentError); - if (vertex->m_level != face->m_level) + if (vertex->SubdivisionLevel() != face->SubdivisionLevel()) return ON_SubDIsNotValid(bSilentError); const unsigned int vertex_face_count = vertex->m_face_count; @@ -2776,7 +3749,7 @@ static bool IsValidEdgeFaceLink( if (nullptr == edge || nullptr == face) return ON_SubDIsNotValid(bSilentError); - if (edge->m_level != face->m_level) + if (edge->SubdivisionLevel() != face->SubdivisionLevel()) return ON_SubDIsNotValid(bSilentError); const unsigned int edge_face_count = edge->m_face_count; @@ -2830,6 +3803,200 @@ static bool IsValidEdgeFaceLink( return true; } +class ON_Internal_DamagedMarker +{ +public: + ON_Internal_DamagedMarker() = default; + ~ON_Internal_DamagedMarker() + { + if (nullptr != m_subd_component) + m_subd_component->m_status.SetDamagedState(true); + } + +private: + ON_Internal_DamagedMarker(const ON_Internal_DamagedMarker&) = delete; + ON_Internal_DamagedMarker& operator=(const ON_Internal_DamagedMarker&) = delete; + +public: + ON_Internal_DamagedMarker(const ON_SubDComponentBase* subd_component) + : m_subd_component(subd_component) + {} + + void ClearComponent() + { + m_subd_component = nullptr; + } + +private: + const ON_SubDComponentBase* m_subd_component = nullptr; +}; + +static bool IsValidSubDVertexTag( + const ON_SubDVertex* vertex, + bool bSilentError +) +{ + if (nullptr == vertex) + return true; // this error detected elsewhere. + + ON_Internal_DamagedMarker dm(vertex); + + const unsigned short vertex_edge_count = vertex->m_edge_count; + unsigned short crease_edge_count = 0; + unsigned short smooth_edge_count = 0; + for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); + if (nullptr == e) + continue; + if (e->IsSmooth()) + ++smooth_edge_count; + else if (e->IsCrease()) + ++crease_edge_count; + } + const bool bValidEdgeTags = (vertex_edge_count == crease_edge_count + smooth_edge_count); + + switch (vertex->m_vertex_tag) + { + case ON_SubD::VertexTag::Unset: + return ON_SubDIsNotValid(bSilentError); + break; + + case ON_SubD::VertexTag::Smooth: + if (false == bValidEdgeTags) + break; // invalid edge tags detected in IsValidSubDEdgeTag(); + + if ( + 0 != crease_edge_count + || vertex_edge_count < 2 + || vertex_edge_count != smooth_edge_count + || vertex_edge_count != vertex->m_face_count + ) + { + return ON_SubDIsNotValid(bSilentError); + } + break; + + case ON_SubD::VertexTag::Crease: + if (false == bValidEdgeTags) + break; // invalid edge tags detected in IsValidSubDEdgeTag(); + + if ( 2 != crease_edge_count ) + { + return ON_SubDIsNotValid(bSilentError); + } + break; + + case ON_SubD::VertexTag::Corner: + if (false == bValidEdgeTags) + break; // invalid edge tags detected in IsValidSubDEdgeTag(); + + if ( crease_edge_count < 2 ) + { + return ON_SubDIsNotValid(bSilentError); + } + break; + + case ON_SubD::VertexTag::Dart: + if (false == bValidEdgeTags) + break; // invalid edge tags detected in IsValidSubDEdgeTag(); + + if ( + 1 != crease_edge_count + || vertex_edge_count < 2 + || vertex_edge_count != smooth_edge_count + crease_edge_count + || vertex_edge_count != vertex->m_face_count + ) + { + return ON_SubDIsNotValid(bSilentError); + } + break; + + default: + return ON_SubDIsNotValid(bSilentError); + break; + } + + dm.ClearComponent(); + return true; +} + +static bool IsValidSubDEdgeTag( + const ON_SubDEdge* edge, + bool bSilentError +) +{ + if (nullptr == edge) + return true; // this error detected elsewhere. + + //ON_SubD::VertexTag vtag[2] = { ON_SubD::VertexTag::Unset,ON_SubD::VertexTag::Unset }; + unsigned int smooth_vertex_count = 0; + unsigned int crease_vertex_count = 0; + unsigned int corner_vertex_count = 0; + unsigned int dart_vertex_count = 0; + + for ( unsigned int evi = 0; evi < 2; evi++) + { + if (nullptr == edge->m_vertex[evi]) + return true; // topology errors detected elsewhere + switch (edge->m_vertex[evi]->m_vertex_tag) + { + case ON_SubD::VertexTag::Smooth: + ++smooth_vertex_count; + break; + case ON_SubD::VertexTag::Crease: + ++crease_vertex_count; + break; + case ON_SubD::VertexTag::Corner: + ++corner_vertex_count; + break; + case ON_SubD::VertexTag::Dart: + ++dart_vertex_count; + break; + } + }; + + if (2 != smooth_vertex_count + crease_vertex_count + corner_vertex_count + dart_vertex_count) + return true; // invalid vertex tags detected in IsValidSubDVertexTag(); + + ON_Internal_DamagedMarker dm(edge); + + //const unsigned short edge_face_count = edge->m_face_count; + switch(edge->m_edge_tag) + { + + case ON_SubD::EdgeTag::Unset: + return ON_SubDIsNotValid(bSilentError); + break; + + case ON_SubD::EdgeTag::Smooth: + if ( 2 != edge->m_face_count) + return ON_SubDIsNotValid(bSilentError); + if ( smooth_vertex_count < 1) + return ON_SubDIsNotValid(bSilentError); + break; + + case ON_SubD::EdgeTag::Crease: + if ( 0 != smooth_vertex_count ) + return ON_SubDIsNotValid(bSilentError); + break; + + case ON_SubD::EdgeTag::SmoothX: + if ( 2 != edge->m_face_count) + return ON_SubDIsNotValid(bSilentError); + if ( 0 != smooth_vertex_count ) + return ON_SubDIsNotValid(bSilentError); + break; + + default: + return ON_SubDIsNotValid(bSilentError); + break; + } + + dm.ClearComponent(); + return true; +} + static bool IsValidSubDVertex( const ON_SubDVertex* vertex, @@ -2842,7 +4009,7 @@ static bool IsValidSubDVertex( if (nullptr == vertex) return ON_SubDIsNotValid(bSilentError); - if (level != vertex->m_level) + if (vertex->SubdivisionLevel() != level) return ON_SubDIsNotValid(bSilentError); if (nullptr != vertex_id_range) @@ -2853,8 +4020,13 @@ static bool IsValidSubDVertex( return ON_SubDIsNotValid(bSilentError); } - if ( vertex->m_edge_count < vertex->m_face_count) - return ON_SubDIsNotValid(bSilentError); + ON_Internal_DamagedMarker dm(vertex); + + if (vertex->m_edge_count < vertex->m_face_count) + { + if ( ON_SubD::VertexTag::Corner != vertex->m_vertex_tag || vertex->m_edge_count < 3 ) + return ON_SubDIsNotValid(bSilentError); + } if (vertex->m_edge_count > 0 && nullptr == vertex->m_edges) return ON_SubDIsNotValid(bSilentError); @@ -2868,18 +4040,24 @@ static bool IsValidSubDVertex( if (vertex->m_edge_count != vertex->m_face_count) return ON_SubDIsNotValid(bSilentError); break; + case ON_SubD::VertexTag::Crease: - if (vertex->m_face_count <= 0) + if ( vertex->m_edge_count < 2 ) return ON_SubDIsNotValid(bSilentError); break; + case ON_SubD::VertexTag::Corner: + if ( vertex->m_edge_count < 1 ) + return ON_SubDIsNotValid(bSilentError); break; + case ON_SubD::VertexTag::Dart: // interior vertex if (level > 0 && ordinary_valence_count != vertex->m_edge_count) return ON_SubDIsNotValid(bSilentError); if (vertex->m_edge_count != vertex->m_face_count) return ON_SubDIsNotValid(bSilentError); break; + default: // invalid value for this enum return ON_SubDIsNotValid(bSilentError); @@ -2902,9 +4080,11 @@ static bool IsValidSubDVertex( return ON_SubDIsNotValid(bSilentError); } + dm.ClearComponent(); return true; } + static bool IsValidSubDEdge( const ON_SubDEdge* edge, unsigned short level, @@ -2915,7 +4095,7 @@ static bool IsValidSubDEdge( if (nullptr == edge) return ON_SubDIsNotValid(bSilentError); - if (level != edge->m_level) + if (edge->SubdivisionLevel() != level) return ON_SubDIsNotValid(bSilentError); if (nullptr != edge_id_range) @@ -2926,6 +4106,8 @@ static bool IsValidSubDEdge( return ON_SubDIsNotValid(bSilentError); } + ON_Internal_DamagedMarker dm(edge); + const ON_SubDVertex* edge_vertex[2] = { 0 }; for (unsigned int i = 0; i < 2; i++) { @@ -2939,7 +4121,7 @@ static bool IsValidSubDEdge( if (edge->IsSmooth()) { - // m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::X + // m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX if ( 2 != edge->m_face_count) return ON_SubDIsNotValid(bSilentError); } @@ -2951,6 +4133,7 @@ static bool IsValidSubDEdge( if (edge->m_face_count > 2 && nullptr == edge->m_facex) return ON_SubDIsNotValid(bSilentError); + dm.ClearComponent(); return true; } @@ -2966,7 +4149,7 @@ static bool IsValidSubDFace( if (nullptr == face) return ON_SubDIsNotValid(bSilentError); - if (level != face->m_level) + if (face->SubdivisionLevel() != level) return ON_SubDIsNotValid(bSilentError); if (nullptr != face_id_range) @@ -2977,6 +4160,8 @@ static bool IsValidSubDFace( return ON_SubDIsNotValid(bSilentError); } + ON_Internal_DamagedMarker dm(face); + if (face->m_edge_count < 3) return ON_SubDIsNotValid(bSilentError); @@ -2986,6 +4171,8 @@ static bool IsValidSubDFace( if (level > 0 && ordinary_face_edge_count != face->m_edge_count) return ON_SubDIsNotValid(bSilentError); + dm.ClearComponent(); + return true; } @@ -3002,12 +4189,11 @@ bool ON_SubDimple::IsValidLevel( const ON_SubDLevel* level = m_levels[level_index]; if ( nullptr == level) return ON_SubDIsNotValid(bSilentError); + level->ClearComponentDamagedState(); if ( level->m_level_index != level_index) return ON_SubDIsNotValid(bSilentError); - const ON_SubD::SubDType subdivision_type = level->m_subdivision_type; - if (level_index <= 0) { if (level->m_vertex_count < 3) @@ -3028,8 +4214,6 @@ bool ON_SubDimple::IsValidLevel( return ON_SubDIsNotValid(bSilentError); if (level->m_face_count <= previous_level->m_face_count) return ON_SubDIsNotValid(bSilentError); - if (ON_SubD::SubDType::TriLoopWarren != level->m_subdivision_type && ON_SubD::SubDType::QuadCatmullClark != level->m_subdivision_type) - return ON_SubDIsNotValid(bSilentError); } if (nullptr == level->m_vertex[0]) @@ -3212,7 +4396,7 @@ bool ON_SubDimple::IsValidLevel( edge = vertex->Edge(i); if (false == IsValidSubDEdge(edge, expected_level, e_id_range, bSilentError)) return false; - if (false == IsValidVertexEdgeLink(vertex, edge, vertex->EdgeDirection(i), subdivision_type, bSilentError)) + if (false == IsValidVertexEdgeLink(vertex, edge, vertex->EdgeDirection(i), bSilentError)) return false; } @@ -3234,7 +4418,7 @@ bool ON_SubDimple::IsValidLevel( vertex = edge->m_vertex[i]; if (false == IsValidSubDVertex(vertex, expected_level, v_id_range, level->m_ordinary_vertex_valence, bSilentError)) return false; - if (false == IsValidVertexEdgeLink(vertex, edge, i, subdivision_type, bSilentError)) + if (false == IsValidVertexEdgeLink(vertex, edge, i, bSilentError)) return false; } @@ -3270,6 +4454,32 @@ bool ON_SubDimple::IsValidLevel( } } + + // edge tag validation + for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge) + { + if (false == IsValidSubDEdgeTag(edge, bSilentError)) + return false; + } + + // vertex tag validation + for (vertex = level->m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) + { + if (false == IsValidSubDVertexTag(vertex, bSilentError)) + return false; + } + + // edge length validation + for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge) + { + const ON_3dPoint P[2] = { edge->m_vertex[0]->ControlNetPoint(), edge->m_vertex[1]->ControlNetPoint() }; + if (false == (P[0] != P[1])) + { + edge->m_status.SetDamagedState(true); + return ON_SubDIsNotValid(bSilentError); + } + } + return true; } @@ -3374,10 +4584,17 @@ unsigned int ON_SubD::DumpTopology( const ON__UINT64 runtime_sn = (text_log.IsTextHash()) ? 0 : RuntimeSerialNumber(); // TextHash ignores settings that don't depend on 3dm file content. + const ON_wString subd_texture_domain = ON_SubD::TextureDomainTypeToString(this->TextureDomainType()); if (level_count > 1) - text_log.Print(L"SubD[%" PRIu64 "]: %u levels. Level %u is active.\n", runtime_sn, level_count, active_level_index); + text_log.Print(L"SubD[%" PRIu64 "]: %u levels. Level %u is active. texture domain type = %ls.\n", + runtime_sn, + level_count, + active_level_index, + static_cast(subd_texture_domain) + ); else - text_log.Print(L"SubD[%" PRIu64 "]:\n", runtime_sn ); + text_log.Print(L"SubD[%" PRIu64 "]: texture domain type = %ls.\n", runtime_sn, static_cast(subd_texture_domain)); + ON_SubDLevelIterator lit(subdimple->LevelIterator()); @@ -3463,7 +4680,7 @@ unsigned int ON_SubDLevel::DumpTopology( unsigned int uniformN = 0; for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) { - if (face_count >= m_face_count && f->m_level != level_index) + if (face_count >= m_face_count && f->SubdivisionLevel() != level_index) break; face_count++; unsigned int N = f->EdgeCount(); @@ -3525,7 +4742,7 @@ unsigned int ON_SubDLevel::DumpTopology( unsigned int max_vertex_id = 0; for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) { - if (vertex_count >= m_vertex_count && v->m_level != level_index) + if (vertex_count >= m_vertex_count && v->SubdivisionLevel() != level_index) break; if (v->m_id > max_vertex_id) max_vertex_id = v->m_id; @@ -3572,15 +4789,54 @@ unsigned int ON_SubDLevel::DumpTopology( vertex_dump_count++; ON_TextLogIndent vindent(text_log); + const ON_3dPoint P0(v->ControlNetPoint()); + + ON_wString vtag; + switch (v->m_vertex_tag) + { + case ON_SubD::VertexTag::Unset: + vtag = L"Unset"; + break; + case ON_SubD::VertexTag::Smooth: + vtag = L"Smooth"; + break; + case ON_SubD::VertexTag::Crease: + vtag = L"Crease"; + break; + case ON_SubD::VertexTag::Corner: + vtag = L"Corner"; + break; + case ON_SubD::VertexTag::Dart: + vtag = L"Dart"; + break; + default: + vtag = L"INVALID"; + break; + } + text_log.Print( - "v%u: (%g, %g, %g)\n", - v->m_id, v->m_P[0], v->m_P[1], v->m_P[2] + "v%u: %ls (%g, %g, %g)\n", + v->m_id, + static_cast(vtag), + P0.x, P0.y, P0.z ); text_log.PushIndent(); + ON_3dVector D(ON_3dVector::NanVector); + if (v->GetSubdivisionDisplacement(&D.x) && D.IsValid()) + text_log.Print( "v.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z ); + + ON_3dPoint P1(ON_3dPoint::NanPoint); + if (v->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid()) + text_log.Print( "v.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z ); + + ON_3dPoint S(ON_3dPoint::NanPoint); + if (v->GetSavedSurfacePoint(&S.x) && S.IsValid()) + text_log.Print( "v.SurfacePoint: (%g, %g, %g)\n", S.x, S.y, S.z ); + const unsigned int vertex_edge_count = v->m_edge_count; - text_log.Print("vEdges[%u] = {", vertex_edge_count); + text_log.Print("v.Edges[%u] = {", vertex_edge_count); prefix[0] = ON_String::Space; prefix[1] = error_code_point; prefix[2] = 'e'; @@ -3666,7 +4922,7 @@ unsigned int ON_SubDLevel::DumpTopology( unsigned int max_edge_id = 0; for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) { - if (edge_count >= m_edge_count && e->m_level != level_index) + if (edge_count >= m_edge_count && e->SubdivisionLevel() != level_index) break; if (e->m_id > max_edge_id) max_edge_id = e->m_id; @@ -3712,9 +4968,30 @@ unsigned int ON_SubDLevel::DumpTopology( edge_dump_count++; ON_TextLogIndent eindent(text_log); + ON_wString etag; + switch (e->m_edge_tag) + { + case ON_SubD::EdgeTag::Unset: + etag = L"Unset"; + break; + case ON_SubD::EdgeTag::Smooth: + etag = L"Smooth"; + break; + case ON_SubD::EdgeTag::Crease: + etag = L"Crease"; + break; + case ON_SubD::EdgeTag::SmoothX: + etag = L"SmmothX"; + break; + default: + etag = L"INVALID"; + break; + } + text_log.Print( - "e%u: (", - e->m_id + "e%u: %ls (", + e->m_id, + static_cast(etag) ); prefix[0] = ON_String::Space; @@ -3742,8 +5019,16 @@ unsigned int ON_SubDLevel::DumpTopology( text_log.PushIndent(); + ON_3dVector D(ON_3dVector::NanVector); + if (e->GetSubdivisionDisplacement(&D.x) && D.IsValid()) + text_log.Print( "e.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z ); + + ON_3dPoint P1(ON_3dPoint::NanPoint); + if (e->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid()) + text_log.Print( "e.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z ); + const unsigned int edge_face_count = e->m_face_count; - text_log.Print("eFaces[%u] = {", edge_face_count); + text_log.Print("e.Faces[%u] = {", edge_face_count); prefix[0] = ON_String::Space; prefix[1] = error_code_point; prefix[2] = 'f'; @@ -3762,7 +5047,7 @@ unsigned int ON_SubDLevel::DumpTopology( if (nullptr != f) { fid = f->m_id; - ON_SubDEdgePtr eptr = f->EdgePtr(e); + ON_SubDEdgePtr eptr = f->EdgePtrFromEdge(e); if (eptr.Edge() == e && eptr.EdgeDirection() == edge_fdir) { prefix[1] = (0 == edge_fdir) ? '+' : '-'; @@ -3806,7 +5091,7 @@ unsigned int ON_SubDLevel::DumpTopology( unsigned int max_face_id = 0; for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) { - if (face_count >= m_face_count && f->m_level != level_index) + if (face_count >= m_face_count && f->SubdivisionLevel() != level_index) break; if (f->m_id > max_face_id) max_face_id = f->m_id; @@ -3859,8 +5144,17 @@ unsigned int ON_SubDLevel::DumpTopology( text_log.PushIndent(); + ON_3dVector D(ON_3dVector::NanVector); + if (f->GetSubdivisionDisplacement(&D.x) && D.IsValid()) + text_log.Print( "f.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z ); + + ON_3dPoint P1(ON_3dPoint::NanPoint); + if (f->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid()) + text_log.Print( "f.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z ); + + const unsigned int face_edge_count = f->m_edge_count; - text_log.Print("fEdges[%u] = {", face_edge_count); + text_log.Print("f.Edges[%u] = {", face_edge_count); prefix[0] = ON_String::Space; prefix[1] = error_code_point; prefix[2] = 'e'; @@ -3879,7 +5173,7 @@ unsigned int ON_SubDLevel::DumpTopology( if (nullptr != e) { eid = e->m_id; - ON_SubDFacePtr fptr = e->FacePtr(f); + ON_SubDFacePtr fptr = e->FacePtrFromFace(f); if (fptr.Face() == f && fptr.FaceDirection() == face_edir) { prefix[1] = (0 == face_edir) ? '+' : '-'; @@ -3898,7 +5192,7 @@ unsigned int ON_SubDLevel::DumpTopology( ? last_edge->m_vertex[0 == last_eptr.EdgeDirection() ? 1 : 0] : nullptr; - text_log.Print("fVertices[%u] = {", face_edge_count); + text_log.Print("f.Vertices[%u] = {", face_edge_count); prefix[0] = ON_String::Space; prefix[1] = error_code_point; prefix[2] = 'v'; @@ -3935,6 +5229,97 @@ unsigned int ON_SubDLevel::DumpTopology( } text_log.Print(" }\n"); + if (f->TextureDomainIsSet()) + { + const ON_wString s = ON_SubD::TextureDomainTypeToString(f->TextureDomainType()); + const bool bGridOrder = true; + const bool bNormalized = false; + ON_2dPoint corners[4] = { + f->TextureDomainCorner(bGridOrder,bNormalized,0), + f->TextureDomainCorner(bGridOrder,bNormalized,1), + f->TextureDomainCorner(bGridOrder,bNormalized,2), + f->TextureDomainCorner(bGridOrder,bNormalized,3) + }; + text_log.Print("%ls texture domain. Grid corners: (%g,%g), (%g,%g), (%g,%g), (%g,%g)\n", + static_cast(s), + corners[0].x, corners[0].y, + corners[1].x, corners[1].y, + corners[2].x, corners[2].y, + corners[3].x, corners[3].y + ); + ////const ON_SubDMeshFragment *fragments = f->MeshFragments(); + ////if (nullptr != fragments) + ////{ + ////ON_TextLogIndent indent1(text_log); + //// unsigned short fragment_count = 0; + //// const unsigned short expected_fragment_count = (unsigned short)((4 == face_edge_count) ? 1 : face_edge_count); + //// ON_3dPoint fragmentT[4]; + //// bool bDamagedOrIncompleteFragments = false; + //// for (unsigned short fragdex = 0; fragdex <= expected_fragment_count && nullptr != fragments; fragdex++, fragments = fragments->m_next_fragment) + //// { + //// ++fragment_count; + //// if (expected_fragment_count != fragments->m_face_fragment_count) + //// { + //// bDamagedOrIncompleteFragments = true; + //// break; + //// } + //// if (fragdex != fragments->m_face_fragment_index) + //// { + //// bDamagedOrIncompleteFragments = true; + //// break; + //// } + //// } + //// if (expected_fragment_count == fragment_count) + //// { + //// fragments = f->MeshFragments(); + //// if (1 == fragment_count) + //// { + //// if (fragments->GetTextureCoordinteCorners(bGridOrder, fragmentT)) + //// { + //// text_log.Print("Quad face fragment texture corners = (%g,%g,%g), (%g,%g,%g), (%g,%g,%g), (%g,%g,%g)\n", + //// fragmentT[0].x, fragmentT[0].y, fragmentT[0].z, + //// fragmentT[1].x, fragmentT[1].y, fragmentT[1].z, + //// fragmentT[2].x, fragmentT[2].y, fragmentT[2].z, + //// fragmentT[3].x, fragmentT[3].y, fragmentT[3].z + //// ); + //// } + //// else + //// { + //// text_log.Print("Quad face fragment texture corners not available.\n"); + //// } + //// } + //// else + //// { + //// text_log.Print("%u face fragments.\n", (unsigned)expected_fragment_count); + //// ON_TextLogIndent indent2(text_log); + //// for (unsigned short fragdex = 0; fragdex <= expected_fragment_count && nullptr != fragments; fragdex++, fragments = fragments->m_next_fragment) + //// { + //// if (fragments->GetTextureCoordinteCorners(bGridOrder, fragmentT)) + //// { + //// text_log.Print("fragment[%u] texture corners = (%g,%g,%g), (%g,%g,%g), (%g,%g,%g), (%g,%g,%g)\n", + //// (unsigned)fragdex, + //// fragmentT[0].x, fragmentT[0].y, fragmentT[0].z, + //// fragmentT[1].x, fragmentT[1].y, fragmentT[1].z, + //// fragmentT[2].x, fragmentT[2].y, fragmentT[2].z, + //// fragmentT[3].x, fragmentT[3].y, fragmentT[3].z + //// ); + //// } + //// else + //// { + //// text_log.Print("fragment[%u] texture corners not available.\n", (unsigned)fragdex); + //// } + //// } + //// } + //// } + //// else if (nullptr == fragments) + //// { + //// bDamagedOrIncompleteFragments = true; + //// } + //// if (bDamagedOrIncompleteFragments ) + //// text_log.Print("Damaged or incomplete mesh fragments.\n"); + ////} + } + text_log.PopIndent(); } @@ -4017,13 +5402,11 @@ void ON_SubD::DestroyRuntimeCache( bool bDelete ) const ON_SubDLevel* level = dimple->SubDLevel(level_index); if (level) { - level->ClearBoundingBox(); - level->ClearEdgeFlags(); - level->ClearSubdivisonAndLimitPoints(); - level->m_limit_mesh = ON_SubDLimitMesh::Empty; + level->ClearEvaluationCache(); level->AggregateComponentStatus().MarkAsNotCurrent(); } } + dimple->ChangeContentSerialNumber(); } return; } @@ -4051,7 +5434,8 @@ bool ON_SubD::GetBBox( ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox; bool rc = false; - bbox = ActiveLevel().BoundingBox(); + // GetBBox must always returns the control net box - it contains both the surface and the control net. + bbox = ActiveLevel().ControlNetBoundingBox(); rc = bbox.IsValid(); if (rc) { @@ -4129,9 +5513,9 @@ bool ON_SubD::HasBrepForm() const } //virtual -ON_Brep* ON_SubD::BrepForm( ON_Brep*) const +ON_Brep* ON_SubD::BrepForm( ON_Brep* destination_brep) const { - return 0; + return nullptr; } //virtual @@ -4200,7 +5584,7 @@ void ON_SubD::ShareDimple(const ON_SubD& other_subd) } } -void ON_SubD::ShareDimple(const class ON_SubDLimitMeshImpl& subd_limple) +void ON_SubD::ShareDimple(const class ON_SubDMeshImpl& subd_limple) { std::shared_ptr limple_sp(subd_limple.m_subdimple_wp.lock()); @@ -4210,7 +5594,7 @@ void ON_SubD::ShareDimple(const class ON_SubDLimitMeshImpl& subd_limple) { // weak pointer is nullptr, meaning there are no known references to the // subd used to create this limit mesh. - const_cast(subd_limple).ClearFragmentFacePointers(true); + const_cast(subd_limple).ClearFragmentFacePointers(true); } if (subd_imple != limple_sp.get()) @@ -4220,7 +5604,7 @@ void ON_SubD::ShareDimple(const class ON_SubDLimitMeshImpl& subd_limple) } } -void ON_SubD::SwapDimple(class ON_SubDLimitMeshImpl& subd_limple ) +void ON_SubD::SwapDimple(class ON_SubDMeshImpl& subd_limple ) { std::shared_ptr limple_sp(subd_limple.m_subdimple_wp.lock()); if (m_subdimple_sp.get() != limple_sp.get()) @@ -4269,15 +5653,6 @@ void ON_SubD::Destroy() m_subdimple_sp.reset(); } -bool ON_SubD::SetSubDType( - ON_SubD::SubDType subdivision_type - ) -{ - ON_SubDimple* subdimple = SubDimple(true); - return (subdimple) ? subdimple->SetSubDType(subdivision_type) : ON_SUBD_RETURN_ERROR(false); -} - - class ON_SubDVertex* ON_SubD::AddVertex( const double* P ) @@ -4293,7 +5668,8 @@ class ON_SubDVertex* ON_SubD::AddVertex( ON_SubDimple* subdimple = SubDimple(true); if ( 0 == subdimple ) return 0; - class ON_SubDVertex* v = subdimple->AllocateVertex(vertex_tag, 0, P); // 0 = level + const unsigned int level_index = subdimple->ActiveLevelIndex(); + class ON_SubDVertex* v = subdimple->AllocateVertex(vertex_tag, level_index, P); // 0 = level subdimple->AddVertexToLevel(v); return v; } @@ -4313,6 +5689,9 @@ class ON_SubDEdge* ON_SubDimple::AddEdge( if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v1_sector_weight,true) ) return ON_SUBD_RETURN_ERROR(nullptr); + if ( nullptr != v0 && nullptr != v1 && v0->SubdivisionLevel() != v1->SubdivisionLevel() ) + return ON_SUBD_RETURN_ERROR(nullptr); + const bool bEdgeTagSet = ON_SubD::EdgeTagIsSet(edge_tag); if ( bEdgeTagSet @@ -4341,6 +5720,13 @@ class ON_SubDEdge* ON_SubDimple::AddEdge( if ( nullptr == e) return ON_SUBD_RETURN_ERROR(nullptr); + if ( nullptr != v0 ) + e->SetSubdivisionLevel(v0->SubdivisionLevel()); + else if ( nullptr != v1 ) + e->SetSubdivisionLevel(v1->SubdivisionLevel()); + else if ( ActiveLevelIndex() < ON_UNSET_UINT_INDEX ) + e->SetSubdivisionLevel(ActiveLevelIndex()); + for (unsigned int i = 0; i < 2; i++) { ON_SubDVertex* v = (i ? v1 : v0); @@ -4356,9 +5742,6 @@ class ON_SubDEdge* ON_SubDimple::AddEdge( return ON_SUBD_RETURN_ERROR(nullptr); } v->m_edges[v->m_edge_count++] = ON_SubDEdgePtr::Create(e, i); - if (e->m_level < v->m_level) - e->m_level = v->m_level; - //v->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; } } @@ -4412,7 +5795,7 @@ ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext( if ( ON_SubD::VertexTagIsSet(v0_tag) && ON_SubD::VertexTagIsSet(v1_tag) ) { if (2 == edge_face_count) - edge_tag = ON_SubD::EdgeTag::X; + edge_tag = ON_SubD::EdgeTag::SmoothX; break; } @@ -4422,6 +5805,69 @@ ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext( return edge_tag; } +const ON_SubDVertex* ON_SubD::FindVertex( + const double* control_net_point, + double distance_tolerance +) const +{ + if (nullptr == control_net_point ) + return nullptr; + const ON_3dPoint P(control_net_point); + if ( false == P.IsValid() ) + return nullptr; + if ( false == (0.0 <= distance_tolerance)) + return nullptr; + + const ON_SubDVertex* best_v = nullptr; + ON_SubDVertexIterator vit(*this); + for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + { + const double d = P.DistanceTo(v->ControlNetPoint()); + if (0.0 == d) + return v; + if (d < distance_tolerance && (nullptr == best_v || d <= distance_tolerance) ) + { + distance_tolerance = d; + best_v = v; + } + } + return best_v; +} + +const ON_SubDVertex* ON_SubD::FindOrAddVertex( + const double* control_net_point, + double distance_tolerance +) +{ + if (nullptr == control_net_point) + return nullptr; + const ON_3dPoint P(control_net_point); + if (false == P.IsValid()) + return nullptr; + if (false == (0.0 <= distance_tolerance)) + return nullptr; + const ON_SubDVertex* v = FindVertex(&P.x, distance_tolerance); + return (nullptr != v) ? v : AddVertex(&P.x); +} + +const ON_SubDEdgePtr ON_SubD::FindEdge( + const class ON_SubDVertex* v0, + const class ON_SubDVertex* v1 +) const +{ + return ON_SubDEdge::FromVertices(v0, v1); +} + +const ON_SubDEdgePtr ON_SubD::FindOrAddEdge( + class ON_SubDVertex* v0, + class ON_SubDVertex* v1 +) +{ + ON_SubDEdgePtr eptr = ON_SubDEdge::FromVertices(v0, v1); + if (nullptr == eptr.Edge()) + eptr = ON_SubDEdgePtr::Create(AddEdge(v0, v1), 0); + return eptr; +} class ON_SubDEdge* ON_SubD::AddEdge( ON_SubDVertex* v0, @@ -4437,21 +5883,10 @@ class ON_SubDEdge* ON_SubD::AddEdge( ON_SubDVertex* v1 ) { - // NO automatic setting - causes more problems than it helps. + // NO automatic edge tag setting - causes more problems than it helps. // Users can call ON_SubD::EdgeTagFromContext() if they want to // preset the edge tag based on context. - ////if (ON_SubD::EdgeTag::Unset == edge_tag && nullptr != v0 && nullptr != v1 ) - ////{ - //// if ( v0->IsCreaseOrCornerOrDart() && v1->IsCreaseOrCornerOrDart() ) - //// edge_tag = ON_SubD::EdgeTag::Crease; - //// else if ( ON_SubD::VertexTag::Unset != v0->m_vertex_tag - //// && ON_SubD::VertexTag::Unset != v1->m_vertex_tag - //// && (v0->IsSmooth() || v1->IsSmooth()) - //// ) - //// edge_tag = ON_SubD::EdgeTag::Smooth; - ////} - return AddEdgeWithSectorCoefficients( edge_tag, v0, @@ -4475,17 +5910,47 @@ ON_SubDEdge* ON_SubD::AddEdgeWithSectorCoefficients( return ON_SUBD_RETURN_ERROR(nullptr); } + class ON_SubDFace* ON_SubDimple::AddFace( unsigned int edge_count, const ON_SubDEdgePtr* edge +) +{ + return AddFace(nullptr, edge_count, edge); +} + +class ON_SubDFace* ON_SubDimple::AddFace( + const ON_SubDFace* candidate_face, + unsigned int edge_count, + const ON_SubDEdgePtr* edge ) { if ( edge_count > 0 && nullptr == edge) return ON_SUBD_RETURN_ERROR(nullptr); - ON_SubDFace* f = AllocateFace(); + unsigned f_level = 0; + bool bHaveLevel = false; + for (unsigned int i = 0; i < edge_count; i++) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(edge[i].m_ptr); + if (nullptr == e) + continue; + if (bHaveLevel) + { + if (e->SubdivisionLevel() != f_level) + return ON_SUBD_RETURN_ERROR(nullptr); + } + else + { + f_level = e->SubdivisionLevel(); + bHaveLevel = true; + } + } + + ON_SubDFace* f = AllocateFace(candidate_face); if ( nullptr == f) return ON_SUBD_RETURN_ERROR(nullptr); + f->SetSubdivisionLevel(f_level); if (edge_count > 0) { @@ -4498,9 +5963,7 @@ class ON_SubDFace* ON_SubDimple::AddFace( } } - ON_SubDEdgePtr* f_edge = f->m_edge4; - unsigned short f_level = 0; for (unsigned int i = 0; i < edge_count; i++) { if (4 == i) @@ -4546,13 +6009,14 @@ class ON_SubDFace* ON_SubDimple::AddFace( } else { - if (2 == e->m_face_count) - { - // Getting this error in a valid situation means it is time - // to support non-manifold subdivision objects. - ON_SubDIncrementErrorCount(); - ON_WARNING("creating non-manifold subdivision object"); - } + // Dale Lear, April 3, 2019 RH-49843 - we support non-manifold SubD objects now. + //if (2 == e->m_face_count) + //{ + // // Getting this error in a valid situation means it is time + // // to support non-manifold subdivision objects. + // ON_SubDIncrementErrorCount(); + // ON_WARNING("creating non-manifold subdivision object"); + //} if (false == m_heap.GrowEdgeFaceArrayByOne(e)) { e->m_status.SetDamagedState(true); @@ -4561,11 +6025,7 @@ class ON_SubDFace* ON_SubDimple::AddFace( e_faces = e->m_facex - 2; } e_faces[e->m_face_count++] = ON_SubDFacePtr::Create(f, eptr); - if (f_level < e->m_level) - f_level = e->m_level; } - - f->m_level = f_level; f->m_edge_count = (unsigned short)edge_count; } @@ -4575,114 +6035,79 @@ class ON_SubDFace* ON_SubDimple::AddFace( return f; } +bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCoefficientsOnly) +{ + const double input_sector_coefficient[2] = { m_sector_coefficient[0], m_sector_coefficient[1] }; + if (bUnsetEdgeSectorCoefficientsOnly) + { + if (input_sector_coefficient[0] >= 0.0 && input_sector_coefficient[0] <= 1.0 + && input_sector_coefficient[1] >= 0.0 && input_sector_coefficient[1] <= 1.0 + ) + return false; + } + m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + if (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::SmoothX == m_edge_tag) + { + const unsigned int tagged_end_index = TaggedEndIndex(); + if (tagged_end_index < 2) + { + m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::Create( this, tagged_end_index).SectorWeight(); + } + else if (2 == tagged_end_index) + { + if (ON_SubD::EdgeTag::Smooth == m_edge_tag && 2 == m_face_count ) + m_edge_tag = ON_SubD::EdgeTag::SmoothX; + + if (ON_SubD::EdgeTag::Smooth == m_edge_tag) + m_edge_tag = ON_SubD::EdgeTag::Crease; + else if (ON_SubD::EdgeTag::SmoothX == m_edge_tag) + { + m_sector_coefficient[0] = ON_SubDSectorType::Create( this, 0).SectorWeight(); + m_sector_coefficient[1] = ON_SubDSectorType::Create( this, 1).SectorWeight(); + } + } + } + + const bool bNoChanges + = input_sector_coefficient[0] == m_sector_coefficient[0] + && input_sector_coefficient[1] == m_sector_coefficient[1]; + + return (false == bNoChanges); +} + unsigned int ON_SubDLevel::UpdateEdgeSectorCoefficients( bool bUnsetEdgeSectorCoefficientsOnly ) { unsigned int changed_edge_count = 0; - const bool bUnsetSubdivisiontType = (false == ON_SubD::IsQuadOrTriSubDType(m_subdivision_type)); - for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) { - ON_SubDEdge* e = const_cast(edge); - if (bUnsetEdgeSectorCoefficientsOnly) - { - if ( e->m_sector_coefficient[0] >= 0.0 && e->m_sector_coefficient[0] <= 1.0 - && e->m_sector_coefficient[1] >= 0.0 && e->m_sector_coefficient[1] <= 1.0 - ) - continue; - } - const double m_sector_coefficient0[2] = { e->m_sector_coefficient[0], e->m_sector_coefficient[1] }; - e->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; - e->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; - if (ON_SubD::EdgeTag::Smooth == edge->m_edge_tag || ON_SubD::EdgeTag::X == edge->m_edge_tag) - { - const unsigned int tagged_end_index = edge->TaggedEndIndex(); - if (tagged_end_index < 2) - { - e->m_sector_coefficient[tagged_end_index] - = bUnsetSubdivisiontType - ? ON_SubDSectorType::UnsetSectorWeight - : ON_SubDSectorType::Create(m_subdivision_type, edge, tagged_end_index).SectorWeight(); - } - else if (2 == tagged_end_index) - { - if (ON_SubD::EdgeTag::Smooth == edge->m_edge_tag) - e->m_edge_tag = ON_SubD::EdgeTag::Crease; - else if (ON_SubD::EdgeTag::X == edge->m_edge_tag) - { - e->m_sector_coefficient[0] - = bUnsetSubdivisiontType - ? ON_SubDSectorType::UnsetSectorWeight - : ON_SubDSectorType::Create(m_subdivision_type, edge, 0).SectorWeight(); - e->m_sector_coefficient[1] - = bUnsetSubdivisiontType - ? ON_SubDSectorType::UnsetSectorWeight - : ON_SubDSectorType::Create(m_subdivision_type, edge, 1).SectorWeight(); - } - } - } - if (!(m_sector_coefficient0[0] == e->m_sector_coefficient[0] && m_sector_coefficient0[1] == e->m_sector_coefficient[1])) - changed_edge_count++; + if (const_cast(edge)->UpdateEdgeSectorCoefficientsForExperts(bUnsetEdgeSectorCoefficientsOnly)) + ++changed_edge_count; } - return changed_edge_count; } -bool ON_SubDLevel::SetSubDType( - ON_SubD::SubDType subd_type - ) -{ - const bool bQuadSubD = (ON_SubD::SubDType::QuadCatmullClark == subd_type); - const bool bTriSubD = (ON_SubD::SubDType::TriLoopWarren == subd_type); - if (false == bQuadSubD && false == bTriSubD) - return ON_SUBD_RETURN_ERROR(false); - - const bool bUpdateEdgeWeights = (m_subdivision_type != subd_type); - - if (bQuadSubD) - { - m_subdivision_type = subd_type; - m_ordinary_vertex_valence = 4; - m_ordinary_face_edge_count = 4; - } - else if (bTriSubD) - { - m_subdivision_type = subd_type; - m_ordinary_vertex_valence = 6; - m_ordinary_face_edge_count = 3; - } - - if (bUpdateEdgeWeights) - { - UpdateEdgeSectorCoefficients(false); - } - - return true; -} - -bool ON_SubDimple::SetSubDType( - ON_SubD::SubDType subd_type - ) -{ - ON_SubDLevel* subd_level = ActiveLevel(0 == m_levels.UnsignedCount()); - if (nullptr == subd_level) - return ON_SUBD_RETURN_ERROR(false); - - return subd_level->SetSubDType(subd_type); -} - class ON_SubDFace* ON_SubD::AddTriangleFace( - class ON_SubDEdge* edge0, bool bReverseEdge0, - class ON_SubDEdge* edge1, bool bReverseEdge1, - class ON_SubDEdge* edge2, bool bReverseEdge2 + class ON_SubDEdge* edge0, + class ON_SubDEdge* edge1, + class ON_SubDEdge* edge2 ) { - return AddTriangleFace( - ON_SubDEdgePtr::Create(edge0, bReverseEdge0 ? 1 : 0), - ON_SubDEdgePtr::Create(edge1, bReverseEdge1 ? 1 : 0), - ON_SubDEdgePtr::Create(edge2, bReverseEdge2 ? 1 : 0) - ); + ON_SubDEdge* edges[3] = { edge0,edge1,edge2 }; + return AddFace(edges, 3); +} + +class ON_SubDFace* ON_SubD::AddQuadFace( + class ON_SubDEdge* edge0, + class ON_SubDEdge* edge1, + class ON_SubDEdge* edge2, + class ON_SubDEdge* edge3 + ) +{ + ON_SubDEdge* edges[4] = { edge0,edge1,edge2,edge3 }; + return AddFace(edges, 4); } class ON_SubDFace* ON_SubD::AddTriangleFace( @@ -4692,22 +6117,7 @@ class ON_SubDFace* ON_SubD::AddTriangleFace( ) { ON_SubDEdgePtr eptr3[3] = { edge0,edge1,edge2 }; - return AddFace(3, eptr3); -} - -class ON_SubDFace* ON_SubD::AddQuadFace( - class ON_SubDEdge* edge0, bool bReverseEdge0, - class ON_SubDEdge* edge1, bool bReverseEdge1, - class ON_SubDEdge* edge2, bool bReverseEdge2, - class ON_SubDEdge* edge3, bool bReverseEdge3 -) -{ - return AddQuadFace( - ON_SubDEdgePtr::Create(edge0, bReverseEdge0 ? 1 : 0), - ON_SubDEdgePtr::Create(edge1, bReverseEdge1 ? 1 : 0), - ON_SubDEdgePtr::Create(edge2, bReverseEdge2 ? 1 : 0), - ON_SubDEdgePtr::Create(edge3, bReverseEdge3 ? 1 : 0) - ); + return AddFace(eptr3, 3); } class ON_SubDFace* ON_SubD::AddQuadFace( @@ -4718,18 +6128,88 @@ class ON_SubDFace* ON_SubD::AddQuadFace( ) { ON_SubDEdgePtr eptr4[4] = { edge0,edge1,edge2,edge3 }; - return AddFace(4, eptr4); + return AddFace(eptr4, 4); } class ON_SubDFace* ON_SubD::AddFace( - unsigned int edge_count, - const ON_SubDEdgePtr* edge + const ON_SimpleArray& edges +) +{ + return AddFace(edges.Array(), edges.UnsignedCount()); +} + +class ON_SubDFace* ON_SubD::AddFace( + const ON_SimpleArray& edges +) +{ + return AddFace(edges.Array(), edges.UnsignedCount()); +} + +class ON_SubDFace* ON_SubD::AddFace( + ON_SubDEdge *const* edge, + unsigned int edge_count + ) +{ + if (edge_count < 3 || nullptr == edge) + return ON_SUBD_RETURN_ERROR(nullptr); + if (nullptr == edge[0] || nullptr == edge[0]->m_vertex[0] || nullptr == edge[0]->m_vertex[1] || edge[0]->m_vertex[0] == edge[0]->m_vertex[1]) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( edge[0] == edge[edge_count-1] ) + return ON_SUBD_RETURN_ERROR(nullptr); + ON_SubDEdgePtr* eptr = (ON_SubDEdgePtr*)onmalloc(edge_count * sizeof(*eptr)); + if (nullptr == eptr) + return ON_SUBD_RETURN_ERROR(nullptr); + eptr[0] = ON_SubDEdgePtr::Create( + edge[0], + (edge[0]->m_vertex[0] == edge[1]->m_vertex[0] || edge[0]->m_vertex[0] == edge[1]->m_vertex[1]) ? 1 : 0 + ); + eptr[edge_count - 1] = ON_SubDEdgePtr::Null; + for (unsigned int fei = 1; fei < edge_count; ++fei) + { + if (nullptr == edge[fei] || nullptr == edge[fei]->m_vertex[0] || nullptr == edge[fei]->m_vertex[1] || edge[fei]->m_vertex[0] == edge[fei]->m_vertex[1]) + break; + if (edge[fei - 1] == edge[fei]) + break; + const ON_SubDVertex* v = eptr[fei - 1].RelativeVertex(1); + if (nullptr == v) + break; + eptr[fei] = ON_SubDEdgePtr::Create(edge[fei], v == edge[fei]->m_vertex[0] ? 0 : 1); + if (v != eptr[fei].RelativeVertex(0)) + break; + } + + ON_SubDFace* face + = (eptr[edge_count - 1].IsNull() && (eptr[0].RelativeVertex(0) != eptr[edge_count - 1].RelativeVertex(1))) + ? AddFace(eptr, edge_count) + : nullptr; + onfree(eptr); + if (nullptr == face) + { + ON_SUBD_ERROR("Invalid input edge[] array"); + } + return face; +} + +class ON_SubDFace* ON_SubD::AddFace( + const ON_SubDEdgePtr* edge, + unsigned int edge_count ) { ON_SubDimple* subdimple = SubDimple(true); return (nullptr != subdimple) ? subdimple->AddFace(edge_count, edge) : nullptr; } + +class ON_SubDFace* ON_SubD::AddFace( + const ON_SubDFace* candidate_face, + const ON_SubDEdgePtr* edge, + unsigned int edge_count + ) +{ + ON_SubDimple* subdimple = SubDimple(true); + return (nullptr != subdimple) ? subdimple->AddFace(candidate_face, edge_count, edge) : nullptr; +} + bool ON_SubD::AddFaceEdgeConnection( ON_SubDFace* face, unsigned int i, @@ -4849,15 +6329,64 @@ bool ON_SubD::RemoveFaceEdgeConnection( return ON_SUBD_RETURN_ERROR(false); ON_SubDEdge* edge = removed_edge.Edge(); - if ( nullptr == edge ) - return ON_SUBD_RETURN_ERROR(false); - - if (false == edge->RemoveFaceFromArray(face)) - return ON_SUBD_RETURN_ERROR(false); + if (nullptr != edge) + { + if (false == edge->RemoveFaceFromArray(face)) + return ON_SUBD_RETURN_ERROR(false); + } return true; } +bool ON_SubD::RemoveFaceConnections( + ON_SubDFace* face +) +{ + if ( nullptr == face ) + { + return ON_SUBD_RETURN_ERROR(false); + } + if (face->m_edge_count > 0) + { + ON_SubDEdgePtr removed_edge; + for (unsigned short fei = face->m_edge_count; fei > 0; --fei) + { + removed_edge = ON_SubDEdgePtr::Null; + if (false == face->RemoveEdgeFromArray(fei - 1, removed_edge)) + return ON_SUBD_RETURN_ERROR(false); + ON_SubDEdge* edge = removed_edge.Edge(); + if (nullptr != edge) + { + if (false == edge->RemoveFaceFromArray(face)) + return ON_SUBD_RETURN_ERROR(false); + + for (int evi = 0; evi < 2; ++evi) + { + ON_SubDVertex* v = const_cast(edge->m_vertex[evi]); + if (nullptr != v) + { + for (unsigned short vfi = 0; vfi < v->m_face_count; ++vfi) + { + if (face == v->m_faces[vfi]) + { + for (++vfi; vfi < v->m_face_count; ++vfi) + v->m_faces[vfi - 1] = v->m_faces[vfi]; + v->m_face_count--; + break; + } + } + } + } + + } + } + face->m_edge_count = 0; + } + + return true; +} + + static bool ON_SubDFace_GetSubdivisionPointError( const class ON_SubDFace* face, @@ -4873,24 +6402,42 @@ static bool ON_SubDFace_GetSubdivisionPointError( return ON_SUBD_RETURN_ERROR(false); } +const ON_3dPoint ON_SubDFace::SubdivisionPoint() const +{ + ON_3dPoint S; + return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint; +} + bool ON_SubDFace::GetSubdivisionPoint( - ON_SubD::SubDType subd_type, - bool bUseSavedSubdivisionPoint, - double subdivision_point[3] - ) const + double subdivision_point[3] +) const +{ + if (nullptr == subdivision_point) + return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, false); + + if (GetSavedSubdivisionPoint(subdivision_point)) + return true; + + if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point)) + { + SetSavedSubdivisionPoint(subdivision_point); + return true; + } + + return false; +} + +bool ON_SubDFace::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const { if (nullptr == subdivision_point) return ON_SubDFace_GetSubdivisionPointError(this,subdivision_point,false); - if (bUseSavedSubdivisionPoint && GetSavedSubdivisionPoint(subd_type,subdivision_point)) - return true; - const unsigned int count = m_edge_count; if (count < 3) return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); double displacementV[3] = { 0 }; - const bool bApplyDisplacement = GetDisplacement(subd_type,displacementV); + const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV); const class ON_SubDEdgePtr* edge_ptr = m_edge4; @@ -4929,16 +6476,11 @@ bool ON_SubDFace::GetSubdivisionPoint( subdivision_point[2] += displacementV[2]; } - if (bUseSavedSubdivisionPoint) - SetSavedSubdivisionPoint(subd_type,subdivision_point); - - return true; } if (3 == count) { - // most common case in triangle subdivision schemes and // 2nd most common case in quad subdivision schemes subdivision_point[0] = (vertexP[0][0] + vertexP[1][0] + vertexP[2][0]) / 3.0; subdivision_point[1] = (vertexP[0][1] + vertexP[1][1] + vertexP[2][1]) / 3.0; @@ -4951,9 +6493,6 @@ bool ON_SubDFace::GetSubdivisionPoint( subdivision_point[2] += displacementV[2]; } - if (bUseSavedSubdivisionPoint) - SetSavedSubdivisionPoint(subd_type,subdivision_point); - return true; } @@ -5013,53 +6552,64 @@ bool ON_SubDFace::GetSubdivisionPoint( subdivision_point[1] += displacementV[1]; subdivision_point[2] += displacementV[2]; } - - if (bUseSavedSubdivisionPoint) - SetSavedSubdivisionPoint(subd_type,subdivision_point); - return true; } +int ON_SubDComponentBase::CompareId( + const ON_SubDComponentBase* lhs, + const ON_SubDComponentBase* rhs +) +{ + if (lhs == rhs) + return 0; + // nulls at end of list + if (nullptr == rhs) + return -1; + if (nullptr == lhs) + return 1; + if (lhs->m_id < rhs->m_id) + return -1; + if (lhs->m_id > rhs->m_id) + return 1; + return 0; +} + +void ON_SubDComponentBase::ClearSavedSubdivisionPoint() const +{ + Internal_ClearSubdivisionPointAndSurfacePointFlags(); +} + bool ON_SubDComponentBase::SetSavedSubdivisionPoint( - ON_SubD::SubDType subd_type, const double subdivision_point[3] ) const { - if (ON_SubD::SubDType::Unset == subd_type) + if (nullptr == subdivision_point) { - ClearSavedSubdivisionPoint(); + Internal_ClearSubdivisionPointAndSurfacePointFlags(); return true; } - if ( nullptr != subdivision_point - && ON_IsValid(subdivision_point[0]) + if ( ON_IsValid(subdivision_point[0]) && ON_IsValid(subdivision_point[1]) && ON_IsValid(subdivision_point[2]) ) { - const unsigned char c = (unsigned char)subd_type; - if ( c != ON_SUBD_CACHE_TYPE(m_saved_points_flags)) - m_saved_points_flags = 0U; m_saved_subd_point1[0] = subdivision_point[0]; m_saved_subd_point1[1] = subdivision_point[1]; m_saved_subd_point1[2] = subdivision_point[2]; - m_saved_points_flags |= (ON_SUBD_CACHE_POINT_FLAG_MASK | c); + m_saved_points_flags |= ON_SUBD_CACHE_POINT_FLAG_BIT; return true; } - ClearSavedSubdivisionPoint(); + Internal_ClearSubdivisionPointAndSurfacePointFlags(); return ON_SUBD_RETURN_ERROR(false); } bool ON_SubDComponentBase::GetSavedSubdivisionPoint( - ON_SubD::SubDType subd_type, double subdivision_point[3] ) const { - if ( 0 == (ON_SUBD_CACHE_POINT_FLAG_MASK & m_saved_points_flags) ) - return false; - - if ( subd_type != (ON_SubD::SubDType)ON_SUBD_CACHE_TYPE(m_saved_points_flags) ) + if ( 0 == (ON_SUBD_CACHE_POINT_FLAG_BIT & m_saved_points_flags) ) return false; if (nullptr != subdivision_point) @@ -5072,12 +6622,81 @@ bool ON_SubDComponentBase::GetSavedSubdivisionPoint( return true; } +const ON_3dPoint ON_SubDComponentBase::SavedSubdivisionPoint() const +{ + ON_3dPoint p(ON_3dPoint::NanPoint); + return GetSavedSubdivisionPoint(&p.x) ? p : ON_3dPoint::NanPoint; +} + +unsigned const ON_SubDComponentBase::SubdivisionLevel() const +{ + return (unsigned)m_level; +} + +void ON_SubDComponentBase::SetSubdivisionLevel(unsigned level) +{ + if (level <= 255U) + m_level = ((unsigned char)level); +} const ON_ComponentStatus ON_SubDComponentBase::Status() const { return m_status; } +bool ON_SubDComponentBase::IsActive() const +{ + return (m_id >= 0 && m_archive_id != ON_UNSET_UINT_INDEX); +} + + +bool ON_SubDComponentBase::Mark() const +{ + return m_status.RuntimeMark(); +} + +bool ON_SubDComponentBase::ClearMark() const +{ + return m_status.ClearRuntimeMark(); +} + +bool ON_SubDComponentBase::SetMark() const +{ + return m_status.SetRuntimeMark(); +} + +bool ON_SubDComponentBase::SetMark( + bool bMark +) const +{ + return m_status.SetRuntimeMark(bMark); +} + +bool ON_SubDComponentPtr::IsActive() const +{ + const ON_SubDComponentBase* c = this->ComponentBase(); + return (nullptr != c) ? c->IsActive() : false; +} + +bool ON_SubDVertexPtr::IsActive() const +{ + const ON_SubDVertex* v = ON_SUBD_VERTEX_POINTER(m_ptr); + return (nullptr != v) ? v->IsActive() : false; +} + +bool ON_SubDEdgePtr::IsActive() const +{ + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr); + return (nullptr != e) ? e->IsActive() : false; +} + +bool ON_SubDFacePtr::IsActive() const +{ + const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr); + return (nullptr != f) ? f->IsActive() : false; +} + + const ON_ComponentStatus ON_SubDVertex::NeighborhoodStatusLogicalOr( bool bIncludeEdges, bool bIncludeFaces @@ -5178,7 +6797,7 @@ const ON_ComponentStatus ON_SubDFace::NeighborhoodStatusLogicalOr(bool bIncludeV static void Internal_ClearFaceNeighborhoodCache(const ON_SubDFace* face) { // Clear cached values for every component associated with this face. - face->ClearSavedSubdivisionPoint(); + face->ClearSavedSubdivisionPoints(); const ON_SubDEdgePtr* eptr = face->m_edge4; for (unsigned int efi = 0; efi < face->m_edge_count; efi++) { @@ -5191,16 +6810,13 @@ static void Internal_ClearFaceNeighborhoodCache(const ON_SubDFace* face) const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr != edge) { - edge->ClearSavedSubdivisionPoint(); - edge->UnsetSectorCoefficients(); + edge->ClearSavedSubdivisionPoints(); + edge->UnsetSectorCoefficientsForExperts(); for (unsigned int evi = 0; evi < 2; evi++) { const ON_SubDVertex* vertex = edge->m_vertex[evi]; if (nullptr != vertex) - { - vertex->ClearSavedSubdivisionPoint(); - vertex->ClearSavedLimitPoints(); - } + vertex->ClearSavedSubdivisionPoints(); } } eptr++; @@ -5209,8 +6825,7 @@ static void Internal_ClearFaceNeighborhoodCache(const ON_SubDFace* face) void ON_SubDVertex::VertexModifiedNofification() const { - ClearSavedSubdivisionPoint(); - ClearSavedLimitPoints(); + ClearSavedSubdivisionPoints(); if (nullptr != m_edges) { for (unsigned short vei = 0; vei < m_edge_count; vei++) @@ -5218,8 +6833,8 @@ void ON_SubDVertex::VertexModifiedNofification() const const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); if (nullptr != edge) { - edge->ClearSavedSubdivisionPoint(); - edge->UnsetSectorCoefficients(); + edge->ClearSavedSubdivisionPoints(); + edge->UnsetSectorCoefficientsForExperts(); } } @@ -5237,8 +6852,8 @@ void ON_SubDVertex::VertexModifiedNofification() const void ON_SubDEdge::EdgeModifiedNofification() const { - ClearSavedSubdivisionPoint(); - UnsetSectorCoefficients(); + ClearSavedSubdivisionPoints(); + UnsetSectorCoefficientsForExperts(); for (unsigned int evi = 0; evi < 2; evi++) { const_cast(this)->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorWeight; @@ -5265,12 +6880,33 @@ void ON_SubDEdge::EdgeModifiedNofification() const } } -void ON_SubDEdge::UnsetSectorCoefficients() const +void ON_SubDEdge::UnsetSectorCoefficientsForExperts() const { const_cast(this)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; const_cast(this)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; } +void ON_SubDVertex::UnsetSectorCoefficientsForExperts(unsigned int relative_edge_end_dex) const +{ + for (unsigned short vei = 0; vei < m_edge_count; ++vei) + { + ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); + if (nullptr == e) + continue; + ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(m_edges[vei].m_ptr); + const unsigned evi + = (relative_edge_end_dex < 2) + ? ((0 == edir ? false : true) == (0 == relative_edge_end_dex ? false : true) ? 0U : 1U) + : 2U; + if ( evi < 2) + e->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorWeight; + else + { + e->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; + e->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; + } + } +} void ON_SubDFace::FaceModifiedNofification() const { @@ -5309,77 +6945,103 @@ void ON_SubDFace::FaceModifiedNofification() const } } -void ON_SubDComponentBase::ClearSavedSubdivisionPoint() const +void ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags() const +{ + ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags); + ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(m_saved_points_flags); + ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(m_saved_points_flags); +} + +bool ON_SubDComponentBase::Internal_SubdivisionPointFlag() const +{ + return (0 != ON_SUBD_CACHE_POINT_FLAG(m_saved_points_flags)); +} + +void ON_SubDComponentBase::Internal_ClearSubdivisionPointFlag() const { ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags); } -ON_SubD::SubDType ON_SubDComponentBase::SavedSubdivisionPointType() const +bool ON_SubDComponentBase::Internal_SurfacePointFlag() const { - return - (0 != (ON_SUBD_CACHE_POINT_FLAG_MASK & m_saved_points_flags)) - ? ((ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags)) - : ON_SubD::SubDType::Unset; + return (0 != ON_SUBD_CACHE_LIMITLOC_FLAG(m_saved_points_flags)); } -ON_SubD::SubDType ON_SubDComponentBase::DisplacementType() const +bool ON_SubDComponentBase::Internal_ControlNetFragmentFlag() const { - return - (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) - ? ((ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags)) - : ON_SubD::SubDType::Unset; + return (0 != ON_SUBD_CACHE_CTRLNETFRAG_FLAG(m_saved_points_flags)); } -bool ON_SubDComponentBase::SetDisplacement( - ON_SubD::SubDType subd_type, +void ON_SubDComponentBase::Internal_ClearSurfacePointFlag() const +{ + ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(m_saved_points_flags); + ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(m_saved_points_flags); +} + +void ON_SubDComponentBase::Internal_ClearControlNetFragmentFlag() const +{ + ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(m_saved_points_flags); +} + +bool ON_SubDComponentBase::SavedSubdivisionPointIsSet() const +{ + return + (0 != ON_SUBD_CACHE_POINT_FLAG(m_saved_points_flags)) + ? (ON_IS_VALID(m_saved_subd_point1[0]) && ON_IS_VALID(m_saved_subd_point1[1]) && ON_IS_VALID(m_saved_subd_point1[2])) + : false; +} + +bool ON_SubDComponentBase::SubdivisionDisplacementIsNonzero() const +{ + return (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) + ? ( + (0.0 != m_displacement_V[0] || 0.0 != m_displacement_V[1] || 0.0 != m_displacement_V[2]) + && ON_IS_VALID(m_displacement_V[0]) && ON_IS_VALID(m_displacement_V[1]) && ON_IS_VALID(m_displacement_V[2]) + ) + : false; +} + +bool ON_SubDComponentBase::SetSubdivisionDisplacement( const double displacement[3] ) { - if ( ON_SubD::SubDType::Unset != subd_type - && nullptr != displacement - && ON_IsValid(displacement[0]) && ON_IsValid(displacement[1]) && ON_IsValid(displacement[2]) + if ( + nullptr == displacement + || (0.0 == displacement[0] && 0.0 == displacement[1] && 0.0 == displacement[2]) ) { - if (0.0 == displacement[0] && 0.0 == displacement[1] && 0.0 == displacement[2]) - { - ClearDisplacement(); - return true; - } - ON_SubD::SubDType f = (ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags); - if ( subd_type != f ) - m_saved_points_flags = (unsigned char)f; - m_saved_points_flags |= ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK; + ClearSubdivisionDisplacement(); + return true; + } + + if ( ON_IsValid(displacement[0]) && ON_IsValid(displacement[1]) && ON_IsValid(displacement[2]) ) + { + m_saved_points_flags |= ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT; m_displacement_V[0] = displacement[0]; m_displacement_V[1] = displacement[1]; m_displacement_V[2] = displacement[2]; return true; } - if (ON_SubD::SubDType::Unset == subd_type) - { - ClearDisplacement(); - return true; - } + ClearSubdivisionDisplacement(); return ON_SUBD_RETURN_ERROR(false); } -void ON_SubDComponentBase::ClearDisplacement() const +void ON_SubDComponentBase::ClearSubdivisionDisplacement() const { - if (0 != (m_saved_points_flags & ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK)) + if (0 != (m_saved_points_flags & ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT)) { ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags); ON_SUBD_CACHE_CLEAR_DISPLACEMENT_FLAG(m_saved_points_flags); } } -bool ON_SubDComponentBase::GetDisplacement( - ON_SubD::SubDType subd_type, +bool ON_SubDComponentBase::GetSubdivisionDisplacement( double displacement[3] ) const { - const bool rc = (0 != (ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK & m_saved_points_flags)) - && subd_type == (ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags); + const bool rc = (0 != (ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT & m_saved_points_flags)); if (nullptr != displacement) { if (rc) @@ -5398,6 +7060,27 @@ bool ON_SubDComponentBase::GetDisplacement( return rc; } +const ON_3dVector ON_SubDComponentBase::SubdivisionDisplacement() const +{ + ON_3dVector v(ON_3dVector::ZeroVector); + return GetSubdivisionDisplacement(&v.x) ? v : ON_3dVector::ZeroVector; +} + +void ON_SubDComponentBase::Internal_SetSavedSurfacePointFlag(bool bSavedSurfacePointFlag) const +{ + if (bSavedSurfacePointFlag) + m_saved_points_flags |= ON_SUBD_CACHE_LIMITLOC_FLAG_BIT; + else + Internal_ClearSurfacePointFlag(); +} + +void ON_SubDComponentBase::Internal_SetSavedControlNetFragmentFlag(bool bSavedControlNetFragmentFlag) const +{ + if (bSavedControlNetFragmentFlag) + m_saved_points_flags |= ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT; + else + Internal_ClearSurfacePointFlag(); +} bool ON_SubDFace::ReverseEdgeList() { @@ -5601,24 +7284,55 @@ unsigned int ON_SubDEdge::GetFacePointSum( return n; } +const ON_3dPoint ON_SubDEdge::SubdivisionPoint() const +{ + ON_3dPoint S; + return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint; +} + bool ON_SubDEdge::GetSubdivisionPoint( - ON_SubD::SubDType subd_type, - bool bUseSavedSubdivisionPoint, - double subdivision_point[3] - ) const + double subdivision_point[3] +) const { if (nullptr == subdivision_point) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, false); - if (bUseSavedSubdivisionPoint && GetSavedSubdivisionPoint(subd_type, subdivision_point)) + if (GetSavedSubdivisionPoint(subdivision_point)) return true; + if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point)) + { + SetSavedSubdivisionPoint(subdivision_point); + return true; + } + + return false; +} + +const ON_3dPoint ON_SubDEdge::ControlNetCenterPoint() const +{ + return 0.5*(ControlNetPoint(0) + ControlNetPoint(1)); +} + +const ON_3dVector ON_SubDEdge::ControlNetCenterNormal( + unsigned int edge_face_index +) const +{ + const ON_SubDFace* face = Face(edge_face_index); + return (nullptr != face) ? face->ControlNetCenterNormal() : ON_3dVector::NanVector; +} + +bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const +{ + if (nullptr == subdivision_point) + return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, false); + const ON_SubDVertex* edge_vertex[2] = { m_vertex[0], m_vertex[1] }; if (nullptr == edge_vertex[0] || nullptr == edge_vertex[1]) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, true); double displacementV[3] = { 0 }; - const bool bApplyDisplacement = GetDisplacement(subd_type,displacementV); + const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV); const double* edgeP[2] = { edge_vertex[0]->m_P, edge_vertex[1]->m_P }; @@ -5651,7 +7365,7 @@ bool ON_SubDEdge::GetSubdivisionPoint( if ( ON_UNSET_UINT_INDEX == tagged_end || 0.5 == m_sector_coefficient[tagged_end] - || (ON_SubD::EdgeTag::X == m_edge_tag) + || (ON_SubD::EdgeTag::SmoothX == m_edge_tag) ) { // ignore edge weights @@ -5694,9 +7408,6 @@ bool ON_SubDEdge::GetSubdivisionPoint( subdivision_point[2] += displacementV[2]; } - if (bUseSavedSubdivisionPoint) - SetSavedSubdivisionPoint(subd_type,subdivision_point); - return true; } @@ -5714,9 +7425,6 @@ bool ON_SubDEdge::GetSubdivisionPoint( subdivision_point[2] += displacementV[2]; } - if (bUseSavedSubdivisionPoint) - SetSavedSubdivisionPoint(subd_type,subdivision_point); - return true; } @@ -5734,9 +7442,6 @@ bool ON_SubDEdge::GetSubdivisionPoint( subdivision_point[2] += displacementV[2]; } - if (bUseSavedSubdivisionPoint) - SetSavedSubdivisionPoint(subd_type,subdivision_point); - return true; } @@ -5753,9 +7458,6 @@ bool ON_SubDEdge::GetSubdivisionPoint( subdivision_point[2] += displacementV[2]; } - if (bUseSavedSubdivisionPoint) - SetSavedSubdivisionPoint(subd_type,subdivision_point); - return true; } @@ -5919,380 +7621,6 @@ private: ON_ScratchBuffer& operator-(const ON_ScratchBuffer&); }; -////static bool IsSmoothManifoldEdge(const ON_SubDEdge* edge) -////{ -//// return (nullptr != edge && (ON_SubD::EdgeTag::Smooth == edge->m_edge_tag || ON_SubD::EdgeTag::X == edge->m_edge_tag) && 2 == edge->m_face_count); -////} - -////unsigned int ON_SubDimple::GetSector( -//// const ON_SubDFace* starting_face, -//// ON__UINT_PTR face_vertex_index, -//// ON_SubDVertex& sector -//// ) const -////{ -//// sector.m_edge_count = 0; -//// sector.m_face_count = 0; -//// -//// if (nullptr == starting_face || face_vertex_index >= starting_face->m_edge_count) -//// return GetSectorError(sector); -//// -//// unsigned short face_edge_index = (unsigned short)face_vertex_index; -//// ON__UINT_PTR edge0_ptr = starting_face->EdgePtr(face_edge_index > 0 ? (face_edge_index - 1) : (starting_face->m_edge_count - 1)).m_ptr; -//// ON__UINT_PTR edge1_ptr = starting_face->EdgePtr(face_edge_index).m_ptr; -//// const ON_SubDEdge* edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr); -//// const ON_SubDEdge* edge1 = ON_SUBD_EDGE_POINTER(edge1_ptr); -//// if (nullptr == edge0 || nullptr == edge1) -//// return GetSectorError(sector); -//// ON__UINT_PTR edge0_end = 1 - ON_SUBD_EDGE_DIRECTION(edge0_ptr); -//// ON__UINT_PTR edge1_end = ON_SUBD_EDGE_DIRECTION(edge1_ptr); -//// -//// const ON_SubDVertex* vertex = edge0->m_vertex[edge0_end]; -//// if ( nullptr == vertex || vertex->m_face_count < 1 || vertex->m_edge_count < 2) -//// return GetSectorError(sector); -//// if (vertex != edge1->m_vertex[edge1_end]) -//// return GetSectorError(sector); -//// -//// const unsigned int sector_capacity = vertex->m_face_count; -//// const size_t buffer_capacity = (size_t)(3*sector_capacity + 2); -//// ON__UINT_PTR stack_buffer[3 * 16 + 2]; -//// ON_ScratchBuffer buffer(buffer_capacity*sizeof(stack_buffer[0]), stack_buffer, sizeof(stack_buffer)); -//// if (nullptr == buffer.Buffer()) -//// return GetSectorError(sector); -//// -//// const ON_SubDFace** sector_faces = (const ON_SubDFace**)buffer.Buffer(); -//// const ON_SubDEdge** sector_edges = (const ON_SubDEdge**)(sector_faces + sector_capacity); -//// ON__UINT_PTR* sector_edge_ends = (ON__UINT_PTR*)(sector_edges + (sector_capacity + 1)); -//// -//// const ON_SubDFace* face0 = starting_face; -//// sector_faces[0] = face0; -//// sector_edges[0] = edge0; -//// sector_edge_ends[0] = edge0_end; -//// sector_edges[1] = edge1; -//// sector_edge_ends[1] = edge1_end; -//// unsigned int sector_index = 1; -//// unsigned int right_side_sector_count = 1; -//// -//// if (false == IsSmoothManifoldEdge(edge0) && false == IsSmoothManifoldEdge(edge1)) -//// { -//// // both edges act as creases -//// sector_edges[sector_capacity] = edge1; -//// sector_edge_ends[sector_capacity] = edge1_end; -//// } -//// else -//// { -//// // at least one input edge is a smooth manifold edge (2 faces) -//// for (unsigned int sector_side = 0; sector_side < 2; sector_side++) -//// { -//// const ON_SubDFace* face1 = nullptr; -//// while (sector_index < sector_capacity) -//// { -//// if (false == IsSmoothManifoldEdge(edge1)) -//// break; -//// -//// face1 = edge1->NeighborFace(face0,true); -//// if (sector_faces[0] == face1) -//// { -//// // circled around vertex back where we started. -//// // Since the edge trap at the end of the for loop -//// // did not break, this is an error condition. -//// return GetSectorError(sector); -//// } -//// -//// if (nullptr == face1 || face0 == face1) -//// return GetSectorError(sector); -//// -//// unsigned int face1_edge_count = face1->m_edge_count; -//// if (face1_edge_count < 2) -//// return GetSectorError(sector); -//// -//// unsigned int face1_edge0_index = face1->EdgeArrayIndex(edge1); -//// if (ON_UNSET_UINT_INDEX == face1_edge0_index) -//// return GetSectorError(sector); -//// edge0_ptr = face1->EdgePtr(face1_edge0_index).m_ptr; -//// edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr); -//// if (edge0 != edge1) -//// return GetSectorError(sector); -//// ON__UINT_PTR edge0_dir = ON_SUBD_EDGE_DIRECTION(edge0_ptr); // edge0 - face1 natural orientation relationship -//// edge0_end = edge1_end; -//// -//// // If 1 == (edge0_end + edge0_dir), then face0 and face1 have same natural -//// // orientations across shared edge and new edge1 = next edge on face1. -//// // If 1 != edge0_to_edge1_index_delta, then face0 and face1 opposite same natural -//// // orientations across shared edge and new edge1 = prev edge on face1. -//// unsigned int face1_edge1_index -//// = (1 == (edge0_end + edge0_dir)) -//// ? (face1_edge0_index + 1) -//// : (face1_edge0_index + (face1_edge_count - 1)); -//// face1_edge1_index %= face1_edge_count; -//// -//// edge1_ptr = face1->EdgePtr(face1_edge1_index).m_ptr; -//// edge1 = ON_SUBD_EDGE_POINTER(edge1_ptr); -//// if (nullptr == edge1) -//// return GetSectorError(sector); -//// ON__UINT_PTR edge1_dir = ON_SUBD_EDGE_DIRECTION(edge1_ptr); // edge1 - face1 natural orientation relationship -//// edge1_end = (1 == ((edge0_end + edge0_dir + edge1_dir) % 2)) ? 0 : 1; -//// if (vertex != edge1->m_vertex[edge1_end]) -//// return GetSectorError(sector); -//// if (vertex == edge1->m_vertex[1 - edge1_end]) -//// return GetSectorError(sector); -//// -//// face0 = face1; -//// sector_faces[sector_index] = face0; -//// if (0 == sector_side) -//// { -//// sector_edges[sector_index] = edge0; -//// sector_edge_ends[sector_index] = edge0_end; -//// sector_index++; -//// if (edge1 == sector_edges[0]) -//// { -//// // circled around back to where we started -//// break; -//// } -//// } -//// else -//// { -//// sector_edges[sector_index] = edge1; -//// sector_edge_ends[sector_index] = edge1_end; -//// sector_index++; -//// } -//// } -//// -//// if (0 == sector_side) -//// { -//// // finished first side -//// -//// // Mark where the first side information ends. -//// right_side_sector_count = sector_index; -//// -//// // Save the final boundary information in a place where -//// // the 2nd pass will not write over it. -//// sector_edges[sector_capacity] = edge1; -//// sector_edge_ends[sector_capacity] = edge1_end; -//// -//// if (sector_edges[0] == sector_edges[sector_capacity]) -//// { -//// // If sector_edge_ends[0] is a smooth 2-faced edge, then -//// // we circled around the vertex through smooth 2-faced edges -//// // until we got back to the starting vertex. -//// // -//// // If sector_edge_ends[0] is a crease, then the vertex is a dart. -//// // -//// // If sector_edge_ends[0] has more than 2 faces, then we've gone -//// // around a manifold sector that "begins/ends" at sector_edge_ends[0] -//// break; -//// } -//// -//// // prepare for second side -//// face0 = sector_faces[0]; -//// edge1 = sector_edges[0]; -//// edge1_end = sector_edge_ends[0]; -//// } -//// else -//// { -//// // finished second side -//// if (sector_faces[0] == face1) -//// return GetSectorError(sector); -//// -//// break; -//// } -//// } -//// } -//// -//// if (0 == sector_index) -//// return GetSectorError(sector); -//// -//// if (nullptr == sector_edges[0] || nullptr == sector_edges[sector_index - 1] || nullptr == sector_edges[sector_capacity]) -//// return GetSectorError(sector); -//// -//// if (IsSmoothManifoldEdge(sector_edges[sector_capacity])) -//// { -//// if (right_side_sector_count < 2) -//// return GetSectorError(sector); -//// if (sector_index > right_side_sector_count) -//// return GetSectorError(sector); -//// if (sector_edges[0] != sector_edges[sector_capacity]) -//// return GetSectorError(sector); -//// } -//// else if (sector_index > right_side_sector_count) -//// { -//// if (IsSmoothManifoldEdge(sector_edges[sector_index-1])) -//// return GetSectorError(sector); -//// } -//// -//// if (!InitializeSector(vertex, sector)) -//// return GetSectorError(sector); -//// -//// if (false == const_cast(this)->m_heap.GrowVertexEdgeArray(§or, sector_index + 1)) -//// return GetSectorError(sector); -//// if (false == const_cast(this)->m_heap.GrowVertexFaceArray(§or, sector_index)) -//// return GetSectorError(sector); -//// unsigned int sector_face_count = 0; -//// while (sector_index > right_side_sector_count) -//// { -//// sector_index--; -//// sector.m_edges[sector_face_count] = ON_SubDEdgePtr::Create(sector_edges[sector_index], sector_edge_ends[sector_index]); -//// sector.m_faces[sector_face_count] = sector_faces[sector_index]; -//// sector_face_count++; -//// } -//// for (sector_index = 0; sector_index < right_side_sector_count; sector_index++) -//// { -//// sector.m_edges[sector_face_count] = ON_SubDEdgePtr::Create(sector_edges[sector_index], sector_edge_ends[sector_index]); -//// sector.m_faces[sector_face_count] = sector_faces[sector_index]; -//// sector_face_count++; -//// } -//// -//// sector.m_edges[sector_face_count] = ON_SubDEdgePtr::Create(sector_edges[sector_capacity], sector_edge_ends[sector_capacity]); -//// sector.m_face_count = (unsigned short)sector_face_count; -//// sector.m_edge_count = sector.m_face_count+1; -//// -//// return sector_face_count; -////} - -////unsigned int ON_SubDimple::GetSector( -//// const ON_SubDVertex* vertex, -//// const ON_SubDFace* face, -//// ON_SubDVertex& sector -//// ) const -////{ -//// unsigned int face_vertex_index = (nullptr != face) ? face->VertexIndex(vertex) : ON_UNSET_UINT_INDEX; -//// if (ON_UNSET_UINT_INDEX == face_vertex_index) -//// return GetSectorError(sector); -//// return GetSector(face, face_vertex_index, sector); -////} -//// -////unsigned int ON_SubDimple::GetSector( -//// const ON_SubDVertex* vertex, -//// ON_SubDFacePtr face_ptr, -//// ON_SubDVertex& sector -//// ) const -////{ -//// if (nullptr == vertex) -//// return GetSectorError(sector); -//// return GetSector(vertex, ON_SUBD_FACE_POINTER(face_ptr.m_ptr), sector); -////} -//// -////unsigned int ON_SubDimple::GetSector( -//// const ON_SubDEdge* smooth_edge, -//// ON__UINT_PTR smooth_edge_end_index, -//// ON_SubDVertex& sector -//// ) const -////{ -//// if (nullptr == smooth_edge || smooth_edge_end_index > 1) -//// return GetSectorError(sector); -//// const ON_SubDVertex* vertex = smooth_edge->m_vertex[smooth_edge_end_index]; -//// if (nullptr == vertex) -//// return GetSectorError(sector); -//// -//// switch (smooth_edge->m_face_count) -//// { -//// case 0: -//// if (!InitializeSector(vertex, sector)) -//// break; -//// sector.m_edges = (ON_SubDEdgePtr*)const_cast(this)->m_heap.GrowArrayByOneElement(sector.m_edge_count, (ON__UINT_PTR*)sector.m_edges); -//// sector.m_edges[sector.m_edge_count++] = ON_SubDEdgePtr::Create(smooth_edge, smooth_edge_end_index); -//// return true; -//// break; -//// -//// case 1: -//// return GetSector(vertex, ON_SUBD_FACE_POINTER(smooth_edge->m_face2[0].m_ptr), sector); -//// break; -//// -//// case 2: -//// if (ON_SubD::EdgeTag::Smooth == smooth_edge->m_edge_tag) -//// return GetSector(vertex, ON_SUBD_FACE_POINTER(smooth_edge->m_face2[0].m_ptr), sector); -//// break; -//// } -//// -//// return GetSectorError(sector); -////} -//// -////unsigned int ON_SubDimple::GetSector( -//// const ON_SubDVertex* vertex, -//// const ON_SubDEdge* smooth_edge, -//// ON_SubDVertex& sector -//// ) const -////{ -//// if (nullptr == vertex || nullptr == smooth_edge) -//// return GetSectorError(sector); -//// unsigned int smooth_edge_end_index -//// = (vertex == smooth_edge->m_vertex[0]) -//// ? 0 -//// : ((vertex == smooth_edge->m_vertex[1]) ? 1 : ON_UNSET_UINT_INDEX); -//// if (ON_UNSET_UINT_INDEX == smooth_edge_end_index) -//// return GetSectorError(sector); -//// return GetSector(smooth_edge, smooth_edge_end_index, sector); -////} -//// -//// -////unsigned int ON_SubDimple::GetSector( -//// ON_SubDEdgePtr smooth_edge_ptr, -//// ON_SubDVertex& sector -//// ) const -////{ -//// return GetSector(ON_SUBD_EDGE_POINTER(smooth_edge_ptr.m_ptr), ON_SUBD_EDGE_DIRECTION(smooth_edge_ptr.m_ptr), sector); -////} -//// -////unsigned int ON_SubD::GetSector( -//// const ON_SubDFace* face, -//// ON__UINT_PTR face_vertex_index, -//// ON_SubDVertex& sector -//// ) const -////{ -//// const ON_SubDimple* subdimple = SubDimple(); -//// return (nullptr != subdimple) ? subdimple->GetSector(face, face_vertex_index, sector) : GetSectorError(sector); -////} -//// -////unsigned int ON_SubD::GetSector( -//// const ON_SubDVertex* vertex, -//// const ON_SubDFace* face, -//// ON_SubDVertex& sector -//// ) const -////{ -//// const ON_SubDimple* subdimple = SubDimple(); -//// return (nullptr != subdimple) ? subdimple->GetSector(vertex, face, sector) : GetSectorError(sector); -////} -//// -////unsigned int ON_SubD::GetSector( -//// const ON_SubDVertex* vertex, -//// ON_SubDFacePtr face_ptr, -//// ON_SubDVertex& sector -//// ) const -////{ -//// const ON_SubDimple* subdimple = SubDimple(); -//// return (nullptr != subdimple) ? subdimple->GetSector(vertex, face_ptr, sector) : GetSectorError(sector); -////} -//// -////unsigned int ON_SubD::GetSector( -//// const ON_SubDVertex* vertex, -//// const ON_SubDEdge* smooth_edge, -//// ON_SubDVertex& sector -//// ) const -////{ -//// const ON_SubDimple* subdimple = SubDimple(); -//// return (nullptr != subdimple) ? subdimple->GetSector(vertex, smooth_edge, sector) : GetSectorError(sector); -////} -//// -////unsigned int ON_SubD::GetSector( -//// const ON_SubDEdge* smooth_edge, -//// ON__UINT_PTR smooth_edge_end_index, -//// ON_SubDVertex& sector -//// ) const -////{ -//// const ON_SubDimple* subdimple = SubDimple(); -//// return (nullptr != subdimple) ? subdimple->GetSector(smooth_edge, smooth_edge_end_index, sector) : GetSectorError(sector); -////} -//// -////unsigned int ON_SubD::GetSector( -//// ON_SubDEdgePtr smooth_edge_ptr, -//// ON_SubDVertex& sector -//// ) const -////{ -//// const ON_SubDimple* subdimple = SubDimple(); -//// return (nullptr != subdimple) ? subdimple->GetSector(smooth_edge_ptr, sector) : GetSectorError(sector); -////} - - - class FACE_AND_FACE_POINT { public: @@ -6312,17 +7640,17 @@ int FACE_AND_FACE_POINT::CompareFacePointer(const void* a, const void* b) return 0; } -bool ON_SubDSectorLimitPoint::IsUnset() const +bool ON_SubDSectorSurfacePoint::IsUnset() const { return (m_limitP[0] == ON_UNSET_VALUE); } -bool ON_SubDSectorLimitPoint::IsNan() const +bool ON_SubDSectorSurfacePoint::IsNan() const { return !(m_limitP[0] == m_limitP[0]); } -bool ON_SubDSectorLimitPoint::IsZero() const +bool ON_SubDSectorSurfacePoint::IsZero() const { const double* p = m_limitP; const double* p1 = p+12; @@ -6334,7 +7662,9 @@ bool ON_SubDSectorLimitPoint::IsZero() const return true; } -bool ON_SubDSectorLimitPoint::IsSet() const +bool ON_SubDSectorSurfacePoint::IsSet( + bool bUndefinedNormalIsPossible +) const { double x, y; const double* p = m_limitP; @@ -6346,37 +7676,40 @@ bool ON_SubDSectorLimitPoint::IsSet() const return false; } - p = m_limitT1; - p1 = p+6; - while (p < p1) + if (false == bUndefinedNormalIsPossible) { - const double* p2 = p+3; + p = m_limitT1; + p1 = p + 6; + while (p < p1) + { + const double* p2 = p + 3; + y = 0.0; + while (p < p2) + { + x = *p++; + if (ON_UNSET_VALUE == x || !(x == x)) + return false; + if (0.0 != x) + y = x; + } + if (!(y != 0.0)) + return false; + } + + p = m_limitN; + p1 = p + 3; y = 0.0; - while (p < p2) + while (p < p1) { x = *p++; if (ON_UNSET_VALUE == x || !(x == x)) return false; - if ( 0.0 != x ) - y = x; + y += x * x; } - if (!(y != 0.0)) + if (!(fabs(y - 1.0) <= 1e-4)) return false; } - p = m_limitN; - p1 = p+3; - y = 0.0; - while (p < p1) - { - x = *p++; - if (ON_UNSET_VALUE == x || !(x == x)) - return false; - y += x*x; - } - if (!(fabs(y - 1.0) <= 1e-4)) - return false; - return true; } @@ -6390,12 +7723,10 @@ void ON_SubDVertex::CopyFrom( if (nullptr == src) src = &ON_SubDVertex::Empty; - ClearSavedLimitPoints(); + ClearSavedSubdivisionPoints(); CopyBaseFrom(src); m_vertex_tag = src->m_vertex_tag; - //m_vertex_edge_order = src->m_vertex_edge_order; - //m_vertex_facet_type = src->m_vertex_facet_type; m_P[0] = src->m_P[0]; m_P[1] = src->m_P[1]; @@ -6404,14 +7735,13 @@ void ON_SubDVertex::CopyFrom( if (bCopyLimitPointList) { - ON_SubD::SubDType limit_point_subd_type = src->SavedLimitPointType(); - if (ON_SubD::SubDType::Unset != limit_point_subd_type) + if ( src->SurfacePointIsSet() ) { - for (const ON_SubDSectorLimitPoint* p = &src->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point) + for (const ON_SubDSectorSurfacePoint* p = &src->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point) { - ON_SubDSectorLimitPoint limit_point = *p; - limit_point.m_next_sector_limit_point = (ON_SubDSectorLimitPoint*)1; // disable checks - SetSavedLimitPoint(limit_point_subd_type, limit_point); + ON_SubDSectorSurfacePoint limit_point = *p; + limit_point.m_next_sector_limit_point = (ON_SubDSectorSurfacePoint*)1; // disable checks + SetSavedSurfacePoint( true, limit_point); } } } @@ -6457,7 +7787,7 @@ static bool ON_SubDVertex_GetSubdivisionPointError( ON_SubDIncrementErrorCount(); vertex->m_status.SetDamagedState(bDamagedState); - vertex->ClearSavedSubdivisionPoint(); + vertex->ClearSavedSubdivisionPoints(); if (nullptr != vertexP) { @@ -6469,17 +7799,39 @@ static bool ON_SubDVertex_GetSubdivisionPointError( return true; } -bool ON_SubDVertex::GetGeneralQuadSubdivisionPoint( +bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( const class ON_SubDVertex* vertex, - bool bUseSavedSubdivisionPoint, double vertex_point[3] - ) +) { - const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::QuadCatmullClark; + if (nullptr != vertex_point) + { + vertex_point[0] = ON_DBL_QNAN; + vertex_point[1] = ON_DBL_QNAN; + vertex_point[2] = ON_DBL_QNAN; + } + + if (nullptr == vertex) + { + ON_SUBD_ERROR("input vertex is nullptr."); + return false; + } + + + const unsigned int n = vertex->m_face_count; + if (nullptr == vertex + || nullptr == vertex->m_faces + || nullptr == vertex->m_edges + || vertex->m_face_count != vertex->m_edge_count + || n < ON_SubDSectorType::MinimumSectorFaceCount(ON_SubD::VertexTag::Smooth) + ) + { + ON_SUBD_ERROR("input vertex is not valid."); + return false; + } const double* vertexP = vertex->m_P; - const unsigned int n = vertex->m_face_count; // It is critical to use the centroids of the neighboring faces // in this step because the number of edges in each face's @@ -6492,7 +7844,7 @@ bool ON_SubDVertex::GetGeneralQuadSubdivisionPoint( if (nullptr != face) { double faceC[3]; - if (face->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoint, faceC)) + if (face->GetSubdivisionPoint( faceC)) { facePsum[0] += faceC[0]; facePsum[1] += faceC[1]; @@ -6535,15 +7887,11 @@ bool ON_SubDVertex::GetGeneralQuadSubdivisionPoint( vertex_point[1] = v_weight*vertexP[1] + ef_weight*(edgePsum[1] + facePsum[1]); vertex_point[2] = v_weight*vertexP[2] + ef_weight*(edgePsum[2] + facePsum[2]); - if (bUseSavedSubdivisionPoint) - vertex->SetSavedSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,vertex_point); - return true; } -bool ON_SubDVertex::GetQuadPoint( +bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( const class ON_SubDVertex* vertex, - bool bUseSavedSubdivisionPoint, double vertex_point[3] ) { @@ -6558,7 +7906,8 @@ bool ON_SubDVertex::GetQuadPoint( const unsigned int n = (nullptr != vertex->m_edges ? vertex->m_edge_count : 0); if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag || ON_SubD::VertexTag::Dart == vertex->m_vertex_tag) { - if (n < 3 || n != vertex->m_face_count || nullptr == vertex->m_faces) + const unsigned int minimum_n = ON_SubDSectorType::MinimumSectorEdgeCount(vertex->m_vertex_tag); + if (n < minimum_n || n != vertex->m_face_count || nullptr == vertex->m_faces) return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); double facePsum[3] = { 0 }; @@ -6580,7 +7929,7 @@ bool ON_SubDVertex::GetQuadPoint( for (unsigned int i = 0; i < n; i++) { const ON_SubDFace* vface = vertex_faces[i]; - const unsigned int face_n = ON_SubDVertex::GetFacePointSum(vface, vertex, sum); + const unsigned int face_n = ON_SubDVertex::Internal_GetFacePointSum(vface, vertex, sum); if (4 != face_n) { // The first face is a quadrangle and this face is not a quadrangle. @@ -6588,7 +7937,7 @@ bool ON_SubDVertex::GetQuadPoint( // It is critical to use the centroids of the neighboring faces // for this vertex subdivision point because the number of edges // in each face's boundary is not constant. - return ON_SubDVertex::GetGeneralQuadSubdivisionPoint(vertex, bUseSavedSubdivisionPoint, vertex_point); + return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point); } facePsum[0] += sum[0]; facePsum[1] += sum[1]; @@ -6608,7 +7957,7 @@ bool ON_SubDVertex::GetQuadPoint( // It is critical to use the centroids of the neighboring faces // for this vertex subdivision point because the number of edges // in each face's boundary is not constant. - return ON_SubDVertex::GetGeneralQuadSubdivisionPoint(vertex, bUseSavedSubdivisionPoint, vertex_point); + return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point); } } } @@ -6623,7 +7972,7 @@ bool ON_SubDVertex::GetQuadPoint( // in each face's boundary may not constant. In any case, this // situation is not common and typically happens only on the // first subdivision step. - return ON_SubDVertex::GetGeneralQuadSubdivisionPoint(vertex, bUseSavedSubdivisionPoint, vertex_point); + return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point); } double edgePsum[3] = { 0 }; @@ -6669,9 +8018,6 @@ bool ON_SubDVertex::GetQuadPoint( vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2]; } - if (bUseSavedSubdivisionPoint) - vertex->SetSavedSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,vertex_point); - return true; } @@ -6680,24 +8026,41 @@ bool ON_SubDVertex::GetQuadPoint( } +const ON_3dPoint ON_SubDVertex::SubdivisionPoint() const +{ + ON_3dPoint S; + return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint; +} + bool ON_SubDVertex::GetSubdivisionPoint( - ON_SubD::SubDType subd_type, - bool bUseSavedSubdivisionPoint, - double subdivision_point[3] - ) const + double subdivision_point[3] +) const +{ + if (nullptr == subdivision_point) + return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, nullptr, false); + + if (GetSavedSubdivisionPoint(subdivision_point)) + return true; + + if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point)) + { + SetSavedSubdivisionPoint(subdivision_point); + return true; + } + + return false; +} + +bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const { // This function is used to convert an arbitrary control polygon into the // "level 1" subD. It cannot use the faster sub-D formulas because // a face can have an arbitrary number of edges. - if (nullptr == subdivision_point - || (ON_SubD::SubDType::TriLoopWarren != subd_type && ON_SubD::SubDType::QuadCatmullClark != subd_type)) + if (nullptr == subdivision_point ) return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, nullptr, false); - if ( bUseSavedSubdivisionPoint && GetSavedSubdivisionPoint(subd_type,subdivision_point) ) - return true; - double displacementV[3] = { 0 }; - const bool bApplyDisplacement = GetDisplacement(subd_type,displacementV); + const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV); const double* vertexP = m_P; @@ -6707,10 +8070,7 @@ bool ON_SubDVertex::GetSubdivisionPoint( if (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag) { - if (ON_SubD::SubDType::QuadCatmullClark == subd_type) - return ON_SubDVertex::GetQuadPoint(this, bUseSavedSubdivisionPoint, subdivision_point); - else if (ON_SubD::SubDType::TriLoopWarren == subd_type) - return ON_SubDVertex::GetTriPoint(this, bUseSavedSubdivisionPoint, subdivision_point); + return ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint(this, subdivision_point); } if (ON_SubD::VertexTag::Crease == m_vertex_tag) @@ -6765,9 +8125,6 @@ bool ON_SubDVertex::GetSubdivisionPoint( subdivision_point[2] += displacementV[2]; } - if (bUseSavedSubdivisionPoint) - SetSavedSubdivisionPoint(subd_type,subdivision_point); - return true; } @@ -6788,9 +8145,6 @@ bool ON_SubDVertex::GetSubdivisionPoint( subdivision_point[2] += displacementV[2]; } - if (bUseSavedSubdivisionPoint) - SetSavedSubdivisionPoint(subd_type,subdivision_point); - return true; } @@ -6798,7 +8152,7 @@ bool ON_SubDVertex::GetSubdivisionPoint( return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); } -unsigned int ON_SubDVertex::GetFacePointSum( +unsigned int ON_SubDVertex::Internal_GetFacePointSum( const ON_SubDFace* face, const ON_SubDVertex* vertex, double* facePsum @@ -6900,85 +8254,239 @@ unsigned int ON_SubDVertex::GetFacePointSum( return 0; } -bool ON_SubDVertex::GetTriPoint( - const class ON_SubDVertex* vertex, - bool bUseSavedSubdivisionPoint, - double vertex_point[3] - ) +bool ON_SubDimple::LocalSubdivide( + ON_SubDFace const*const* face_list, + size_t face_list_count +) { - if (nullptr == vertex || nullptr == vertex_point) - return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, nullptr, false); + if (nullptr == face_list || face_list_count < 1 || m_levels.UnsignedCount() < 1) + return false; - const double* vertexP = vertex->m_P; - - const unsigned int n = (nullptr != vertex->m_edges ? vertex->m_edge_count : 0); - if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag || ON_SubD::VertexTag::Dart == vertex->m_vertex_tag) + unsigned int level0_index = ON_UNSET_UINT_INDEX; + for (size_t i = 0; i < face_list_count; ++i) { - if (n < 3) - return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); + const ON_SubDFace* f = face_list[i]; + if (nullptr == f || f->m_edge_count < 3 || f->SubdivisionLevel() >= m_levels.UnsignedCount()) + continue; + level0_index = f->SubdivisionLevel(); + break; + } + + if (level0_index >= m_levels.UnsignedCount() || nullptr == m_levels[level0_index]) + return ON_SUBD_RETURN_ERROR(false); - double edgePsum[3] = { 0 }; - const ON_SubDEdgePtr* edges = vertex->m_edges; - ON__UINT_PTR e_ptr; - for (unsigned int i = 0; i < n; i++) + ClearHigherSubdivisionLevels(level0_index + 1); + if (level0_index + 1 != m_levels.UnsignedCount()) + return ON_SUBD_RETURN_ERROR(false); + + m_active_level = m_levels[level0_index]; + if ( nullptr == m_active_level || 0 == m_active_level->m_face_count) + return ON_SUBD_RETURN_ERROR(false); + + ON_SubDLevel& level0 = *m_levels[level0_index]; + m_active_level = &level0; + + level0.ClearRuntimeMarks(true, true, true); + + unsigned face_count = 0; + unsigned edge_count = 0; + unsigned vertex_count = 0; + ON_3dPoint P; + for (size_t i = 0; i < face_list_count; ++i) + { + const ON_SubDFace* f = face_list[i]; + if (nullptr == f || f->m_edge_count < 3 || level0_index != f->SubdivisionLevel() ) + continue; + if (f->m_status.RuntimeMark()) + continue; + f->m_status.SetRuntimeMark(); + if ( false == f->GetSubdivisionPoint(P)) + return ON_SUBD_RETURN_ERROR(false); + const ON_SubDEdgePtr* eptr = f->m_edge4; + for (unsigned short fei = 0; fei < f->m_edge_count; ++fei, ++eptr) { - e_ptr = edges[i].m_ptr; - const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(e_ptr); - if (nullptr != edge) + if (4 == fei) { - const ON_SubDVertex* edge_vertex = (vertex != edge->m_vertex[0]) ? edge->m_vertex[0] : edge->m_vertex[1]; - if (nullptr != edge_vertex) - { - const double* edgeP = edge_vertex->m_P; - edgePsum[0] += edgeP[0]; - edgePsum[1] += edgeP[1]; - edgePsum[2] += edgeP[2]; - continue; - } + eptr = f->m_edgex; + if (nullptr == eptr) + return ON_SUBD_RETURN_ERROR(false); } - // treat missing or damaged face as infinitesimally small - edgePsum[0] += vertexP[0]; - edgePsum[1] += vertexP[1]; - edgePsum[2] += vertexP[2]; + ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); + if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) + return ON_SUBD_RETURN_ERROR(false); + if (e->m_status.RuntimeMark()) + continue; + e->UpdateEdgeSectorCoefficientsForExperts(true); + e->m_status.SetRuntimeMark(); + if (false == e->GetSubdivisionPoint(P)) + return ON_SUBD_RETURN_ERROR(false); + for (unsigned evi = 0; evi < 2; ++evi) + { + if (e->m_vertex[evi]->m_status.RuntimeMark()) + continue; + e->m_vertex[evi]->m_status.SetRuntimeMark(); + if ( false == e->m_vertex[evi]->GetSubdivisionPoint(P) ) + return ON_SUBD_RETURN_ERROR(false); + ++vertex_count; + } + ++edge_count; } + ++face_count; + } + if (face_count < 1 || edge_count < 3 || vertex_count < 3) + return false; + if (face_count >= level0.m_face_count) + return GlobalSubdivide(); - double v_weight, e_weight; - if (3 == n) - { - v_weight = 0.4375; // 7/16 - e_weight = 0.1875; // 3/16 = (9/16) / 3 - } - else - { - v_weight = 0.625; // 5/8 - e_weight = 0.375/((double)n); // = (3/8)/n - } - vertex_point[0] = v_weight*vertexP[0] + e_weight*edgePsum[0]; - vertex_point[1] = v_weight*vertexP[1] + e_weight*edgePsum[1]; - vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2]; + // Get face subdivision points + ON_SimpleArray faces(face_count); + ON_SimpleArray face_points(face_count); + for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) + { + if (false == f0->m_status.RuntimeMark()) + continue; + faces.Append(const_cast(f0)); + P = f0->SubdivisionPoint(); + if ( false == P.IsValid()) + return ON_SUBD_RETURN_ERROR(false); + face_points.Append(P); + } + if (face_count != face_points.UnsignedCount()) + return ON_SUBD_RETURN_ERROR(false); - if (bUseSavedSubdivisionPoint) - vertex->SetSavedSubdivisionPoint(ON_SubD::SubDType::TriLoopWarren,vertex_point); + // Get edge subdivision points + ON_SimpleArray edge_points(edge_count); + ON_SimpleArray edges(edge_count); + for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge) + { + if (false == e0->m_status.RuntimeMark()) + continue; + P = (e0->IsSmooth() && 2 == e0->m_face_count && 2 == e0->MarkedFaceCount()) ? e0->SubdivisionPoint() : e0->ControlNetCenterPoint(); + edge_points.Append(P); + edges.Append(const_cast(e0)); + } + if ( edge_count != edge_points.UnsignedCount()) + return ON_SUBD_RETURN_ERROR(false); - return true; + // Set vertex points + for (const ON_SubDVertex* v0 = level0.m_vertex[0]; nullptr != v0; v0 = v0->m_next_vertex) + { + if (false == v0->m_status.RuntimeMark() || v0->m_edge_count < 2 || ((unsigned)v0->m_edge_count) != v0->MarkedEdgeCount()) + continue; + P = v0->SubdivisionPoint(); + ON_SubDVertex* v = const_cast(v0); + v->m_P[0] = P.x; + v->m_P[1] = P.y; + v->m_P[2] = P.z; } - // vertex->m_vertex_tag is damaged - return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); + // split edges + for (unsigned edex = 0; edex < edge_count; ++edex) + { + ON_SubDEdge* e = edges[edex]; + e->EdgeModifiedNofification(); + const ON_SubDEdge* new_edge = SplitEdge(e, edge_points[edex]); + if (nullptr == new_edge) + return ON_SUBD_RETURN_ERROR(false); + new_edge->m_status.ClearRuntimeMark(); + e->m_status.SetRuntimeMark(); + } + + ON_SimpleArray fbdry(32); + ON_SimpleArray radial_edges(32); + for (unsigned fdex = 0; fdex < face_count; ++fdex) + { + ON_SubDFace* f = faces[fdex]; + f->FaceModifiedNofification(); + P = face_points[fdex]; + fbdry.SetCount(0); + const unsigned e_count = f->GetEdgeArray(fbdry); + if (e_count < 6 || 0 != e_count %2) + return ON_SUBD_RETURN_ERROR(false); + + // Get edges[] with edge[0] = marked edges. + const ON_SubDEdgePtr* eptrs = fbdry.Array(); + if (nullptr != eptrs[0].RelativeVertex(0) && false == eptrs[0].RelativeVertex(0)->Mark() ) + { + fbdry.Append(eptrs[0]); + eptrs = fbdry.Array() + 1; + } + + // save face status and candidate_face + const ON_ComponentStatus fstatus = f->m_status; + const ON_SubDFace* candidate_face = f; + + for (unsigned fei = 0; fei < e_count; ++fei) + { + ON_SubDEdge* e = eptrs[fei].Edge(); + if ( nullptr == e) + return ON_SUBD_RETURN_ERROR(false); + const ON_SubDVertex* ev[2] = { eptrs[fei].RelativeVertex(0), eptrs[fei].RelativeVertex(1) }; + if ( nullptr == ev[0] || nullptr == ev[1] || ev[0] == ev[1]) + return ON_SUBD_RETURN_ERROR(false); + if (0 == fei % 2) + { + if (false == ev[0]->Mark()) + return ON_SUBD_RETURN_ERROR(false); + if (ev[1]->Mark()) + return ON_SUBD_RETURN_ERROR(false); + } + else + { + if (ev[0]->Mark()) + return ON_SUBD_RETURN_ERROR(false); + if (false == ev[1]->Mark()) + return ON_SUBD_RETURN_ERROR(false); + } + } + + // remove face that will be subdivided; + for (unsigned short fei = 0; fei < f->m_edge_count; ++fei) + { + eptrs[fei].Edge()->RemoveFaceFromArray(f); + const_cast(eptrs[fei].RelativeVertex(0))->RemoveFaceFromArray(f); + } + ReturnFace(f); + + ON_SubDVertex* center = AllocateVertex(ON_SubD::VertexTag::Smooth, level0_index, &P.x); + AddVertexToLevel(center); + + radial_edges.SetCount(0); + radial_edges.Reserve(e_count /2); + for (unsigned fei = 0; fei < e_count; fei += 2) + { + ON_SubDEdge* r = AddEdge(ON_SubD::EdgeTag::Smooth, center, ON_SubDSectorType::UnsetSectorWeight, const_cast(eptrs[fei].RelativeVertex(1)), ON_SubDSectorType::UnsetSectorWeight); + radial_edges.Append(r); + } + + ON_SubDEdge* r[2] = { nullptr,radial_edges[e_count / 2 - 1] }; + for (unsigned fei = 0; fei < e_count; fei += 2) + { + r[0] = r[1]; + r[1] = radial_edges[fei / 2]; + const ON_SubDEdgePtr qbdry[4] = { + ON_SubDEdgePtr::Create(r[0],0), + eptrs[(fei + e_count - 1) % e_count], + eptrs[fei], + ON_SubDEdgePtr::Create(r[1],1) + }; + ON_SubDFace* q = AddFace(candidate_face, 4, qbdry); + candidate_face = nullptr; + q->m_status = fstatus; + } + + } + + level0.ClearRuntimeMarks(true, true, false); + level0.ClearBoundingBox(); + level0.ClearEvaluationCache(); + level0.ClearEdgeFlags(); + + return true; } - -unsigned int ON_SubDimple::GlobalSubdivide( - ON_SubD::SubDType subdivision_type, - bool bUseSavedSubdivisionPoints - ) +unsigned int ON_SubDimple::GlobalSubdivide() { - const bool bQuadSubD = (ON_SubD::SubDType::QuadCatmullClark == subdivision_type); - const bool bTriSubD = (ON_SubD::SubDType::TriLoopWarren == subdivision_type); - - if (false == bQuadSubD && false == bTriSubD) - return ON_SUBD_RETURN_ERROR(0); - if (m_levels.UnsignedCount() <= 0) return ON_SUBD_RETURN_ERROR(0U); const unsigned int level0_index = m_levels.UnsignedCount()-1; @@ -6986,57 +8494,48 @@ unsigned int ON_SubDimple::GlobalSubdivide( if ( nullptr == m_levels[level0_index]) return ON_SUBD_RETURN_ERROR(0U); - const ON_SubDLevel& level0 = *m_levels[level0_index]; + ON_SubDLevel& level0 = *m_levels[level0_index]; if (level0.IsEmpty()) return ON_SUBD_RETURN_ERROR(0U); if ( level0.m_edge_count <= 0U ) return ON_SUBD_RETURN_ERROR(0U); + level0.UpdateEdgeSectorCoefficients(true); + const unsigned int level1_index = level0_index+1; - if (0 == level0_index && subdivision_type != level0.m_subdivision_type ) - { - if (false == m_levels[level0_index]->SetSubDType(subdivision_type)) - return ON_SUBD_RETURN_ERROR(0); - } - ON_SubDLevel* level1 = SubDLevel(level1_index,true); if ( nullptr == level1 ) return ON_SUBD_RETURN_ERROR(0); - if (false == level1->SetSubDType(subdivision_type)) - return ON_SUBD_RETURN_ERROR(0); double P[3]; ON_SubDVertex* v; - if (bQuadSubD) + // Add face points + for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) { - // Add face points - for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) + if (false == f0->GetSubdivisionPoint(P)) + continue; + if (nullptr == f0->m_subd_point1) { - if (false == f0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoints, P)) - continue; - if (nullptr == f0->m_subd_point1) - { - const_cast(f0)->m_subd_point1 = v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, P); - AddVertexToLevel(v); - } - else - { - v = const_cast(f0->m_subd_point1); - v->m_P[0] = P[0]; - v->m_P[1] = P[1]; - v->m_P[2] = P[2]; - } + const_cast(f0)->m_subd_point1 = v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, P); + AddVertexToLevel(v); + } + else + { + v = const_cast(f0->m_subd_point1); + v->m_P[0] = P[0]; + v->m_P[1] = P[1]; + v->m_P[2] = P[2]; } } // Add edge points for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge) { - if (false == e0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoints, P)) + if (false == e0->GetSubdivisionPoint(P)) continue; - // (the subdivision point of an edge tagged as ON_SubD::EdgeTag::X is a smooth vertex.) + // (the subdivision point of an edge tagged as ON_SubD::EdgeTag::SmoothX is a smooth vertex.) const ON_SubD::VertexTag vertex_tag = ON_SubD::EdgeTag::Crease == e0->m_edge_tag ? ON_SubD::VertexTag::Crease @@ -7059,7 +8558,7 @@ unsigned int ON_SubDimple::GlobalSubdivide( // Add vertex points for (const ON_SubDVertex* v0 = level0.m_vertex[0]; nullptr != v0; v0 = v0->m_next_vertex) { - if (false == v0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoints, P)) + if (false == v0->GetSubdivisionPoint(P)) continue; if (nullptr == v0->m_subd_point1) { @@ -7073,12 +8572,9 @@ unsigned int ON_SubDimple::GlobalSubdivide( v->m_P[0] = P[0]; v->m_P[1] = P[1]; v->m_P[2] = P[2]; - } - + } } - bool bUpdateEdgeWeights = false; - // subdivide edges for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge) { @@ -7087,25 +8583,8 @@ unsigned int ON_SubDimple::GlobalSubdivide( ON_SubDVertex* end_vertex[2] = { const_cast(e0->m_vertex[0]->m_subd_point1), const_cast(e0->m_vertex[1]->m_subd_point1) }; ON_SubDVertex* mid_vertex = const_cast(e0->m_subd_point1); double w[2] = { e0->m_sector_coefficient[0], e0->m_sector_coefficient[1] }; - if (bTriSubD && ON_SubD::EdgeTag::Smooth == e0->m_edge_tag && !(0.0 == w[0] && 0.0 == w[1])) - { - // If a neighboring face is not a triangle, the weight will need to be recalculated. - for (unsigned int i = 0; i < e0->m_face_count; i++) - { - const ON_SubDFace* f = e0->Face(i); - if (nullptr != f && 3 != f->m_edge_count) - { - bUpdateEdgeWeights = true; - if (!(0.0 == w[0])) - w[0] = ON_SubDSectorType::UnsetSectorWeight; - if (!(0.0 == w[1])) - w[1] = ON_SubDSectorType::UnsetSectorWeight; - break; - } - } - } ON_SubD::EdgeTag edge_tag = e0->m_edge_tag; - if (ON_SubD::EdgeTag::X == edge_tag && 2 == e0->m_face_count) + if (ON_SubD::EdgeTag::SmoothX == edge_tag && 2 == e0->m_face_count) { if ( nullptr != mid_vertex && ON_SubD::VertexTag::Smooth == mid_vertex->m_vertex_tag ) edge_tag = ON_SubD::EdgeTag::Smooth; @@ -7114,71 +8593,36 @@ unsigned int ON_SubDimple::GlobalSubdivide( AddEdge(edge_tag, mid_vertex, 0.0, end_vertex[1], w[1]); } - // subdivide faces - if (bTriSubD) + for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) { - for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) - { - bool bUnsetEdgeWeight = false; - GlobalTriSubdivideFace(f0, bUseSavedSubdivisionPoints, &bUnsetEdgeWeight); - if (bUnsetEdgeWeight) - bUpdateEdgeWeights = true; - } - - if (bUpdateEdgeWeights) - { - for (const ON_SubDEdge* e1 = level1->m_edge[0]; nullptr != e1; e1 = e1->m_next_edge) - { - if (ON_SubD::EdgeTag::Smooth != e1->m_edge_tag) - continue; - for (unsigned int i = 0; i < 2; i++) - { - if (ON_SubDSectorType::UnsetSectorWeight == e1->m_sector_coefficient[i]) - { - const double w = ON_SubDSectorType::Create(subdivision_type, e1, i).SectorWeight(); - const_cast(e1)->m_sector_coefficient[i] = w; - } - } - } - } - } - - if (bQuadSubD) - { - for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) - { - GlobalQuadSubdivideFace(bUseSavedSubdivisionPoints,f0); - } + Internal_GlobalQuadSubdivideFace(f0); } return level1_index; } -unsigned int ON_SubDimple::GlobalQuadSubdivideFace( - bool bUseSavedSubdivisionPoint, +unsigned int ON_SubDimple::Internal_GlobalQuadSubdivideFace( const ON_SubDFace* f0 ) { // This is a private member function. // The caller insures f0 != nullptr. - const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::QuadCatmullClark; - const unsigned int f0_edge_count = f0->m_edge_count; if (f0_edge_count < 3) return 0; const unsigned int parent_face_id = f0->m_id; - const unsigned int zero_face_id = (0 == f0->m_level) ? parent_face_id : f0->m_zero_face_id; + const unsigned int zero_face_id = (0 == f0->SubdivisionLevel()) ? parent_face_id : f0->m_zero_face_id; if (nullptr == f0->m_subd_point1) { // add face centroid double faceC[3]; - if (false == f0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoint, faceC)) + if (false == f0->GetSubdivisionPoint( faceC)) return 0; - f0->SetSavedSubdivisionPoint(subdivision_type,faceC); - unsigned int level1_index = f0->m_level + 1; + f0->SetSavedSubdivisionPoint(faceC); + unsigned int level1_index = f0->SubdivisionLevel() + 1; ON_SubDVertex* v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, faceC ); AddVertexToLevel(v); const_cast(f0)->m_subd_point1 = v; @@ -7201,7 +8645,7 @@ unsigned int ON_SubDimple::GlobalQuadSubdivideFace( unsigned int f1_count = 0; - const double w_2facesector = ON_SubDSectorType::CreaseSectorWeight(subdivision_type, 2); + const double w_2facesector = ON_SubDSectorType::CreaseSectorWeight(2); for (unsigned int i = 0; i < f0_edge_count; i++) { @@ -7274,465 +8718,89 @@ unsigned int ON_SubDimple::GlobalQuadSubdivideFace( return f1_count; } - -static double TriCornerSectorWeight( - ON_SubDEdgePtr e0_ptr, - ON_SubDEdgePtr e1_ptr, - ON_SubD::VertexTag vertex_tag - ) +bool ON_SubD::GlobalSubdivide() { - const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::TriLoopWarren; - - if (ON_SubD::VertexTag::Smooth == vertex_tag) - return 0.0; - - if (ON_SubD::VertexTag::Unset == vertex_tag) - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); - - const ON_SubDEdge* e0 = ON_SUBD_EDGE_POINTER(e0_ptr.m_ptr); - if (nullptr == e0) - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); - ON__INT_PTR e0dir = ON_SUBD_EDGE_DIRECTION(e0_ptr.m_ptr); - - const ON_SubDEdge* e1 = ON_SUBD_EDGE_POINTER(e1_ptr.m_ptr); - if (nullptr == e1) - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); - ON__INT_PTR e1dir = ON_SUBD_EDGE_DIRECTION(e1_ptr.m_ptr); - - // flip direction of e0 so that both edges are leaving the vertex - e0dir = 1 - e0dir; - - if (ON_SubD::EdgeTag::Crease == e0->m_edge_tag && ON_SubD::EdgeTag::Crease == e1->m_edge_tag) - { - // The radial edge we are about to add has two faces in its sector between the - // creased edges e0 and e1. - unsigned int sector_face_count = 2; - if (ON_SubD::VertexTag::Crease == vertex_tag) - { - return ON_SubDSectorType::CreaseSectorWeight(subdivision_type, sector_face_count); - } - - if (ON_SubD::VertexTag::Corner == vertex_tag) - { - const double corner_sector_angle_radians - = ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(ON_SubDEdgePtr::Create(e0, e0dir), ON_SubDEdgePtr::Create(e1, e1dir)); - return ON_SubDSectorType::CreateCornerSectorType(subdivision_type, sector_face_count, corner_sector_angle_radians).SectorWeight(); - } - - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); - } - - if (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag) - { - // The weight calculation requires all edges in the sector exist - // and has to be delayed until the subdivision topology is complete. - return ON_SubDSectorType::UnsetSectorWeight; - } - - double w0 - = (ON_SubD::EdgeTag::Smooth == e0->m_edge_tag) - ? e0->m_sector_coefficient[e0dir] - : ON_SubDSectorType::UnsetSectorWeight; - - double w1 - = (ON_SubD::EdgeTag::Smooth == e1->m_edge_tag) - ? e1->m_sector_coefficient[e1dir] - : ON_SubDSectorType::UnsetSectorWeight; - - double w = (w0 == w1) ? w0 : ((ON_SubDSectorType::UnsetSectorWeight != w0) ? w0 : w1); - if (w == w && ON_SubDSectorType::UnsetSectorWeight != w) - return w; - - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + return GlobalSubdivide(1U); } -unsigned int ON_SubDimple::GlobalTriSubdivideFace( - const ON_SubDFace* f0, - bool bUseSavedSubdivisionPoint, - bool* bUnsetEdgeWeight - ) -{ - const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::TriLoopWarren; - - // This is a private member function. - // The caller insures f0 != nullptr and bUnsetEdgeWeight != nullptr. - - *bUnsetEdgeWeight = false; - - const unsigned int f0_edge_count = f0->m_edge_count; - if (f0_edge_count < 3) - return 0; - - const unsigned int parent_face_id = f0->m_id; - const unsigned int zero_face_id = (0 == f0->m_level) ? parent_face_id : f0->m_zero_face_id; - - - ON_SubDEdge* E0[3]; - ON__UINT_PTR E0dir[3]; - ON_SubDEdge* E1[9]; - ON_SubDEdgePtr f1edges[3]; - ON__UINT_PTR e_ptr; - ON_SubDFace* f1; - - e_ptr = f0->EdgePtr(f0_edge_count - 1).m_ptr; - E0[1] = ON_SUBD_EDGE_POINTER(e_ptr); - E0dir[1] = ON_SUBD_EDGE_DIRECTION(e_ptr); - E1[2] = nullptr; - - unsigned int f1_count = 0; - - if (3 == f0_edge_count) - { - unsigned int j = 0; - for (unsigned int i = 0; i < 3; i++) - { - e_ptr = f0->m_edge4[i].m_ptr; - E0[i] = ON_SUBD_EDGE_POINTER(e_ptr); - if (nullptr == E0[i] || nullptr == E0[i]->m_subd_point1) - break; - E0dir[i] = ON_SUBD_EDGE_DIRECTION(e_ptr); - - e_ptr = E0[i]->m_subd_point1->m_edges[E0dir[i]].m_ptr; - E1[j] = ON_SUBD_EDGE_POINTER(e_ptr); - if (nullptr == E1[j]) - break; - j++; - - e_ptr = E0[i]->m_subd_point1->m_edges[1 - E0dir[i]].m_ptr; - E1[j] = ON_SUBD_EDGE_POINTER(e_ptr); - if (nullptr == E1[j]) - break; - j++; - } - if (6 != j) - return 0; - - // The value of E0[0]->m_subd_point1->m_vertex_tag should be either - // ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the - // case when it's value is "crease", the resulting edge end weight - // will be 0.5 because the edge has three adjacent faces and "theta" - // will be pi/3. - // The resulting tri edge weight is 0.5 = 1/3 + 1/3*cos(pi/3). - const double w_3facesector = ON_SubDSectorType::CreaseSectorWeight(subdivision_type, 3); - - double w0 = (ON_SubD::VertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0; - double w1 = (ON_SubD::VertexTag::Crease == E0[2]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0; - E1[6] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast(E0[0]->m_subd_point1), w0, const_cast(E0[2]->m_subd_point1), w1); - w0 = w1; - w1 = (ON_SubD::VertexTag::Crease == E0[1]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0; - E1[7] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast(E0[2]->m_subd_point1), w0, const_cast(E0[1]->m_subd_point1), w1); - w0 = w1; - w1 = (ON_SubD::VertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0; - E1[8] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast(E0[1]->m_subd_point1), w0, const_cast(E0[0]->m_subd_point1), w1); - - f1edges[0] = ON_SubDEdgePtr::Create(E1[0], E0dir[0]); - f1edges[1] = ON_SubDEdgePtr::Create(E1[6], 0); - f1edges[2] = ON_SubDEdgePtr::Create(E1[5], E0dir[2]); - f1 = AddFace(3, f1edges); - if (nullptr != f1) - { - f1->m_zero_face_id = zero_face_id; - f1->m_parent_face_id = parent_face_id; - f1_count++; - } - - f1edges[0] = ON_SubDEdgePtr::Create(E1[4], E0dir[2]); - f1edges[1] = ON_SubDEdgePtr::Create(E1[7], 0); - f1edges[2] = ON_SubDEdgePtr::Create(E1[3], E0dir[1]); - f1 = AddFace(3, f1edges); - if (nullptr != f1) - { - f1->m_zero_face_id = zero_face_id; - f1->m_parent_face_id = parent_face_id; - f1_count++; - } - - f1edges[0] = ON_SubDEdgePtr::Create(E1[2], E0dir[1]); - f1edges[1] = ON_SubDEdgePtr::Create(E1[8], 0); - f1edges[2] = ON_SubDEdgePtr::Create(E1[1], E0dir[0]); - f1 = AddFace(3, f1edges); - if (nullptr != f1) - { - f1->m_zero_face_id = zero_face_id; - f1->m_parent_face_id = parent_face_id; - f1_count++; - } - - f1edges[0] = ON_SubDEdgePtr::Create(E1[6], 1); - f1edges[1] = ON_SubDEdgePtr::Create(E1[8], 1); - f1edges[2] = ON_SubDEdgePtr::Create(E1[7], 1); - f1 = AddFace(3, f1edges); - if (nullptr != f1) - { - f1->m_zero_face_id = zero_face_id; - f1->m_parent_face_id = parent_face_id; - f1_count++; - } - - // return number of new faces - return f1_count; - } - - if (f0_edge_count < 2) - return 0; - - // This code builds triangles from the subdivided edges to the face's centroid. - // - // In general, there is no single robust way to deal with polygons with lots of sides. - // In particular, the code below is not approprite for non-convex polygons and is - // of limited use for polygons that are not close to regular. - // - // The basic issue is that if a SubD control net makes it to this point, - // higher level code and, ultimately, the user, must be responsible - // for creating a "reasonable" input control net. - // - // This code is here so something happens in trianglular subd when - // a non-triangle is subdivided. - // - // This code is "reasonable" when a user wants to apply a triangular - // subdivision to a "nice" quad mesh SubD control polygon. - - ON_SubDVertex* center_vertex = const_cast(f0->m_subd_point1); - if (nullptr == center_vertex) - { - // add face centroid - double faceC[3]; - if (false == f0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoint, faceC)) - return 0; - f0->SetSavedSubdivisionPoint(subdivision_type, faceC); - unsigned int level1_index = f0->m_level + 1; - center_vertex = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, faceC); - if (nullptr == center_vertex) - return 0; - AddVertexToLevel(center_vertex); - const_cast(f0)->m_subd_point1 = center_vertex; - } - - E1[4] = nullptr; // radial edge from end of previous vertex to this vertex - E1[8] = nullptr; // used to save pointer to first radial edge. - const ON_SubDVertex* edge1_vertex; - ON_SubDVertex* right_vertex = nullptr; - for (unsigned int i = 0; i < f0_edge_count; i++) - { - // This case is a special case and it will be called on control - // polygons created by inexperienced programmers, - // It is important that all information be validated. - // When invalid information is detected, that edge/triangle - // is simply skipped and there will be a "smooth sided hole" - // in the resulting mesh. - // - ON_SubDVertex* left_vertex = right_vertex; - right_vertex = nullptr; - - // E1[4] = previous "right" radial - // E1[2] = current "left" radial from left_vertex to center_vertex - E1[2] = E1[4]; - E1[4] = nullptr; - - const ON_SubDEdgePtr ei_ptr = f0->EdgePtr(i); - - E0[0] = ON_SUBD_EDGE_POINTER(ei_ptr.m_ptr); - E0dir[0] = ON_SUBD_EDGE_DIRECTION(ei_ptr.m_ptr); - if (nullptr == E0[0]) - continue; - - ON_SubDVertex* mid_vertex = const_cast(E0[0]->m_subd_point1); - if (nullptr == mid_vertex || mid_vertex->m_edge_count < 2 || nullptr == mid_vertex->m_edges) - continue; - - // E1[0] = "left" side of E0[0] - e_ptr = mid_vertex->m_edges[E0dir[0]].m_ptr; - E1[0] = ON_SUBD_EDGE_POINTER(e_ptr); - if (nullptr == E1[0]) - continue; - - if (mid_vertex != E1[0]->m_vertex[1 - E0dir[0]]) - continue; - - // E1[1] = "right" side of E0[0] - e_ptr = mid_vertex->m_edges[1 - E0dir[0]].m_ptr; - E1[1] = ON_SUBD_EDGE_POINTER(e_ptr); - if (nullptr == E1[1]) - continue; - - if (mid_vertex != E1[1]->m_vertex[E0dir[0]]) - continue; - - // If E0[0] is a crease: - // Then mid_vertex should be tagged as a crease vertex and both - // E1[0] and E1[1] should also be tagged as creased edges. - // The new edge from mid_vertex to center_vertex - // the "sector_face_count" will be 2 and the "theta" used to calculate the - // edge weight will be pi/2. - // mid_weight = 1/3 + 1/3*cos(pi/2) = 1/3 ( cos(pi/2) = zero ) - bool bValidTags; - if (ON_SubD::EdgeTag::Smooth == E0[0]->m_edge_tag || ON_SubD::EdgeTag::X == E0[0]->m_edge_tag) - { - bValidTags - = ON_SubD::VertexTag::Smooth == mid_vertex->m_vertex_tag - && ON_SubD::EdgeTag::Smooth == E1[0]->m_edge_tag - && ON_SubD::EdgeTag::Smooth == E1[1]->m_edge_tag; - } - else if (ON_SubD::EdgeTag::Crease == E0[0]->m_edge_tag) - { - bValidTags - = ON_SubD::VertexTag::Crease == mid_vertex->m_vertex_tag - && ON_SubD::EdgeTag::Crease == E1[0]->m_edge_tag - && ON_SubD::EdgeTag::Crease == E1[1]->m_edge_tag; - } - else - bValidTags = false; - - if (false == bValidTags) - { - ON_SubDIncrementErrorCount(); - continue; - } - - const double mid_weight - = (ON_SubD::EdgeTag::Crease == E0[0]->m_edge_tag) - ? ON_SubDSectorType::CreaseSectorWeight(subdivision_type, 2) - : ON_SubDSectorType::IgnoredSectorWeight; - - // E1[2] = radial from "start" of E1[0] to face centroid. - edge1_vertex = E1[0]->m_vertex[E0dir[0]]; - if (nullptr == left_vertex || nullptr == E1[2] || left_vertex != E1[2]->m_vertex[0] || left_vertex != edge1_vertex) - { - // when input is valid, this clause is executed when i = 0. - left_vertex = const_cast(edge1_vertex); - if (nullptr != left_vertex) - { - const double left_w = TriCornerSectorWeight(f0->EdgePtr((i + (f0_edge_count-1)) % f0_edge_count), ei_ptr, left_vertex->m_vertex_tag); - if (ON_SubDSectorType::UnsetSectorWeight == left_w) - *bUnsetEdgeWeight = true; - E1[2] = AddEdge(ON_SubD::EdgeTag::Smooth, left_vertex, left_w, center_vertex, 0.0); - if (0 == i) - { - // E1[8] = first radial edge - E1[8] = E1[2]; - } - } - else - E1[2] = nullptr; - } - - // E1[3] = radial from E0[0]->m_subd_point1 to face centroid. - E1[3] = AddEdge(ON_SubD::EdgeTag::Smooth, mid_vertex, mid_weight, center_vertex, 0.0); - if (nullptr == E1[3]) - continue; - - // E1[4] = radial from "end" of E1[1] to face centroid. - if (i + 1 < f0_edge_count) - { - right_vertex = const_cast(E1[1]->m_vertex[1 - E0dir[0]]); - if (nullptr != right_vertex) - { - const double right_w = TriCornerSectorWeight(ei_ptr, f0->EdgePtr((i + 1) % f0_edge_count), right_vertex->m_vertex_tag); - if (ON_SubDSectorType::UnsetSectorWeight == right_w) - *bUnsetEdgeWeight = true; - E1[4] = AddEdge(ON_SubD::EdgeTag::Smooth, right_vertex, right_w, center_vertex, 0.0); - } - else - E1[4] = nullptr; - } - else - { - // E1[8] = first radial edge - E1[4] = E1[8]; - } - - if (nullptr != E1[0] && nullptr != E1[2]) - { - // "left" triangle with "base" E1[0] and apex at face centroid - // the "base" ON_SubDEdgePtr::Create(E1[0], E0dir[0]) runs from V1[0] to E0[0]->m_subd_point1. - f1edges[0] = ON_SubDEdgePtr::Create(E1[0], E0dir[0]); // V1[0] to E0[0]->m_subd_point1 - f1edges[1] = ON_SubDEdgePtr::Create(E1[3], 0); // E0[0]->m_subd_point1 to center_vertex - f1edges[2] = ON_SubDEdgePtr::Create(E1[2], 1); // center_vertex to V1[0] - f1 = AddFace(3, f1edges); - if (nullptr != f1) - { - f1->m_zero_face_id = zero_face_id; - f1->m_parent_face_id = parent_face_id; - f1_count++; - } - } - - if (nullptr != E1[1] && nullptr != E1[4] ) - { - // "right" triangle with "base" E1[1] and apex at face centroid - // The "base" ON_SubDEdgePtr::Create(E1[1], E0dir[0]) runs from E0[0]->m_subd_point1 to V1[1]. - f1edges[0] = ON_SubDEdgePtr::Create(E1[1], E0dir[0]); // E0[0]->m_subd_point1 to V1[1] - f1edges[1] = ON_SubDEdgePtr::Create(E1[4], 0); // V1[1] to center_vertex - f1edges[2] = ON_SubDEdgePtr::Create(E1[3], 1); // center_vertex to E0[0]->m_subd_point1 - f1 = AddFace(3, f1edges); - if (nullptr != f1) - { - f1->m_zero_face_id = zero_face_id; - f1->m_parent_face_id = parent_face_id; - f1_count++; - } - } - } - - // return number of faces added - return f1_count; -} - -bool ON_SubD::Subdivide( - ON_SubD::SubDType subd_type, - unsigned int level_index, +bool ON_SubD::GlobalSubdivide( unsigned int count - ) +) { ON_SubDimple* subdimple = SubDimple(false); if (nullptr == subdimple) return ON_SUBD_RETURN_ERROR(false); - return subdimple->Subdivide(subd_type,level_index,count); + return subdimple->GlobalSubdivide(count); } +bool ON_SubD::LocalSubdivide( + const ON_SimpleArray& face_list +) +{ + const int count = face_list.Count(); + ON_SimpleArray< const ON_SubDFace* > ptr_list(count); + for (int i = 0; i < count; ++i) + { + const ON_COMPONENT_INDEX ci = face_list[i]; + if (ON_COMPONENT_INDEX::TYPE::subd_face != ci.m_type) + continue; + if (ci.m_index <= 0) + continue; + const ON_SubDFace* f = this->FaceFromId(ci.m_index); + if (nullptr == f) + continue; + ptr_list.Append(f); + } + const bool rc = LocalSubdivide(ptr_list); + UpdateAllTagsAndSectorCoefficients(true); + return rc; +} -bool ON_SubDimple::Subdivide( - ON_SubD::SubDType subd_type, - unsigned int level_index, +bool ON_SubD::LocalSubdivide(const ON_SimpleArray< const ON_SubDFace* >& face_list) +{ + return LocalSubdivide(face_list.Array(), face_list.UnsignedCount()); +} + +bool ON_SubD::LocalSubdivide( ON_SubDFace const*const* face_list, size_t face_count) +{ + ON_SubDimple* subdimple = SubDimple(false); + if (nullptr == subdimple) + return ON_SUBD_RETURN_ERROR(false); + return subdimple->LocalSubdivide(face_list, face_count); +} + +bool ON_SubDimple::GlobalSubdivide( unsigned int count ) { - if (level_index >= m_levels.UnsignedCount() || nullptr == m_levels[level_index]) + if (m_levels.UnsignedCount() < 1) + return ON_SUBD_RETURN_ERROR(false); + if (nullptr == m_active_level) + { + m_active_level = m_levels[m_levels.UnsignedCount() - 1]; + if (nullptr == m_active_level) + return ON_SUBD_RETURN_ERROR(false); + } + + const unsigned level0_index = m_active_level->m_level_index; + if (level0_index >= m_levels.UnsignedCount() || nullptr == m_levels[level0_index]) return ON_SUBD_RETURN_ERROR(false); if (count <= 0) return ON_SUBD_RETURN_ERROR(false); - if (level_index+count > ON_SubD::maximum_subd_level) + if (level0_index + count > ON_SubD::maximum_subd_level) return ON_SUBD_RETURN_ERROR(false); - - if (ON_SubD::SubDType::Unset == subd_type) - { - subd_type = m_levels[level_index]->m_subdivision_type; - if ( ON_SubD::SubDType::Unset == subd_type ) - subd_type = ON_SubD::DefaultSubDType(); - } - - if (false == ON_SubD::IsQuadOrTriSubDType(subd_type)) + ClearHigherSubdivisionLevels(level0_index + 1); + if (level0_index + 1 != m_levels.UnsignedCount() ) return ON_SUBD_RETURN_ERROR(false); - if (subd_type != m_levels[level_index]->m_subdivision_type) - { - if (false == m_levels[level_index]->SetSubDType(subd_type)) - return ON_SUBD_RETURN_ERROR(false); - } - - ClearHigherSubdivisionLevels(level_index + 1); - if ( level_index + 1 != m_levels.UnsignedCount() ) - return ON_SUBD_RETURN_ERROR(false); - - m_active_level = m_levels[level_index]; + m_active_level = m_levels[level0_index]; - const bool bUseSavedSubdivisionPoints = true; - for (unsigned int i = level_index+1; i <= level_index+count; i++) + for (unsigned int i = level0_index +1; i <= level0_index +count; i++) { - unsigned int rc = GlobalSubdivide(subd_type,bUseSavedSubdivisionPoints); + unsigned int rc = GlobalSubdivide(); if (i != rc) return ON_SUBD_RETURN_ERROR(false); m_active_level = m_levels[i]; @@ -7816,7 +8884,7 @@ ON_SubDEdgePtr ON_SubDimple::MergeEdges( { e[0]->m_edge_tag = (bTagged[0] && bTagged[1]) - ? ON_SubD::EdgeTag::X + ? ON_SubD::EdgeTag::SmoothX : ON_SubD::EdgeTag::Smooth; if ( false == bTagged[0]) e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; @@ -7849,14 +8917,15 @@ ON_SubDEdgePtr ON_SubD::MergeEdges( } static bool EdgesAreMergableTest( - ON_SubDEdge* e[2], - ON__UINT_PTR edir[2], + const ON_SubDEdgePtr eptr[2], bool bTestColinearity, double distance_tolerance, double maximum_aspect, double sin_angle_tolerance ) { + const ON_SubDEdge* e[2] = { ON_SUBD_EDGE_POINTER(eptr[0].m_ptr), ON_SUBD_EDGE_POINTER(eptr[1].m_ptr) }; + ON__UINT_PTR edir[2] = { ON_SUBD_EDGE_DIRECTION(eptr[0].m_ptr), ON_SUBD_EDGE_DIRECTION(eptr[1].m_ptr) }; if ( nullptr == e[0] || nullptr == e[1] || e[0] == e[1] @@ -7870,8 +8939,6 @@ static bool EdgesAreMergableTest( if ( nullptr == e[1]->m_vertex[0] || nullptr == e[1]->m_vertex[1] ) { - // Setting e[1] = nullptr used in edge merging code and doesn't hurt other uses of this static function - e[1] = nullptr; return false; } @@ -7989,12 +9056,42 @@ bool ON_SubD::EdgesCanBeMerged( ON_SubDEdgePtr eptr1 ) { - ON_SubDEdge* e[2] = { ON_SUBD_EDGE_POINTER(eptr0.m_ptr), ON_SUBD_EDGE_POINTER(eptr1.m_ptr) }; - ON__UINT_PTR edir[2] = { ON_SUBD_EDGE_DIRECTION(eptr0.m_ptr), ON_SUBD_EDGE_DIRECTION(eptr1.m_ptr) }; - return EdgesAreMergableTest(e,edir,false,ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN); + ON_SubDEdgePtr eptr[2] = { eptr0,eptr1 }; + return EdgesAreMergableTest(eptr,false,ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN); +} + +static bool Internal_EdgesPassTypeFilter( + const ON_SubDEdgePtr eptr[2], + bool bMergeBoundaryEdges, + bool bMergeInteriorCreaseEdges, + bool bMergeInteriorSmoothEdges +) +{ + for (unsigned i = 0; i < 2; ++i) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr[i].m_ptr); + if (nullptr == e || e->m_face_count < 1) + return false; + if (1 == e->m_face_count) + { + if (false == bMergeBoundaryEdges) + return false; + } + else if (e->m_face_count >= 2) + { + if (false == (e->IsSmooth() ? bMergeInteriorSmoothEdges : bMergeInteriorCreaseEdges)) + return false; + } + else + return false; + }; + return true; } unsigned int ON_SubDimple::MergeColinearEdges( + bool bMergeBoundaryEdges, + bool bMergeInteriorCreaseEdges, + bool bMergeInteriorSmoothEdges, double distance_tolerance, double maximum_aspect, double sin_angle_tolerance @@ -8010,44 +9107,59 @@ unsigned int ON_SubDimple::MergeColinearEdges( unsigned int edge_count = f->m_edge_count; if (edge_count < 3) continue; - ON_SubDEdge* e[2] = { 0 }; - ON__UINT_PTR edir[2] = { 0 }; - unsigned int i0 = ON_UNSET_UINT_INDEX; - for (unsigned int i = 0; i <= edge_count; i++) + + // First - find a pair of edges that should not be merged. + ON_SubDEdgePtr eptr[2] = { ON_SubDEdgePtr::Null, f->EdgePtr(edge_count - 1) }; + unsigned int fei0 = 0; + while (fei0 < edge_count) { - e[0] = e[1]; - edir[0] = edir[1]; - ON__UINT_PTR eptr = f->EdgePtr(i%edge_count).m_ptr; - e[1] = ON_SUBD_EDGE_POINTER(eptr); - edir[1] = ON_SUBD_EDGE_DIRECTION(eptr); - if (0 == i) + eptr[0] = eptr[1]; + eptr[1] = f->EdgePtr(fei0); + if (false == EdgesAreMergableTest(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance)) + break; + if (false == Internal_EdgesPassTypeFilter(eptr, bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges)) + break; + ++fei0; + } + if (fei0 >= edge_count) + { + // face is degenerate or corrupt. + continue; + } + if (0 != fei0) + { + if (false == (const_cast(f)->RotateEdgeArray(fei0))) + { + // face is degenerate or corrupt. continue; - - if (EdgesAreMergableTest(e, edir, true, distance_tolerance, maximum_aspect, sin_angle_tolerance)) - { - if (ON_UNSET_UINT_INDEX == i0) - { - i0 = i; - } - if (i < edge_count) - continue; } + } - if (ON_UNSET_UINT_INDEX != i0) + // At this point, we know the last edge and the first edge should not me merged. + eptr[1] = f->EdgePtr(0U); + unsigned int fei = 1; + while ( fei < edge_count) + { + eptr[0] = eptr[1]; + eptr[1] = f->EdgePtr(fei); + if ( + EdgesAreMergableTest(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance) + && Internal_EdgesPassTypeFilter(eptr, bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges) + ) { - const ON_SubDEdgePtr e0 = f->EdgePtr(i0 - 1); - while(i0 < i) + // merge edges f->Edge(fei-1) and f->Edge(fei) into f->Edge(fei-1). + if (eptr[0].m_ptr != MergeEdges(eptr[0], eptr[1]).m_ptr) { - if (e0.m_ptr != MergeEdges(e0, f->EdgePtr(i0)).m_ptr) - { - ON_ERROR("Bug in edge merging."); - break; - } - removed_edge_count++; - i--; - edge_count--; + ON_SUBD_ERROR("Bug in edge merging."); + break; } - i0 = ON_UNSET_UINT_INDEX; + ++removed_edge_count; + --edge_count; + eptr[1] = eptr[0]; + } + else + { + ++fei; } } } @@ -8056,18 +9168,19 @@ unsigned int ON_SubDimple::MergeColinearEdges( } unsigned int ON_SubD::MergeColinearEdges( + bool bMergeBoundaryEdges, + bool bMergeInteriorCreaseEdges, + bool bMergeInteriorSmoothEdges, double distance_tolerance, double maximum_aspect, double sin_angle_tolerance ) { ON_SubDimple* subdimple = SubDimple(false); - return (nullptr != subdimple) ? subdimple->MergeColinearEdges(distance_tolerance, maximum_aspect, sin_angle_tolerance) : 0; -} - -ON_SubD::SubDType ON_SubD::ActiveLevelSubDType() const -{ - return ActiveLevel().m_subdivision_type; + return + (nullptr != subdimple) + ? subdimple->MergeColinearEdges( bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges, distance_tolerance, maximum_aspect, sin_angle_tolerance) + : 0; } unsigned int ON_SubD::LevelCount() const @@ -8096,7 +9209,7 @@ bool ON_SubD::IsNotEmpty() const // // Element (Vertex, Edge, Face) access // -ON_COMPONENT_INDEX ON_SubDComponentPtr::ComponentIndex() const +const ON_COMPONENT_INDEX ON_SubDComponentPtr::ComponentIndex() const { switch (ComponentType()) { @@ -8129,7 +9242,7 @@ ON_COMPONENT_INDEX ON_SubDComponentPtr::ComponentIndex() const return ON_SUBD_RETURN_ERROR(ON_COMPONENT_INDEX::UnsetComponentIndex); } -ON_SubDComponentPtr ON_SubD::ComponentPtrFromComponentIndex( +const ON_SubDComponentPtr ON_SubD::ComponentPtrFromComponentIndex( ON_COMPONENT_INDEX component_index ) const { @@ -8170,6 +9283,11 @@ const ON_SubDVertex* ON_SubD::FirstVertex() const return ActiveLevel().m_vertex[0]; } +const ON_SubDVertex* ON_SubD::LastVertex() const +{ + return ActiveLevel().m_vertex[1]; +} + ON_SubDVertexIterator ON_SubD::VertexIterator() const { return ON_SubDVertexIterator(*this); @@ -8201,12 +9319,12 @@ const class ON_SubDVertex* ON_SubD::VertexFromId( return nullptr; } -ON_COMPONENT_INDEX ON_SubDVertex::ComponentIndex() const +const ON_COMPONENT_INDEX ON_SubDVertex::ComponentIndex() const { return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_vertex,m_id); } -ON_SubDComponentPtr ON_SubDVertex::ComponentPtr() const +const ON_SubDComponentPtr ON_SubDVertex::ComponentPtr() const { return ON_SubDComponentPtr::Create(this); } @@ -8226,6 +9344,11 @@ const ON_SubDEdge* ON_SubD::FirstEdge() const return ActiveLevel().m_edge[0]; } +const ON_SubDEdge* ON_SubD::LastEdge() const +{ + return ActiveLevel().m_edge[1]; +} + ON_SubDEdgeIterator ON_SubD::EdgeIterator() const { return ON_SubDEdgeIterator(*this); @@ -8257,12 +9380,12 @@ const class ON_SubDEdge* ON_SubD::EdgeFromId( return nullptr; } -ON_COMPONENT_INDEX ON_SubDEdge::ComponentIndex() const +const ON_COMPONENT_INDEX ON_SubDEdge::ComponentIndex() const { return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_edge,m_id); } -ON_SubDComponentPtr ON_SubDEdge::ComponentPtr() const +const ON_SubDComponentPtr ON_SubDEdge::ComponentPtr() const { return ON_SubDComponentPtr::Create(this); } @@ -8282,6 +9405,11 @@ const ON_SubDFace* ON_SubD::FirstFace() const return ActiveLevel().m_face[0]; } +const ON_SubDFace* ON_SubD::LastFace() const +{ + return ActiveLevel().m_face[1]; +} + ON_SubDFaceIterator ON_SubD::FaceIterator() const { return ON_SubDFaceIterator(*this); @@ -8315,12 +9443,12 @@ const class ON_SubDFace* ON_SubD::FaceFromId( } -ON_COMPONENT_INDEX ON_SubDFace::ComponentIndex() const +const ON_COMPONENT_INDEX ON_SubDFace::ComponentIndex() const { return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_face,m_id); } -ON_SubDComponentPtr ON_SubDFace::ComponentPtr() const +const ON_SubDComponentPtr ON_SubDFace::ComponentPtr() const { return ON_SubDComponentPtr::Create(this); } @@ -8330,9 +9458,7 @@ ON_SubDComponentPtr ON_SubDFace::ComponentPtr() const // ON_SubD properties // -bool ON_SubD::IsOriented( - unsigned int level_index - ) const +bool ON_SubD::IsOriented() const { for (const ON_SubDEdge* edge = FirstEdge(); nullptr != edge; edge = edge->m_next_edge) { @@ -8347,12 +9473,16 @@ bool ON_SubD::IsOriented( } // reverses the orientation of all facets -bool ON_SubD::ReverseOrientation( - unsigned int level_index - ) const +bool ON_SubD::ReverseOrientation() const { + // Limit point normals and limit surface mesh fragments will need to be recalculated. + // DestroyRuntimeCache() will clear all this information. + const_cast(this)->DestroyRuntimeCache(true); + for (const ON_SubDFace* face = FirstFace(); nullptr != face; face = face->m_next_face) + { const_cast(face)->ReverseEdgeList(); + } return true; } @@ -8451,9 +9581,7 @@ static unsigned int OrientFaceNeighbors( return orient_count; } -bool ON_SubD::Orient( - unsigned int level_index - ) const +bool ON_SubD::Orient() const { const ON_SubDFace* first_face = FirstFace(); if ( nullptr == first_face || nullptr == first_face->m_next_face) @@ -8534,21 +9662,88 @@ bool ON_SubD::Orient( return (connected_region_count > 0 && orient_count > 0); } -const ON_SubDVertex* ON_SubD::TriangulateFace( - ON_SubDFace* face - ) +const ON_SubDVertex * ON_SubD::ReplaceFaceWithTriangleFan(ON_SubDFace * face, ON_3dPoint fan_center_point, bool bMarkFaces) { - // TODO add implementation - return ON_SUBD_RETURN_ERROR(nullptr); + const unsigned edge_count = face->m_edge_count; + if (edge_count < 3) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDimple* subdimple = SubDimple(false); + if (nullptr == subdimple) + return ON_SUBD_RETURN_ERROR(nullptr); + + // validate and get centroid (which may not be needed). + ON_3dPoint P = ON_3dPoint::Origin; + ON_SimpleArray edges(edge_count); + const ON_SubDEdgePtr* eptr = face->m_edge4; + for (unsigned i = 0; i < edge_count; ++i, ++eptr) + { + if (4U == i) + { + eptr = face->m_edgex; + if (nullptr == eptr) + return ON_SUBD_RETURN_ERROR(nullptr); + } + const ON_SubDVertex* v = eptr->RelativeVertex(0); + if (nullptr == v) + return ON_SUBD_RETURN_ERROR(nullptr); + P += v->ControlNetPoint(); + edges.Append(*eptr); + } + + if (fan_center_point.IsValid()) + P = fan_center_point; + else + P /= ((double)edge_count); + + ON_SubDVertex* v0 = AddVertex(ON_SubD::VertexTag::Smooth, P); + if (nullptr == v0) + return ON_SUBD_RETURN_ERROR(nullptr); + + for (unsigned i = 0; i < edge_count; ++i, ++eptr) + { + if (nullptr == AddEdge(ON_SubD::EdgeTag::Smooth, v0, const_cast(edges[i].RelativeVertex(0)))) + { + ON_SubDComponentPtr cptr = ON_SubDComponentPtr::Create(v0); + DeleteComponents(&cptr, 1, false); + return ON_SUBD_RETURN_ERROR(nullptr); + } + } + + for (unsigned i = 0; i < edge_count; ++i, ++eptr) + { + if (i < 4) + face->m_edge4[i] = ON_SubDEdgePtr::Null; + else + face->m_edgex[i - 4] = ON_SubDEdgePtr::Null;; + ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(edges[i].m_ptr); + e->RemoveFaceFromArray(face); + const_cast(edges[i].RelativeVertex(0))->RemoveFaceFromArray(face); + } + + subdimple->ReturnFace(face); + for (unsigned i = 0; i < edge_count; ++i, ++eptr) + { + AddTriangleFace(v0->m_edges[i], edges[i], v0->m_edges[(i + 1) % edge_count].Reversed()); + } + + for (unsigned i = 0; i < edge_count; ++i, ++eptr) + { + ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(edges[i].m_ptr); + e->EdgeModifiedNofification(); + e->UpdateEdgeSectorCoefficientsForExperts(false); + } + + for (unsigned i = 0; i < edge_count; ++i, ++eptr) + ON_SUBD_EDGE_POINTER(edges[i].m_ptr)->UpdateEdgeSectorCoefficientsForExperts(false); + + for (unsigned i = 0; i < edge_count; ++i, ++eptr) + ON_SUBD_EDGE_POINTER(v0->m_edges[i].m_ptr)->UpdateEdgeSectorCoefficientsForExperts(false); + + return v0; } -const ON_SubDFace* ON_SubD::MergeFaces( - ON_SubDEdge* edge - ) -{ - // TODO add implementation - return ON_SUBD_RETURN_ERROR(nullptr); -} + const ON_SubDEdge* ON_SubDimple::SplitEdge( ON_SubDEdge* edge, @@ -8564,7 +9759,7 @@ const ON_SubDEdge* ON_SubDimple::SplitEdge( if (nullptr == edge->m_vertex[1]->m_edges || edge->m_vertex[1]->m_edge_count <= 0 || edge->m_vertex[1]->m_edge_capacity < edge->m_vertex[1]->m_edge_count ) return ON_SUBD_RETURN_ERROR(nullptr); - if (ON_3dPoint::UnsetPoint == vertex_location) + if ( vertex_location.IsUnsetOrNan() ) { ON_Line L; L.from = ON_3dPoint(edge->m_vertex[0]->m_P); @@ -8587,7 +9782,7 @@ const ON_SubDEdge* ON_SubDimple::SplitEdge( case ON_SubD::EdgeTag::Crease: vertex_tag = ON_SubD::VertexTag::Crease; break; - case ON_SubD::EdgeTag::X: + case ON_SubD::EdgeTag::SmoothX: vertex_tag = ON_SubD::VertexTag::Smooth; edge_tag = ON_SubD::EdgeTag::Smooth; break; @@ -8602,11 +9797,11 @@ const ON_SubDEdge* ON_SubDimple::SplitEdge( ON_SubDEdge* new_edge = nullptr; for (;;) { - new_vertex = AllocateVertex(vertex_tag, edge->m_level, static_cast(vertex_location), 2, edge->m_face_count); + new_vertex = AllocateVertex(vertex_tag, edge->SubdivisionLevel(), static_cast(vertex_location), 2, edge->m_face_count); if (nullptr == new_vertex) break; - new_edge = AllocateEdge(edge_tag, edge->m_level, edge->m_face_count); + new_edge = AllocateEdge(edge_tag, edge->SubdivisionLevel(), edge->m_face_count); if (nullptr == new_edge) break; @@ -8736,17 +9931,23 @@ const ON_SubDEdge* ON_SubDimple::SplitFace( unsigned int fvi1 ) { - if ( nullptr == face || face->m_edge_count < 4) - return ON_SUBD_RETURN_ERROR(nullptr); - - if (face->m_level >= m_levels.UnsignedCount()) - return ON_SUBD_RETURN_ERROR(nullptr); - ON_SubDLevel* level = m_levels[face->m_level]; - if ( nullptr == level) + if ( nullptr == face || fvi0 == fvi1) return ON_SUBD_RETURN_ERROR(nullptr); const unsigned int edge_count = face->m_edge_count; - if ( edge_count < 4 || fvi0 == fvi1 || fvi0 >= edge_count || fvi1 >= edge_count || (edge_count > 4 && nullptr == face->m_edgex) ) + if (edge_count < 4 || (edge_count > 4 && nullptr == face->m_edgex)) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (fvi0 >= edge_count || fvi1 >= edge_count) + return ON_SUBD_RETURN_ERROR(nullptr); + + if ((fvi0 + 1) % edge_count == fvi1 || (fvi1 + 1) % edge_count == fvi0) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (face->SubdivisionLevel() >= m_levels.UnsignedCount()) + return ON_SUBD_RETURN_ERROR(nullptr); + ON_SubDLevel* level = m_levels[face->SubdivisionLevel()]; + if ( nullptr == level) return ON_SUBD_RETURN_ERROR(nullptr); ON_SubDVertex* v[2] = { const_cast(face->Vertex(fvi0)), const_cast(face->Vertex(fvi1)) }; @@ -8760,239 +9961,178 @@ const ON_SubDEdge* ON_SubDimple::SplitFace( if (v[0] == v[1]) return ON_SUBD_RETURN_ERROR(nullptr); - // make sure each side is at least a triangle - const bool bReverseEdge = (fvi0 > fvi1); - if ( bReverseEdge ) + unsigned new_edge_count[2]; + if (fvi0 < fvi1) { - unsigned int i = fvi0; - fvi0 = fvi1; - fvi1 = i; + new_edge_count[1] = fvi1 - fvi0 + 1; + new_edge_count[0] = (edge_count + 2) - new_edge_count[1]; } - - // edge_count0 = number of edges remaining on first face - const unsigned int edge_count0 = (0 == fvi0) ? (fvi1+1) : (edge_count + 1 + fvi0 - fvi1); - if ( edge_count0 < 3 || edge_count0 >= edge_count) + else + { + new_edge_count[0] = fvi0 - fvi1 + 1; + new_edge_count[1] = (edge_count + 2) - new_edge_count[0]; + } + // make sure each side is at least a triangle and no overflows occured + if (new_edge_count[0] < 3 || new_edge_count[0] >= edge_count) return ON_SUBD_RETURN_ERROR(nullptr); - - // edge_count1 = number of edges on new face - const unsigned int edge_count1 = edge_count + 2 - edge_count0; - if ( edge_count1 < 3 || edge_count1 >= edge_count) + if (new_edge_count[1] < 3 || new_edge_count[1] >= edge_count) return ON_SUBD_RETURN_ERROR(nullptr); - - + if (new_edge_count[0] + new_edge_count[1] != edge_count+2 ) + return ON_SUBD_RETURN_ERROR(nullptr); + // make sure face topology is valid - ON_SubDEdgePtr* face_eptr = face->m_edge4; - for (unsigned int fei = 0; fei < edge_count; fei++, face_eptr++) + ON_SimpleArray< ON_SubDEdgePtr > edges(edge_count); + ON_SubDEdgePtr* eptr = face->m_edge4; + edges.SetCount(edge_count); + for (unsigned int fei = 0; fei < edge_count; ++fei, ++eptr) { if (4 == fei) - face_eptr = face->m_edgex; - const ON_SubDEdge* face_e = ON_SUBD_EDGE_POINTER(face_eptr->m_ptr); + eptr = face->m_edgex; + const ON_SubDEdge* face_e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr == face_e) return ON_SUBD_RETURN_ERROR(nullptr); if (ON_UNSET_UINT_INDEX == face_e->FaceArrayIndex(face) ) return ON_SUBD_RETURN_ERROR(nullptr); - const ON_SubDVertex* face_v = face_e->m_vertex[ON_SUBD_EDGE_DIRECTION(face_eptr->m_ptr)]; + const ON_SubDVertex* face_v = face_e->m_vertex[ON_SUBD_EDGE_DIRECTION(eptr->m_ptr)]; if ( nullptr == face_v) return ON_SUBD_RETURN_ERROR(nullptr); if (ON_UNSET_UINT_INDEX == face_v->FaceArrayIndex(face) ) return ON_SUBD_RETURN_ERROR(nullptr); + edges[fei] = *eptr; } // create diagonal edge - ON_SubD::EdgeTag etag = ON_SubD::EdgeTag::Unset; - const bool bSmoothVertex[2] = {ON_SubD::VertexTag::Smooth == v[0]->m_vertex_tag,ON_SubD::VertexTag::Smooth == v[1]->m_vertex_tag}; - double sector_weight[2] = { - bSmoothVertex[0] ? ON_SubDSectorType::IgnoredSectorWeight : ON_SubDSectorType::UnsetSectorWeight, - bSmoothVertex[1] ? ON_SubDSectorType::IgnoredSectorWeight : ON_SubDSectorType::UnsetSectorWeight - }; - - if (bSmoothVertex[0] || bSmoothVertex[1]) - etag = ON_SubD::EdgeTag::Smooth; - else - etag = ON_SubD::EdgeTag::X; - - ON_SubDEdge* e = nullptr; - ON_SubDFace* f = nullptr; + ON_SubDEdge* new_e = nullptr; + ON_SubDFace* new_f = nullptr; for (;;) { - f = AllocateFace(); - if (nullptr == f) - break; - f->m_level = face->m_level; - AddFaceToLevel(f); - - if (edge_count1 > 4) - { - if ( false == m_heap.GrowFaceEdgeArray(f,edge_count1) ) - break; - } - if (false == m_heap.GrowVertexFaceArrayByOne(v[0])) break; if (false == m_heap.GrowVertexFaceArrayByOne(v[1])) break; - e = AddEdge(etag, v[0], sector_weight[0], v[1], sector_weight[1]); - if (nullptr == e) - return ON_SUBD_RETURN_ERROR(nullptr); - - unsigned int fvi_limits[2]; - if (0 == fvi0) + new_f = AllocateFace(); + if (nullptr == new_f) + break; + new_f->SetSubdivisionLevel( face->SubdivisionLevel() ); + AddFaceToLevel(new_f); + if (new_edge_count[1] > 4) { - fvi_limits[0] = fvi1; - fvi_limits[1] = edge_count; - } - else - { - fvi_limits[0] = fvi0; - fvi_limits[1] = fvi1; + if (false == m_heap.GrowFaceEdgeArray(new_f, new_edge_count[1])) + break; } - face_eptr = (fvi_limits[0] < 4) ? (face->m_edge4 + fvi_limits[0]) : (face->m_edgex + (fvi_limits[0]-4)); - - ON_SubDEdgePtr* face1_eptr = f->m_edge4; - *face1_eptr++ = ON_SubDEdgePtr::Create(e,bReverseEdge?0:1); - f->m_edge_count++; - - for (unsigned fvi = fvi_limits[0]; fvi < fvi_limits[1]; fvi++) - { - if ( 4 == fvi) - face_eptr = face->m_edgex; - - if ( 4 == f->m_edge_count) - face1_eptr = f->m_edgex; - - // topology validation above checked that face_e is not null - ON_SubDEdge* face_e = ON_SUBD_EDGE_POINTER(face_eptr->m_ptr); - ON__UINT_PTR face_edir = ON_SUBD_EDGE_DIRECTION(face_eptr->m_ptr); - // topology validation above checked that face_edex is valid. - unsigned int face_edex = face_e->FaceArrayIndex(face); - - // topology validation above checked that face_v is not null - ON_SubDVertex* face_v = const_cast(face_e->m_vertex[face_edir]); - - if (v[0] != face_v && v[1] != face_v) - { - // topology validation above checked that face_vdex is valid. - unsigned int face_vdex = face_v->FaceArrayIndex(face); - - // change face_v reference from "face" to "f" - face_v->m_faces[face_vdex] = f; - } - - // change face_e reference from "face" to "f" - ON_SubDFacePtr* e_fptr = (face_edex < 2) ? (face_e->m_face2 + face_edex) : (face_e->m_facex + (face_edex-2)); - *e_fptr = ON_SubDFacePtr::Create(f,face_edir); - - // add edge to new face "f" - *face1_eptr++ = ON_SubDEdgePtr::Create(face_e,face_edir); - f->m_edge_count++; - - // remove edge from original face "face" - *face_eptr++ = ON_SubDEdgePtr::Null; - } - - if ( edge_count1 != f->m_edge_count ) + new_e = AddEdge( + ((v[0]->IsSmooth() || v[1]->IsSmooth()) ? ON_SubD::EdgeTag::Smooth : ON_SubD::EdgeTag::SmoothX), + v[0], ON_SubDSectorType::UnsetSectorWeight, + v[1], ON_SubDSectorType::UnsetSectorWeight); + if (nullptr == new_e) break; - if (0 == fvi0) + face->FaceModifiedNofification(); + + v[0]->m_faces[v[0]->m_face_count++] = new_f; + v[1]->m_faces[v[1]->m_face_count++] = new_f; + + new_e->m_face2[0] = ON_SubDFacePtr::Create(face, 0); + new_e->m_face2[1] = ON_SubDFacePtr::Create(new_f, 1); + new_e->m_face_count = 2; + const ON_SubDEdgePtr new_eptr = ON_SubDEdgePtr::Create(new_e); + + eptr = face->m_edge4; + for (unsigned int fei = 0; fei < edge_count; ++fei, ++eptr) { - face->m_edge_count = (unsigned short)(edge_count0-1); - face_eptr = (face->m_edge_count < 4) ? (face->m_edge4 + face->m_edge_count) : (face->m_edgex + (face->m_edge_count-4)); - *face_eptr = ON_SubDEdgePtr::Create(e,bReverseEdge?1:0); - face->m_edge_count++; + if (4 == fei) + eptr = face->m_edgex; + *eptr = ON_SubDEdgePtr::Null; } - else - { - face->m_edge_count = (unsigned short)fvi0; - face1_eptr = (face->m_edge_count < 4) ? (face->m_edge4 + face->m_edge_count) : (face->m_edgex + (face->m_edge_count-4)); - *face1_eptr++ = ON_SubDEdgePtr::Create(e,bReverseEdge?1:0); - face->m_edge_count++; - face_eptr = (fvi1 < 4) ? (face->m_edge4 + fvi1) : (face->m_edgex + (fvi1-4)); - for (unsigned int fvi = fvi1; fvi < edge_count; fvi++) - { - if (4 == fvi) - face_eptr = face->m_edgex; - if ( 4 == face->m_edge_count) - face1_eptr = face->m_edgex; - *face1_eptr++ = *face_eptr++; - face->m_edge_count++; - } - } - if ( edge_count0 != face->m_edge_count ) - break; - - face_eptr = (face->m_edge_count < 4) ? (face->m_edge4 + face->m_edge_count) : (face->m_edgex + (face->m_edge_count-4)); - for (unsigned int fvi = face->m_edge_count; fvi < edge_count; fvi++) - { - if ( 4 == fvi ) - face_eptr = face->m_edgex; - *face_eptr++ = ON_SubDEdgePtr::Null; - } - - e->m_face2[0] = ON_SubDFacePtr::Create(face,bReverseEdge?1:0); - e->m_face2[1] = ON_SubDFacePtr::Create(f,bReverseEdge?0:1); - e->m_face_count = 2; - - v[0]->m_faces[v[0]->m_face_count++] = f; - //v[0]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; - v[1]->m_faces[v[1]->m_face_count++] = f; - //v[1]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; - - if (face->m_edge_count <= 4 && nullptr != face->m_edgex) + face->m_edge_count = 0; + if (new_edge_count[0] <= 4 && nullptr != face->m_edgex) m_heap.ReturnFaceExtraArray(face); - if ( false == bSmoothVertex[0] || false == bSmoothVertex[1]) + // update old face + face->m_edge4[0] = new_eptr; + eptr = &(face->m_edge4[1]); + for (unsigned fei = 1; fei < new_edge_count[0]; ++fei, ++eptr) { - // update sector weights because they depend on the number of edges - ON_SubD::SubDType subd_type = level->m_subdivision_type; - if (ON_SubD::IsQuadOrTriSubDType(subd_type)) + if (4 == fei) + eptr = face->m_edgex; + *eptr = edges[(fvi1 + fei - 1) % edge_count]; + } + face->m_edge_count = (unsigned short)new_edge_count[0]; + + // initialize new_f + new_f->m_edge4[0] = new_eptr.Reversed(); + eptr = &(new_f->m_edge4[1]); + for (unsigned fei = 1; fei < new_edge_count[1]; ++fei, ++eptr) + { + if (4 == fei) + eptr = new_f->m_edgex; + *eptr = edges[(fvi0 + fei - 1) % edge_count]; + + // change edge's face reference from old face to new_f. + ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); + e->ReplaceFaceInArray(face, new_f); + + ON_SubDVertex* vtx = const_cast(eptr->RelativeVertex(0)); + if (nullptr != vtx && v[0] != vtx && v[1] != vtx) + vtx->ReplaceFaceInArray(face, new_f); + } + new_f->m_edge_count = (unsigned short)new_edge_count[1]; + + // update sector weights because they depend on the number of edges + for (unsigned int vi = 0; vi < 2; vi++) + { + for (unsigned short evi = 0; evi < v[vi]->m_edge_count; ++evi) { - for (unsigned int vi = 0; vi < 2; vi++) - { - if ( bSmoothVertex[vi] ) - continue; - ON_SubDSectorIterator sit; - sit.Initialize(face, 0, v[vi]); - sit.IncrementToCrease(-1); - sit.InitializeToCurrentFace(); - if (v[vi]->IsCreaseOrCorner() || ON_SubD::VertexTag::Dart == v[vi]->m_vertex_tag) - { - const ON_SubDSectorType sector_type = ON_SubDSectorType::Create(subd_type, sit); - sector_weight[vi] = sector_type.SectorWeight(); - } - for (;;) - { - const ON_SubDEdge* edge = sit.CurrentEdge(0); - if (nullptr == edge) - break; - unsigned int evi; - if (v[vi] == edge->m_vertex[0]) - evi = 0; - else if (v[vi] == edge->m_vertex[1]) - evi = 1; - else - evi = 2; - if (evi < 2) - const_cast(edge)->m_sector_coefficient[vi] = sector_weight[vi]; - if (nullptr == sit.CurrentFace() || sit.InitialFace() == sit.NextFace(ON_SubDSectorIterator::StopAt::AnyCrease)) - break; - } - } + ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(v[vi]->m_edges[evi].m_ptr); + if (nullptr != edge) + edge->UpdateEdgeSectorCoefficientsForExperts(true); } } - return e; + // Debugging code + const ON_SubDVertex* v0[6] = { + face->m_edge4[0].RelativeVertex(0),face->m_edge4[0].RelativeVertex(1), + face->m_edge4[1].RelativeVertex(0),face->m_edge4[1].RelativeVertex(1), + face->EdgePtr(face->m_edge_count-1).RelativeVertex(0),face->EdgePtr(face->m_edge_count - 1).RelativeVertex(1), + }; + const ON_SubDEdge* e0[3] = { + face->m_edge4[0].Edge(), + face->m_edge4[1].Edge(), + face->EdgePtr(face->m_edge_count - 1).Edge() + }; + const ON_SubDVertex* v1[6] = { + new_f->m_edge4[0].RelativeVertex(0),new_f->m_edge4[0].RelativeVertex(1), + new_f->m_edge4[1].RelativeVertex(0),new_f->m_edge4[1].RelativeVertex(1), + new_f->EdgePtr(new_f->m_edge_count - 1).RelativeVertex(0),new_f->EdgePtr(new_f->m_edge_count - 1).RelativeVertex(1), + }; + const ON_SubDEdge* e1[3] = { + new_f->m_edge4[0].Edge(), + new_f->m_edge4[1].Edge(), + new_f->EdgePtr(new_f->m_edge_count - 1).Edge() + }; + if (nullptr == v0[5] && nullptr == v1[5] && nullptr == e0[2] && nullptr == e1[2]) + return nullptr; + + + + return new_e; } - if ( nullptr != f ) - ReturnFace(f); + if ( nullptr != new_f ) + ReturnFace(new_f); - if (nullptr != e) + if (nullptr != new_e) { v[0]->m_edge_count--; + v[0]->m_edges[v[0]->m_edge_count] = ON_SubDEdgePtr::Null; v[1]->m_edge_count--; - ReturnEdge(e); + v[1]->m_edges[v[1]->m_edge_count] = ON_SubDEdgePtr::Null; + new_e->m_vertex[0] = nullptr; + new_e->m_vertex[1] = nullptr; + new_e->m_face_count = 0; + ReturnEdge(new_e); } return ON_SUBD_RETURN_ERROR(nullptr); @@ -9011,6 +10151,43 @@ const ON_SubDEdge* ON_SubD::SplitEdge( return subdimple->SplitEdge(edge,vertex_location); } +const ON_SubDEdge * ON_SubD::SplitFace( + ON_SubDFace * face, + const ON_SubDVertex * v0, + const ON_SubDVertex * v1 +) +{ + if (nullptr == face || nullptr == v0 || nullptr == v1 || v0 == v1) + return ON_SUBD_RETURN_ERROR(nullptr); + unsigned int fvi0 = ON_UNSET_UINT_INDEX; + unsigned int fvi1 = ON_UNSET_UINT_INDEX; + ON_SubDEdgePtr* eptr = face->m_edge4; + for (unsigned short fei = 0; fei < face->m_edge_count; ++fei, ++eptr) + { + if (4 == fei) + { + eptr = face->m_edgex; + if (nullptr == eptr) + return ON_SUBD_RETURN_ERROR(nullptr); + } + const ON_SubDVertex* v = eptr->RelativeVertex(0); + if (v == v0) + { + if (ON_UNSET_UINT_INDEX != fvi0) + return ON_SUBD_RETURN_ERROR(nullptr); + fvi0 = fei; + } + else if (v == v1) + { + if (ON_UNSET_UINT_INDEX != fvi1) + return ON_SUBD_RETURN_ERROR(nullptr); + fvi1 = fei; + } + } + return (ON_UNSET_UINT_INDEX != fvi0 && ON_UNSET_UINT_INDEX != fvi1) + ? SplitFace(face, fvi0, fvi1) + : ON_SUBD_RETURN_ERROR(nullptr); +} const ON_SubDEdge* ON_SubD::SplitFace( ON_SubDFace* face, @@ -9024,137 +10201,6 @@ const ON_SubDEdge* ON_SubD::SplitFace( return subdimple->SplitFace(face,fvi0,fvi1); } -static unsigned int OppositeCornerIndex( - const ON_SubDFace* face, - unsigned int fvi0 - ) -{ - if ( nullptr == face) - return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); - - const unsigned int edge_count = face->m_edge_count; - - if ( edge_count < 3 ) - return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); - - if ( edge_count > 4 && nullptr == face->m_edgex ) - return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); - - if ( 3 == face->m_edge_count ) - return ON_UNSET_UINT_INDEX; // not an error - - const ON_SubDVertex* face_v = face->Vertex(fvi0); - if (nullptr == face_v) - return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); - - const ON_3dPoint P0(face_v->m_P); - - const ON_SubDVertex* best_v = nullptr; - unsigned int best_fvi = ON_UNSET_UINT_INDEX; - double best_d = 0.0; - - const unsigned int i0 = (fvi0 + 2) % edge_count; - const unsigned int i1 = (fvi0 + edge_count - 1) % edge_count; - const ON_SubDEdgePtr* eptr = i0 < 4 ? (face->m_edge4 + i0) : (face->m_edgex + (i0-4)); - for (unsigned int i = i0; i != i1; i = (i + 1) % edge_count, eptr++) - { - if ( i == 0 ) - eptr = face->m_edge4; - else if ( i == 4 ) - eptr = face->m_edgex; - const ON_SubDEdge* face_e = ON_SUBD_EDGE_POINTER(eptr->m_ptr); - if ( nullptr == face_e ) - return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); - ON__UINT_PTR face_edir = ON_SUBD_EDGE_DIRECTION(eptr->m_ptr); - face_v = face_e->m_vertex[face_edir]; - if ( nullptr == face_v ) - return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); - const ON_3dPoint P1(face_v->m_P); - double d = P0.DistanceTo(P1); - if (nullptr == best_v || (face_v->IsSmoothOrDart() && best_v->IsCreaseOrCorner()) ) - { - best_v = face_v; - best_d = d; - best_fvi = i; - continue; - } - - if (d > best_d && (face_v->IsSmoothOrDart() || best_v->IsCreaseOrCorner())) - { - best_v = face_v; - best_d = d; - best_fvi = i; - continue; - } - } - - if ( best_fvi < edge_count ) - return best_fvi; - - return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); -} - - - -bool ON_SubD::RepairInvalidSectors( - unsigned int level_index - ) -{ - bool rc = true; - for (const ON_SubDVertex* vertex = FirstVertex(); nullptr != vertex; vertex = vertex->m_next_vertex) - { - if (vertex->IsSmoothOrDart() - && 2 == vertex->m_edge_count && 2 == vertex->m_face_count - && nullptr != vertex->m_edges && nullptr != vertex->m_faces - ) - { - const unsigned int fvi0[2] = { - nullptr == vertex->m_faces[0] ? ON_UNSET_UINT_INDEX : vertex->m_faces[0]->VertexIndex(vertex), - nullptr == vertex->m_faces[1] ? ON_UNSET_UINT_INDEX : vertex->m_faces[1]->VertexIndex(vertex)}; - const unsigned int fvi1[2] = { - OppositeCornerIndex(vertex->m_faces[0],fvi0[0]), - OppositeCornerIndex(vertex->m_faces[1],fvi0[1]) - }; - // split adjacent faces - for (unsigned int pass = 0; pass < 2; pass++) - { - if ( 2 != vertex->m_edge_count || 2 != vertex->m_face_count) - break; - for (unsigned int vfi = 0; vfi < 2; vfi++) - { - if (ON_UNSET_UINT_INDEX == fvi0[vfi] || ON_UNSET_UINT_INDEX == fvi1[vfi]) - continue; - const ON_SubDFace* face = vertex->m_faces[vfi]; - if (nullptr == face) - continue; - const ON_SubDVertex* face_v = face->Vertex(fvi1[vfi]); - if (nullptr == face_v) - continue; - // first pass splits corner vertices. - // If no corners are found during the first pass, then - // second pass splits any neighbor quad. - if (0 == pass && face_v->IsCreaseOrCorner()) - continue; - SplitFace(const_cast(face), fvi0[vfi], fvi1[vfi]); - } - } - if (2 == vertex->m_edge_count && 2 == vertex->m_face_count) - { - TriangulateFace(const_cast(vertex->m_faces[0])); - TriangulateFace(const_cast(vertex->m_faces[1])); - if (2 == vertex->m_edge_count && 2 == vertex->m_face_count) - { - // cannot fix this vertex - ON_SubDIncrementErrorCount(); - rc = false; - } - } - } - } - - return rc; -} - void ON_SubD::MarkAggregateComponentStatusAsNotCurrent() const { const ON_SubDLevel* level = ActiveLevelConstPointer(); @@ -9177,6 +10223,40 @@ unsigned int ON_SubDLevel::ClearStates( return rc; } +unsigned int ON_SubDLevel::ClearRuntimeMarks( + bool bClearVertexMarks, + bool bClearEdgeMarks, + bool bClearFaceMarks +) const +{ + unsigned int rc = 0; + if (bClearVertexMarks) + { + for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) + { + if (vertex->m_status.ClearRuntimeMark()) + ++rc; + } + } + if (bClearEdgeMarks) + { + for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) + { + if (edge->m_status.ClearRuntimeMark()) + ++rc; + } + } + if (bClearFaceMarks) + { + for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) + { + if (face->m_status.ClearRuntimeMark()) + ++rc; + } + } + return rc; +} + unsigned int ON_SubD::ClearComponentStates( ON_ComponentStatus states_to_clear ) const @@ -9408,23 +10488,46 @@ unsigned int ON_SubD::SetComponentStatus( return ON_SUBD_RETURN_ERROR(0); } -void ON_SubDLevel::ClearSubdivisonAndLimitPoints() const +void ON_SubDLevel::ClearEvaluationCache() const { + ClearEdgeFlags(); + ClearBoundingBox(); + m_surface_mesh = ON_SubDMesh::Empty; + m_control_net_mesh = ON_SubDMesh::Empty; + for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) { - vertex->ClearSavedSubdivisionPoint(); - vertex->ClearSavedLimitPoints(); + vertex->ClearSavedSubdivisionPoints(); } + for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) { - edge->ClearSavedSubdivisionPoint(); - // NO // Leave these set - they are not "cached" values // edge->UnsetSectorCoefficients(); + edge->ClearSavedSubdivisionPoints(); + // NO // edge->UnsetSectorCoefficients(); + // Leave these set - they are not "cached" values and except for corner case below + // the are independent vertex locations. + if ( edge->IsSmooth() ) + { + for (unsigned evi = 0; evi < 2; evi++) + { + if ( false == (edge->m_sector_coefficient[evi] > 0.0 && edge->m_sector_coefficient[evi] < 1.0) ) + continue; + const ON_SubDVertex* v = edge->m_vertex[evi]; + if (nullptr == v) + continue; + if (ON_SubD::VertexTag::Corner != v->m_vertex_tag) + continue; + // corner sector coefficients depend on the subtended angle of the sector's crease boundary. + // All other sector coefficients are independent of vertex location. + const_cast(edge)->m_sector_coefficient[evi] = ON_SubDSectorType::Create(edge, evi).SectorWeight(); + } + } } + for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) { - face->ClearSavedSubdivisionPoint(); + face->ClearSavedSubdivisionPoints(); } - m_aggregates.m_bDirtyBoundingBox = true; } unsigned int ON_SubD::ComponentPtrFromComponentIndex( @@ -9501,14 +10604,31 @@ bool ON_SubD::DeleteComponents( if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0) return true; // nothing to delete - return DeleteComponents(cptr_list.Array(),cptr_list.UnsignedCount()); + return DeleteComponents(cptr_list.Array(),cptr_list.UnsignedCount(),false); } bool ON_SubD::DeleteComponents( const ON_SubDComponentPtr* cptr_list, - size_t cptr_count - ) + size_t cptr_count, + bool bMarkDeletedFaceEdges +) { + const bool bDeleteIsolatedEdges = true; + const bool bUpdateTagsAndCoefficients = true; + return DeleteComponentsForExperts(cptr_list, cptr_count, bDeleteIsolatedEdges, bUpdateTagsAndCoefficients, bMarkDeletedFaceEdges); +} + +bool ON_SubD::DeleteComponentsForExperts( + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + bool bDeleteIsolatedEdges, + bool bUpdateTagsAndCoefficients, + bool bMarkDeletedFaceEdges +) +{ + if (bMarkDeletedFaceEdges) + ClearComponentMarks(false,true,false,nullptr); + if ( cptr_count <= 0 ) return true; @@ -9523,14 +10643,14 @@ bool ON_SubD::DeleteComponents( if (level_count <= 0) return ON_SUBD_RETURN_ERROR(false); - unsigned int level_index = level_count; + unsigned level_index = level_count; for (size_t i = 0; i < cptr_count; i++) { const ON_SubDComponentBase* c = cptr_list[i].ComponentBase(); if ( nullptr == c) continue; - if ( c->m_level < level_index ) - level_index = c->m_level; + if ( c->SubdivisionLevel() < level_index ) + level_index = c->SubdivisionLevel(); } if ( level_index == level_count ) return ON_SUBD_RETURN_ERROR(false); @@ -9563,46 +10683,57 @@ bool ON_SubD::DeleteComponents( const ON_SubDComponentBase* c = cptr.ComponentBase(); if (nullptr == c) continue; - if (c->m_level != level_index) + if (c->SubdivisionLevel() != level_index) continue; c->m_status = ON_ComponentStatus::AllSet; switch (cptr.ComponentType()) { case ON_SubDComponentPtr::Type::Vertex: - { - const ON_SubDVertex* vertex = cptr.Vertex(); - if (nullptr == vertex) - continue; - for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++) { - const ON_SubDEdge* edge = vertex->Edge(vei); + const ON_SubDVertex* vertex = cptr.Vertex(); + if (nullptr == vertex) + continue; + for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++) + { + const ON_SubDEdge* edge = vertex->Edge(vei); + if (nullptr == edge) + continue; + edge->m_status = ON_ComponentStatus::AllSet; + } + for (unsigned short vfi = 0; vfi < vertex->m_face_count; vfi++) + { + const ON_SubDFace* face = vertex->Face(vfi); + if (nullptr == face) + continue; + face->m_status = ON_ComponentStatus::AllSet; + } + } + break; + + case ON_SubDComponentPtr::Type::Edge: + { + const ON_SubDEdge* edge = cptr.Edge(); if (nullptr == edge) continue; edge->m_status = ON_ComponentStatus::AllSet; + for (unsigned short efi = 0; efi < edge->m_face_count; efi++) + { + const ON_SubDFace* face = edge->Face(efi); + if (nullptr == face) + continue; + face->m_status = ON_ComponentStatus::AllSet; + } } - for (unsigned short vfi = 0; vfi < vertex->m_face_count; vfi++) + break; + + case ON_SubDComponentPtr::Type::Face: { - const ON_SubDFace* face = vertex->Face(vfi); + const ON_SubDFace* face = cptr.Face(); if (nullptr == face) continue; face->m_status = ON_ComponentStatus::AllSet; } - } - break; - case ON_SubDComponentPtr::Type::Edge: - { - const ON_SubDEdge* edge = cptr.Edge(); - if (nullptr == edge) - continue; - for (unsigned short efi = 0; efi < edge->m_face_count; efi++) - { - const ON_SubDFace* face = edge->Face(efi); - if (nullptr == face) - continue; - face->m_status = ON_ComponentStatus::AllSet; - } - } - break; + break; } } @@ -9635,13 +10766,18 @@ bool ON_SubD::DeleteComponents( if ( 0 == deleted_vertex_count && 0 == deleted_edge_count && 0 == deleted_face_count ) return false; - if (deleted_vertex_count >= level->m_vertex_count || deleted_edge_count >= level->m_edge_count || deleted_face_count >= level->m_face_count) + const bool bDestroy + = deleted_vertex_count >= level->m_vertex_count + || deleted_edge_count >= level->m_edge_count + || (deleted_face_count >= level->m_face_count && bDeleteIsolatedEdges) + ; + if (bDestroy) { Destroy(); return true; } - unsigned int deleted_component_count = subdimple->DeleteComponents(level_index); + unsigned int deleted_component_count = subdimple->DeleteComponents(level_index,bDeleteIsolatedEdges,bUpdateTagsAndCoefficients,bMarkDeletedFaceEdges); if (0 == subdimple->LevelCount()) { @@ -9706,7 +10842,7 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( case ON_SubD::EdgeTag::Unset: if (2 == tagged_end_index) { - edge->m_edge_tag = ON_SubD::EdgeTag::X; + edge->m_edge_tag = ON_SubD::EdgeTag::SmoothX; } else if ( bBothVertexTagsAreSet ) { @@ -9722,7 +10858,7 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( case ON_SubD::EdgeTag::Smooth: if (2 == tagged_end_index) { - edge->m_edge_tag = ON_SubD::EdgeTag::X; + edge->m_edge_tag = ON_SubD::EdgeTag::SmoothX; } else if (3 == tagged_end_index && bBothVertexTagsAreSet) { @@ -9736,11 +10872,11 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; break; - case ON_SubD::EdgeTag::Sharp: - ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp not handled."); - break; + //case ON_SubD::EdgeTag::Sharp: + // ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp is not valid in this version of opennurbs."); + // break; - case ON_SubD::EdgeTag::X: + case ON_SubD::EdgeTag::SmoothX: if ( 2 != tagged_end_index && bBothVertexTagsAreSet) edge->m_edge_tag = ON_SubD::EdgeTag::Smooth; break; @@ -9790,33 +10926,29 @@ unsigned int ON_SubDLevel::UpdateVertexTags( ON_SUBD_ERROR("nullptr vertex->m_edges[] values"); continue; } - if (ON_SubD::EdgeTag::Sharp == edge->m_edge_tag) - { - ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp is not supported yet."); - continue; - } - if ( ON_SubD::EdgeTag::Crease == edge->m_edge_tag - || 2 != edge->m_face_count - ) + if ( ON_SubD::EdgeTag::Crease == edge->m_edge_tag || 2 != edge->m_face_count ) creased_edge_count++; - else if ( ON_SubD::EdgeTag::Sharp == edge->m_edge_tag ) + else if (((ON_SubD::EdgeTag)3) == edge->m_edge_tag) // ON_SubD::EdgeTag::Sharp + { + ON_SUBD_ERROR("ON_SubD::EdgeTag::Sharp is not valid in this version of opennurbs."); sharp_edge_count++; + } // NOTE: // edges tagged as ON_SubD::EdgeTag::Unset with two faces // ending at a vertex with 3 or more edges - // will eventually be tagged as smooth once this vertex - // is tagged as smooth. + // will be tagged as smooth in subsequent passes + // once this vertex is tagged as smooth. } ON_SubD::VertexTag vertex_tag1 = vertex_tag0; - if (edge_count <= 2 || (creased_edge_count+sharp_edge_count) >= 2) + if ( (creased_edge_count+sharp_edge_count) >= 2 ) { if ( ON_SubD::VertexTag::Corner != vertex_tag0 ) vertex_tag1 = ON_SubD::VertexTag::Crease; } - else + else if ( edge_count >= 2 ) { if ( 1 == creased_edge_count && 0 == sharp_edge_count ) vertex_tag1 = ON_SubD::VertexTag::Dart; @@ -9869,15 +11001,71 @@ unsigned int ON_SubDLevel::UpdateAllTagsAndSectorCoefficients( ON_SUBD_ERROR("Recursion limit exceeded."); } + // Adjust edge tag smooth/X settings + // This must be done before UpdateEdgeSectorCoefficients(). + // It is done between the heavy handed setting above so as not to disturb that delicate code. + ON_SubDEdge* next_edge = m_edge[0]; + for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge) + { + next_edge = const_cast(edge->m_next_edge); + if (edge->IsSmooth()) + { + const ON_SubD::EdgeTag etag = (2 == edge->TaggedEndIndex()) ? ON_SubD::EdgeTag::SmoothX : ON_SubD::EdgeTag::Smooth; + if (etag != edge->m_edge_tag) + { + edge->m_edge_tag = etag; + ++change_count; + } + } + } + change_count += UpdateEdgeSectorCoefficients(false); - if ( change_count > 0 ) - m_limit_mesh = ON_SubDLimitMesh::Empty; - + if (change_count > 0) + { + m_surface_mesh = ON_SubDMesh::Empty; + m_control_net_mesh = ON_SubDMesh::Empty; + } return change_count; } +unsigned int ON_SubDLevel::ClearComponentDamagedState() const +{ + return ClearComponentDamagedState(true, true, true); +} + +unsigned int ON_SubDLevel::ClearComponentDamagedState( + bool bClearVerticesDamagedState, + bool bClearEdgesDamagedState, + bool bClearFacesDamagedState +) const +{ + unsigned int change_count = 0; + unsigned int i; + if (bClearVerticesDamagedState) + { + i = 0; + for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v && i++ < m_vertex_count; v = v->m_next_vertex) + if (0 != v->m_status.SetDamagedState(false)) + ++change_count; + } + if (bClearEdgesDamagedState) + { + i = 0; + for (const ON_SubDEdge* e = m_edge[0]; nullptr != e && i++ < m_edge_count; e = e->m_next_edge) + if (0 != e->m_status.SetDamagedState(false)) + ++change_count; + } + if (bClearFacesDamagedState) + { + i = 0; + for (const ON_SubDFace* f = m_face[0]; nullptr != f && i++ < m_face_count; f = f->m_next_face) + if (0 != f->m_status.SetDamagedState(false)) + ++change_count; + } + return change_count; +} unsigned int ON_SubD::UpdateVertexTags( @@ -9916,7 +11104,15 @@ unsigned int ON_SubD::UpdateEdgeSectorCoefficients( void ON_SubD::SubDModifiedNofification() { - ClearLimitSurfaceMesh(); + // DestroyRuntimeCache() + // Clears + // Saved subdivision points. + // Saved limit surface information. + // Bounding boxes. + // + DestroyRuntimeCache(); + + // This is a heavy handed tag update. UpdateAllTagsAndSectorCoefficients(false); } @@ -9932,9 +11128,12 @@ unsigned int ON_SubD::UpdateAllTagsAndSectorCoefficients( } unsigned int ON_SubDimple::DeleteComponents( - unsigned int level_index + unsigned int level_index, + bool bDeleteIsolatedEdges, + bool bUpdateTagsAndCoefficients, + bool bMarkDeletedFaceEdges ) -{ +{ unsigned int deleted_component_count = 0; if (level_index >= m_levels.UnsignedCount()) @@ -9944,6 +11143,9 @@ unsigned int ON_SubDimple::DeleteComponents( if (nullptr == level) return ON_SUBD_RETURN_ERROR(0); + if (bMarkDeletedFaceEdges) + level->ClearRuntimeMarks(false,true,false); + ON_SubDFace* next_face = level->m_face[0]; for (ON_SubDFace* face = next_face; nullptr != face; face = next_face) { @@ -9951,9 +11153,16 @@ unsigned int ON_SubDimple::DeleteComponents( bool bDelete = (ON_ComponentStatus::AllSet == face->m_status || 0 == face->m_edge_count); if (false == bDelete) { - for (unsigned short fei = 0; fei < face->m_edge_count && false == bDelete; fei++) + const ON_SubDEdgePtr* eptr = face->m_edge4; + for (unsigned short fei = 0; fei < face->m_edge_count && false == bDelete; ++fei, ++eptr) { - const ON_SubDEdge* edge = face->Edge(fei); + if (4 == fei) + { + eptr = face->m_edgex; + if (nullptr == eptr) + break; + } + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); if (nullptr == edge || nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1] @@ -9970,6 +11179,25 @@ unsigned int ON_SubDimple::DeleteComponents( if (false == bDelete) continue; } + + if (bMarkDeletedFaceEdges) + { + // Set runtime mark on face's boundary edges. + const ON_SubDEdgePtr* eptr = face->m_edge4; + for (unsigned short fei = 0; fei < face->m_edge_count ; ++fei, ++eptr) + { + if (4 == fei) + { + eptr = face->m_edgex; + if (nullptr == eptr) + break; + } + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr); + if (nullptr != edge) + edge->m_status.SetRuntimeMark(); + } + } + level->RemoveFace(face); m_heap.ReturnFace(face); deleted_component_count++; @@ -9979,7 +11207,7 @@ unsigned int ON_SubDimple::DeleteComponents( for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge) { next_edge = const_cast< ON_SubDEdge* >(edge->m_next_edge); - bool bDelete = (ON_ComponentStatus::AllSet == edge->m_status || 0 == edge->m_face_count ); + bool bDelete = (ON_ComponentStatus::AllSet == edge->m_status || (bDeleteIsolatedEdges && 0 == edge->m_face_count) ); if (false == bDelete) { for (unsigned short evi = 0; evi < 2 && false == bDelete; evi++) @@ -10004,7 +11232,7 @@ unsigned int ON_SubDimple::DeleteComponents( for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex) { next_vertex = const_cast(vertex->m_next_vertex); - bool bDelete = (ON_ComponentStatus::AllSet == vertex->m_status || 0 == vertex->m_face_count || 0 == vertex->m_edge_count ); + bool bDelete = (ON_ComponentStatus::AllSet == vertex->m_status || (bDeleteIsolatedEdges && 0 == vertex->m_face_count) || 0 == vertex->m_edge_count ); if ( false == bDelete ) continue; @@ -10038,7 +11266,7 @@ unsigned int ON_SubDimple::DeleteComponents( fptr1 = edge->m_facex; } - if (0 == edge->m_face_count) + if (0 == edge->m_face_count && bDeleteIsolatedEdges) { level->RemoveEdge(edge); m_heap.ReturnEdge(edge); @@ -10049,11 +11277,14 @@ unsigned int ON_SubDimple::DeleteComponents( if (edge->m_face_count <= 2 && nullptr != edge->m_facex) m_heap.ReturnEdgeExtraArray(edge); - if ( edge->m_face_count != 2 ) - edge->m_edge_tag = ON_SubD::EdgeTag::Crease; + if (bUpdateTagsAndCoefficients) + { + if (edge->m_face_count != 2) + edge->m_edge_tag = ON_SubD::EdgeTag::Crease; - edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; - edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; + edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; + edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; + } } // Remove vertex references to deleted edges and faces @@ -10069,7 +11300,7 @@ unsigned int ON_SubDimple::DeleteComponents( unsigned int crease_count = 0; for (unsigned short vei = 0; vei < count; vei++) { - const ON_SubDEdge* edge = vertex->m_edges[vei].Edge(); + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); if (nullptr == edge || ON_UNSET_UINT_INDEX == edge->ArchiveId()) { bInteriorVertex = false; @@ -10100,7 +11331,7 @@ unsigned int ON_SubDimple::DeleteComponents( vertex->m_faces[vertex->m_face_count++] = vertex->m_faces[vfi]; } - if (0 == vertex->m_face_count && 0 == vertex->m_edge_count ) + if (0 == vertex->m_face_count && 0 == vertex->m_edge_count) { level->RemoveVertex(vertex); m_heap.ReturnVertex(vertex); @@ -10108,33 +11339,53 @@ unsigned int ON_SubDimple::DeleteComponents( } } - if (0 == level->m_edge_count || 0 == level->m_edge_count || 0 == level->m_face_count) + if (0 == level->m_vertex_count || 0 == level->m_edge_count || (bDeleteIsolatedEdges && 0 == level->m_face_count)) { Destroy(); } else { // remove all information that is no longer valid - level->m_limit_mesh.Clear(); level->MarkAggregateComponentStatusAsNotCurrent(); - level->ClearSubdivisonAndLimitPoints(); - level->ClearBoundingBox(); - level->ClearEdgeFlags(); + level->ClearEvaluationCache(); ClearHigherSubdivisionLevels(level_index); - // Update vertex tags, edge tags, and sector weights. - level->UpdateAllTagsAndSectorCoefficients(false); + if (bUpdateTagsAndCoefficients) + { + // Update vertex tags, edge tags, and sector weights. + level->UpdateAllTagsAndSectorCoefficients(false); + } } + ChangeContentSerialNumber(); + return deleted_component_count; } -/* -Descripiton: - Clears the ON_ComponentState -*/ + +unsigned int ON_SubD::ClearComponentMarks() const +{ + return ClearComponentMarks(true, true, true, nullptr); +} + +unsigned int ON_SubD::ClearVertexMarks() const +{ + return ClearComponentMarks(true, false, false, nullptr); +} + +unsigned int ON_SubD::ClearEdgeMarks() const +{ + return ClearComponentMarks(false, true, false, nullptr); +} + +unsigned int ON_SubD::ClearFaceMarks() const +{ + return ClearComponentMarks(false, false, true, nullptr); +} + + unsigned int ON_SubD::ClearComponentMarks( bool bClearVertexMarks, bool bClearEdgeMarks, @@ -10189,6 +11440,48 @@ unsigned int ON_SubD::ClearComponentMarks( return clear_count; } +unsigned int ON_SubD::UnselectComponents( + bool bUnselectAllVertices, + bool bUnselectAllEdges, + bool bUnselectAllFaces + ) const +{ + unsigned int unselected_count = 0; + + if (bUnselectAllVertices) + { + ON_SubDVertexIterator vit(*this); + for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + { + if (v->m_status.SetSelectedState(false, false, false)) + ++unselected_count; + } + } + + if (bUnselectAllEdges) + { + ON_SubDEdgeIterator eit(*this); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + if (e->m_status.SetSelectedState(false, false, false)) + ++unselected_count; + } + } + + if (bUnselectAllFaces) + { + ON_SubDFaceIterator fit(*this); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + if (f->m_status.SetSelectedState(false, false, false)) + ++unselected_count; + } + } + + return unselected_count; +} + + unsigned int ON_SubD::SetComponentMarks( bool bClearBeforeSet, const ON_SimpleArray< const ON_SubDComponentBase* >& marked_component_list @@ -10236,8 +11529,7 @@ unsigned int ON_SubD::GetMarkedComponents( { if (v->m_status.RuntimeMark()) { - if (nullptr != marked_component_list) - marked_component_list.Append(v); + marked_component_list.Append(v); mark_count++; } } @@ -10250,8 +11542,7 @@ unsigned int ON_SubD::GetMarkedComponents( { if (e->m_status.RuntimeMark()) { - if (nullptr != marked_component_list) - marked_component_list.Append(e); + marked_component_list.Append(e); mark_count++; } } @@ -10264,8 +11555,7 @@ unsigned int ON_SubD::GetMarkedComponents( { if (f->m_status.RuntimeMark()) { - if (nullptr != marked_component_list) - marked_component_list.Append(f); + marked_component_list.Append(f); mark_count++; } } @@ -10274,12 +11564,118 @@ unsigned int ON_SubD::GetMarkedComponents( return mark_count; } +unsigned int ON_SubD::GetComponentStatus( + bool bGetVertexStatus, + bool bGetEdgeStatus, + bool bGetFaceStatus, + bool bClearStatus, + ON_ComponentStatus status_mask, + ON_SimpleArray< const class ON_SubDComponentBase* >& component_list, + ON_SimpleArray< ON_ComponentStatus >& status_list +) const +{ + component_list.SetCount(0); + status_list.SetCount(0); + if ( ON_ComponentStatus::NoneSet == status_mask ) + return 0; + + ON_ComponentStatus s; + + if (bGetVertexStatus) + { + ON_SubDVertexIterator vit(*this); + for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + { + s = ON_ComponentStatus::LogicalAnd(status_mask, v->m_status); + if (ON_ComponentStatus::NoneSet == s) + continue; + component_list.Append(v); + status_list.Append(s); + } + } + + if (bGetEdgeStatus) + { + ON_SubDEdgeIterator eit(*this); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + s = ON_ComponentStatus::LogicalAnd(status_mask, e->m_status); + if (ON_ComponentStatus::NoneSet == s) + continue; + component_list.Append(e); + status_list.Append(s); + } + } + + if (bGetFaceStatus) + { + ON_SubDFaceIterator fit(*this); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + s = ON_ComponentStatus::LogicalAnd(status_mask, f->m_status); + if (ON_ComponentStatus::NoneSet == s) + continue; + component_list.Append(f); + status_list.Append(s); + } + } + + const unsigned int count = component_list.UnsignedCount(); + if (bClearStatus && count > 0) + { + const bool bRuntimeMark = status_mask.RuntimeMark(); + for (unsigned int i = 0; i < count; ++i) + { + const ON_SubDComponentBase* c = component_list[i]; + if (nullptr == c) + continue; + c->m_status.ClearStates(status_mask); + if (bRuntimeMark) + c->m_status.ClearRuntimeMark(); + } + } + + return count; +} + +unsigned int ON_SubD::SetComponentStatus( + ON_ComponentStatus status_mask, + const ON_SimpleArray< const class ON_SubDComponentBase* >& component_list, + const ON_SimpleArray< ON_ComponentStatus >& status_list +) const +{ + const unsigned int count = component_list.UnsignedCount(); + if (count < 1 || count != status_list.UnsignedCount()) + return 0; + + const bool bRuntimeMark = status_mask.RuntimeMark(); + + for (unsigned int i = 0; i < count; ++i) + { + const ON_SubDComponentBase* c = component_list[i]; + if (nullptr == c) + continue; + const ON_ComponentStatus s = status_list[i]; + c->m_status.ClearStates(status_mask); + c->m_status.SetStates(s); + if (bRuntimeMark) + { + if ( s.RuntimeMark()) + c->m_status.SetRuntimeMark(); + else + c->m_status.ClearRuntimeMark(); + } + } + + return count; +} + ON_SubDComponentMarksClearAndRestore::ON_SubDComponentMarksClearAndRestore( ON_SubD& subd ) { m_subd.ShareContentsFrom(subd); - m_subd.ClearComponentMarks(true, true, true, &m_saved_marked_component_list); + m_subd.ClearComponentMarks(true, true, true, &m_component_list); } ON_SubDComponentMarksClearAndRestore::~ON_SubDComponentMarksClearAndRestore() @@ -10287,9 +11683,9 @@ ON_SubDComponentMarksClearAndRestore::~ON_SubDComponentMarksClearAndRestore() Restore(true); } -const ON_SimpleArray& ON_SubDComponentMarksClearAndRestore::SavedMarkedComponentList() const +const ON_SimpleArray& ON_SubDComponentMarksClearAndRestore::ComponentList() const { - return m_saved_marked_component_list; + return m_component_list; } bool ON_SubDComponentMarksClearAndRestore::Restore( @@ -10300,11 +11696,41 @@ bool ON_SubDComponentMarksClearAndRestore::Restore( { if ( bDisableFutureRestore) m_bRestore = false; - if (m_saved_marked_component_list.UnsignedCount() > 0) - m_subd.SetComponentMarks(true, m_saved_marked_component_list); - else - m_subd.ClearComponentMarks(true, true, true, nullptr); - m_saved_marked_component_list.Destroy(); + + m_subd.ClearComponentMarks(true, true, true, nullptr); + +// if ( ON_ComponentStatus::Marked == m_status_mask ) + { + // RuntimeMark is the only bit being managed + if (m_component_list.UnsignedCount() > 0) + m_subd.SetComponentMarks(false, m_component_list); + } + //else if ( m_status_mask.IsNotClear() ) + //{ + // // something fancier is going on + + // // clear current settings + // ON_SubDVertexIterator vit(m_subd); + // for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + // v->m_status.ClearStates(m_status_mask); + + // ON_SubDEdgeIterator eit(m_subd); + // for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + // e->m_status.ClearStates(m_status_mask); + + // ON_SubDFaceIterator fit(m_subd); + // for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + // f->m_status.ClearStates(m_status_mask); + + // // restore settings + // m_subd.SetComponentStatus(m_status_mask, m_component_list, m_status_list); + //} + + if (bDisableFutureRestore) + { + m_component_list.Destroy(); + //m_status_list.Destroy(); + } } return rc; } @@ -10317,7 +11743,8 @@ void ON_SubDComponentMarksClearAndRestore::DisableRestore() unsigned int ON_SubD::TransformComponents( const ON_Xform& xform, const ON_COMPONENT_INDEX* ci_list, - size_t ci_count + size_t ci_count, + ON_SubDComponentLocation component_location ) { if ( @@ -10331,88 +11758,264 @@ unsigned int ON_SubD::TransformComponents( if (ComponentPtrFromComponentIndex(ci_list,ci_count,cptr_list) <= 0) return true; // nothing to delete - return TransformComponents(xform,cptr_list.Array(),cptr_list.UnsignedCount()); + return TransformComponents(xform,cptr_list.Array(),cptr_list.UnsignedCount(),component_location); } -static unsigned int Internal_MarkVertices( + +static unsigned int Internal_MarkStuffAndMaybeMoveVertices( const ON_SubD& subd, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, - const ON_Xform& xform + const ON_Xform* xform, + ON_SubDComponentLocation component_location, + bool bExtrusionMarking, + bool bExtrudeBoundaries, + unsigned int& list_vertex_count, + unsigned int& list_edge_count, + unsigned int& list_face_count ) { - unsigned int v_mark_count = 0; + list_vertex_count = 0; + list_edge_count = 0; + list_face_count = 0; - const bool bTransform = xform.IsValidAndNotZeroAndNotIdentity(); + if (false == bExtrusionMarking) + bExtrudeBoundaries = false; - for (size_t i = 0; i < cptr_count; i++) + const bool bTransform + = false == bExtrusionMarking + && nullptr != xform + && xform->IsValidAndNotZeroAndNotIdentity() + ; + + if ((bTransform ? 1 : 0) == (bExtrusionMarking ? 1 : 0)) { - switch (cptr_list[i].ComponentType()) + ON_SUBD_ERROR("Invalid input."); + return 0; + } + + unsigned int marked_vertex_count = 0; + //unsigned int potential_isolated_vertex_count = 0; + unsigned int potential_isolated_edge_count = 0; + + if (bExtrusionMarking && 0 == cptr_count && nullptr == cptr_list) + { + // entire subd is being extruded + ON_SubDFaceIterator fit(subd); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { - case ON_SubDComponentPtr::Type::Vertex: + ++list_face_count; + f->m_status.SetRuntimeMark(); + const unsigned int face_vertex_count = f->m_edge_count; + for (unsigned int fvi = 0; fvi < face_vertex_count; ++fvi) { - const ON_SubDVertex* v = cptr_list[i].Vertex(); + // used when extruding selected components + const ON_SubDEdge* e = f->Edge(fvi); + if (nullptr != e && false == e->m_status.RuntimeMark()) + e->m_status.SetRuntimeMark(); + const ON_SubDVertex* v = f->Vertex(fvi); if (nullptr != v && false == v->m_status.RuntimeMark()) { v->m_status.SetRuntimeMark(); - if ( bTransform) - const_cast(v)->Transform(false, xform); - v_mark_count++; + ++marked_vertex_count; } } - break; + } + } + else + { - case ON_SubDComponentPtr::Type::Edge: + for (size_t i = 0; i < cptr_count; i++) + { + switch (cptr_list[i].ComponentType()) { - const ON_SubDEdge* e = cptr_list[i].Edge(); - if (nullptr != e) + case ON_SubDComponentPtr::Type::Vertex: { - for (unsigned int evi = 0; evi < 2; evi++) + const ON_SubDVertex* v = cptr_list[i].Vertex(); + if (nullptr == v) + continue; + ++list_vertex_count; + if (v->m_status.RuntimeMark()) + continue; + if (bTransform) + { + v->m_status.SetRuntimeMark(); + if (bTransform) + const_cast(v)->Transform(false, *xform); + ++marked_vertex_count; + } + } + break; + + case ON_SubDComponentPtr::Type::Edge: + { + const ON_SubDEdge* e = cptr_list[i].Edge(); + if (nullptr == e) + continue; + ++list_edge_count; + if (e->m_status.RuntimeMark()) + continue; + if (bTransform) + { + e->m_status.SetRuntimeMark(); + for (unsigned int evi = 0; evi < 2; ++evi) + { + const ON_SubDVertex* v = e->m_vertex[evi]; + if (nullptr != v && false == v->m_status.RuntimeMark()) + { + v->m_status.SetRuntimeMark(); + const_cast(v)->Transform(false, *xform); + ++marked_vertex_count; + } + } + } + else if (bExtrudeBoundaries && 1 == e->m_face_count && nullptr != e->m_face2[0].Face()) + ++potential_isolated_edge_count; + } + break; + + case ON_SubDComponentPtr::Type::Face: + { + const ON_SubDFace* f = cptr_list[i].Face(); + if (nullptr != f) + { + ++list_face_count; + f->m_status.SetRuntimeMark(); + const unsigned int face_vertex_count = f->m_edge_count; + for (unsigned int fvi = 0; fvi < face_vertex_count; ++fvi) + { + if (bExtrusionMarking) + { + // used when extruding selected components + const ON_SubDEdge* e = f->Edge(fvi); + if (nullptr != e && false == e->m_status.RuntimeMark()) + e->m_status.SetRuntimeMark(); + } + const ON_SubDVertex* v = f->Vertex(fvi); + if (nullptr != v && false == v->m_status.RuntimeMark()) + { + v->m_status.SetRuntimeMark(); + if (bTransform) + const_cast(v)->Transform(false, *xform); + ++marked_vertex_count; + } + } + } + } + break; + } + } + + if (bExtrusionMarking && potential_isolated_edge_count > 0) + { + for (size_t i = 0; i < cptr_count; i++) + { + if (ON_SubDComponentPtr::Type::Edge != cptr_list[i].ComponentType()) + continue; + + const ON_SubDEdge* e = cptr_list[i].Edge(); + if (nullptr == e) + continue; + + if (e->m_status.RuntimeMark()) + continue; // this edge us part of a boundary belonging to a face in cptr_list[] + + if (1 == e->m_face_count && nullptr != e->m_face2[0].Face()) + { + // this boundary edge was explicitly picked its attached face was not picked. + // It will be extruded to a face. + e->m_status.SetRuntimeMark(); + for (unsigned int evi = 0; evi < 2; ++evi) { const ON_SubDVertex* v = e->m_vertex[evi]; if (nullptr != v && false == v->m_status.RuntimeMark()) { v->m_status.SetRuntimeMark(); - if ( bTransform) - const_cast(v)->Transform(false, xform); - v_mark_count++; + ++marked_vertex_count; } } + if (0 == --potential_isolated_edge_count) + break; } } - break; - - case ON_SubDComponentPtr::Type::Face: - { - const ON_SubDFace* f = cptr_list[i].Face(); - if (nullptr != f) - { - const unsigned int face_vertex_count = f->m_edge_count; - for (unsigned int fvi = 0; fvi < face_vertex_count; fvi++) - { - const ON_SubDVertex* v = f->Vertex(fvi); - if (nullptr != v && false == v->m_status.RuntimeMark()) - { - v->m_status.SetRuntimeMark(); - if ( bTransform) - const_cast(v)->Transform(false, xform); - v_mark_count++; - } - } - } - } - break; } + } - return v_mark_count; + const_cast(subd).ChangeContentSerialNumberForExperts(); + + return marked_vertex_count; +} + +static unsigned int Internal_MarkExtrudeComponents( + const ON_SubD& subd, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + bool bExtrudeBoundaries, + unsigned int& list_vertex_count, + unsigned int& list_edge_count, + unsigned int& list_face_count +) +{ + const bool bExtrusionMarking = true; + const int marked_vertex_count = Internal_MarkStuffAndMaybeMoveVertices( + subd, + cptr_list, + cptr_count, + nullptr, + ON_SubDComponentLocation::Unset, + bExtrusionMarking, + bExtrudeBoundaries, + list_vertex_count, + list_edge_count, + list_face_count + ); + + // It appears the best "hurestic" is to require the user to pick edges and faces. + // isolated vertices will be ignored and no attempts to guess if a user wants to + // extrude the boundary of a face (all its edges) or the face itself. + + // lots of delete "hurestic" code here :) + + return marked_vertex_count; +} + +static unsigned int Internal_TransformComponents( + const ON_SubD& subd, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + const ON_Xform& xform, + ON_SubDComponentLocation component_location +) +{ + // This version is used by SDK tools that simply transform components in cptr_list + if (false == xform.IsValidAndNotZeroAndNotIdentity()) + return 0; + + const bool bExtrusionMarking = false; + unsigned int list_vertex_count = 0; + unsigned int list_edge_count = 0; + unsigned int list_face_count = 0; + + return Internal_MarkStuffAndMaybeMoveVertices( + subd, + cptr_list, + cptr_count, + &xform, + component_location, + bExtrusionMarking, false, + list_vertex_count, + list_edge_count, + list_face_count + ); } unsigned int ON_SubD::TransformComponents( const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, - size_t cptr_count + size_t cptr_count, + ON_SubDComponentLocation component_location ) { if ( @@ -10425,7 +12028,7 @@ unsigned int ON_SubD::TransformComponents( ON_SimpleArray marked_components; const bool bRestoreMarks = ClearComponentMarks(true, true, true, &marked_components) > 0; - const unsigned int v_count = Internal_MarkVertices(*this, cptr_list, cptr_count, xform); + const unsigned int v_count = Internal_TransformComponents(*this, cptr_list, cptr_count, xform, component_location); if (v_count > 0) { @@ -10438,10 +12041,25 @@ unsigned int ON_SubD::TransformComponents( return (v_count > 0); } + +unsigned int ON_SubD::ExtrudeComponents( + const ON_Xform& xform, + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count +) +{ + const bool bExtrudeBoundaries = true; + const bool bPermitNonManifoldEdgeCreation = false; + const ON_SubD::EdgeTag original_edge_tag = ON_SubD::EdgeTag::Unset; + const ON_SubD::EdgeTag moved_edge_tag = ON_SubD::EdgeTag::Unset; + return ExtrudeComponents(xform, ci_list, ci_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation, original_edge_tag, moved_edge_tag); +} + unsigned int ON_SubD::ExtrudeComponents( const ON_Xform& xform, const ON_COMPONENT_INDEX* ci_list, size_t ci_count, + bool bExtrudeBoundaries, bool bPermitNonManifoldEdgeCreation, ON_SubD::EdgeTag original_edge_tag, ON_SubD::EdgeTag moved_edge_tag @@ -10463,6 +12081,7 @@ unsigned int ON_SubD::ExtrudeComponents( xform, cptr_list.Array(), cptr_list.UnsignedCount(), + bExtrudeBoundaries, bPermitNonManifoldEdgeCreation, original_edge_tag, moved_edge_tag @@ -10507,6 +12126,62 @@ public: static const ON_Internal_ExtrudedSide Unset; + bool m_bExtrudedBoundaryEdge = false; + bool m_bHasMovedFace = false; // true if the edge touches a fae that will be moved. + bool m_bHasStationaryFace = false; // true if the edge touches a face that will remain stationary. + ON_SubD::EdgeTag m_original_marked_edge_tag = ON_SubD::EdgeTag::Unset; + + ON_SubD::EdgeTag MarkedEdgeTag() const + { + // this tag is calculated just before the side face is made + if ( m_bExtrudedBoundaryEdge ) + { + const ON_SubDVertex* v0 = nullptr != m_marked_edge ? m_marked_edge->m_vertex[0] : nullptr; + const ON_SubDVertex* v1 = nullptr != m_marked_edge ? m_marked_edge->m_vertex[1] : nullptr; + if ( + m_bHasMovedFace + && false == m_bHasStationaryFace + && nullptr != v0 + && nullptr != v1 + ) + { + return + (v0->IsDartOrCreaseOrCorner() && v1->IsDartOrCreaseOrCorner()) + ? ON_SubD::EdgeTag::SmoothX + : ON_SubD::EdgeTag::Smooth; + } + } + + return m_original_marked_edge_tag; + } + + ON_SubD::EdgeTag UnmarkedEdgeTag( + const ON_SubDVertex* v0, + const ON_SubDVertex* v1 + ) const + { + // This tag is calculate before m_unmarked_edge is created. + if ( + m_bExtrudedBoundaryEdge + && m_bHasStationaryFace + && false == m_bHasMovedFace + && nullptr != v0 + && nullptr != v1 + ) + { + return + (v0->IsDartOrCreaseOrCorner() && v1->IsDartOrCreaseOrCorner()) + ? ON_SubD::EdgeTag::SmoothX + : ON_SubD::EdgeTag::Smooth; + } + + return + (ON_SubD::EdgeTag::Crease == m_original_marked_edge_tag) + ? ON_SubD::EdgeTag::Crease + : ON_SubD::EdgeTag::Unset + ; + } + // the marked edge was in the original object and will be moved. ON_SubDEdge* m_marked_edge = nullptr; @@ -10552,8 +12227,8 @@ static ON_SubD::EdgeTag Internal_AdjustedEdgeTag(const ON_SubDEdge* edge) return ON_SubD::EdgeTag::Smooth; const ON_SubD::EdgeTag etag = edge->m_edge_tag; - if (ON_SubD::EdgeTag::Smooth == etag || ON_SubD::EdgeTag::X == etag ) - return ON_SubD::EdgeTag::X; + if (ON_SubD::EdgeTag::Smooth == etag || ON_SubD::EdgeTag::SmoothX == etag ) + return ON_SubD::EdgeTag::SmoothX; return etag; } @@ -10680,55 +12355,127 @@ static ON_SubDFace* Internal_AddNewFace( new_face_eptr[1] = ON_SubDEdgePtr::Create(side0, 1); new_face_eptr[2] = ON_SubDEdgePtr::Create(side.m_unmarked_edge, edir); new_face_eptr[3] = ON_SubDEdgePtr::Create(side1, 0); + + const ON_SubD::EdgeTag marked_edge_tag = side.MarkedEdgeTag(); + if (marked_edge_tag != marked_edge->m_edge_tag) + marked_edge->m_edge_tag = marked_edge_tag; - side.m_new_face = subd.AddFace(4, new_face_eptr); + side.m_new_face = subd.AddFace(new_face_eptr, 4); + + if (nullptr != side.m_new_face) + { + // When isolated edges are extruded, we need to flip the face. + // In all other cases, we don't. + bool bFlip = false; + bool bFlipSet = false; + for (unsigned int fei = 0; fei < 4; fei++) + { + const ON_SubDEdgePtr eptr = side.m_new_face->m_edge4[fei]; + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr.m_ptr); + if (nullptr == e || e->m_face_count > 2) + { + bFlipSet = false; + break; + } + if (2 != e->m_face_count) + continue; + const ON__UINT_PTR fedir = ON_SUBD_EDGE_DIRECTION(eptr.m_ptr); + const ON_SubDFacePtr fptr[2] = { e->m_face2[0], e->m_face2[1] }; + const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(fptr[0].m_ptr), ON_SUBD_FACE_POINTER(fptr[1].m_ptr) }; + if (nullptr == f[0] || nullptr == f[1] || f[0] == f[1]) + { + bFlipSet = false; + break; + } + if (side.m_new_face != f[0] && side.m_new_face != f[1] ) + { + bFlipSet = false; + break; + } + const ON__UINT_PTR fdir[2] = { ON_SUBD_FACE_DIRECTION(fptr[0].m_ptr), ON_SUBD_FACE_DIRECTION(fptr[1].m_ptr) }; + if (fedir != fdir[(f[0] == side.m_new_face) ? 0 : 1]) + { + bFlipSet = false; + break; + } + const bool bSameDir = (fdir[0] == fdir[1]) ? true : false; + if (false == bFlipSet) + { + bFlipSet = true; + bFlip = bSameDir; + } + else if (bSameDir != bFlip) + { + bFlipSet = false; + break; + } + } + if (bFlip) + side.m_new_face->ReverseEdgeList(); + } return side.m_new_face; } static ON_SubD::EdgeTag Internal_ConnectingEdgeTagAtVertex( + bool bExtrudeBoundaries, const ON_SubDVertex* v, ON_SubD::VertexTag& moved_vertex_tag, ON_SubD::VertexTag& stationary_vertex_tag ) { - moved_vertex_tag = v->m_vertex_tag; - stationary_vertex_tag = v->m_vertex_tag; + moved_vertex_tag = ON_SubD::VertexTag::Unset; + stationary_vertex_tag = ON_SubD::VertexTag::Unset; if (ON_SubD::VertexTag::Crease != v->m_vertex_tag && ON_SubD::VertexTag::Dart != v->m_vertex_tag) { ON_SUBD_ERROR("This function requires a crease or dart vertex as input."); return ON_SubD::EdgeTag::Unset; } - unsigned int expected_crease_count = ON_UNSET_UINT_INDEX; - switch (v->m_vertex_tag) - { - case ON_SubD::VertexTag::Smooth: - expected_crease_count = 0; - break; - case ON_SubD::VertexTag::Dart: - expected_crease_count = 1; - break; - case ON_SubD::VertexTag::Crease: - expected_crease_count = 2; - break; - case ON_SubD::VertexTag::Corner: - break; - default: - break; - } - -/* - - = (ON_SubD::VertexTag::Dart == v->m_vertex_tag) - ? 1 - : ((ON_SubD::VertexTag::CDart == v->m_vertex_tag) ? 2 : 0; -*/ - const unsigned int vertex_edge_count = v->m_edge_count; - unsigned int sep_crease_count = 0; - unsigned int moved_crease_count = 0; - unsigned int stationary_crease_count = 0; + + // total_count = number of edges currently attached to v that are creases + unsigned int total_count = 0; + + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // + // stationary, moved_to_crease, moved_to_smooth, and split are mutually exclusive sitations. + + // stationary_count = number of edges currently attached to v that are creases and remain in current locations + unsigned int stationary_count = 0; + + // moved_to_crease_count = number of edges currently attached to v that are creases and move and remain creases + unsigned int moved_to_crease_count = 0; + + // moved_to_crease_count = number of edges currently attached to v that are creases and move and become smooth + unsigned int moved_to_smooth_count = 0; + + // split_count = number of edges currently attached to v that are creases and are between a moved and stationary face + unsigned int split_count = 0; + + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // + // bdry, interior, wire, nonman are mutually exclusive sitations. + + // bdry_count = number of edges currently attached to v that are creases and have 1 face. + unsigned int bdry_count = 0; + + // interior_count = number of edges currently attached to v that are creases and have 2 faces + unsigned int interior_count = 0; + + // wire_count = number of edges currently attached to v that are creases and have 0 faces + unsigned int wire_count = 0; + + // nonman_count = number of edges currently attached to v that are creases and have 3 or more faces + unsigned int nonman_count = 0; + + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // + // tally up what is happening at each crease edge attached to vertex v + // for (unsigned int vei = 0; vei < vertex_edge_count; vei++) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr); @@ -10738,113 +12485,291 @@ static ON_SubD::EdgeTag Internal_ConnectingEdgeTagAtVertex( if (ON_SubD::EdgeTag::Crease != e->m_edge_tag) continue; - if (e->m_status.RuntimeMark()) - { - sep_crease_count++; - continue; - } + const bool bBoundaryEdge = (1 == e->m_face_count) && (nullptr != e->m_face2[0].Face()); + const bool bMovedBoundaryEdge = bBoundaryEdge && e->m_status.RuntimeMark(); + //const bool bStationaryBoundaryEdge = bBoundaryEdge && false == e->m_status.RuntimeMark(); - bool bThisEdgeMoves = false; + ++total_count; + + if (0 == e->m_face_count) + ++wire_count; + else if (1 == e->m_face_count) + ++bdry_count; + else if (2 == e->m_face_count) + ++interior_count; + else if (2 == e->m_face_count) + ++nonman_count; + + unsigned int moved_face_count = 0; + unsigned int stationary_face_count = 0; for (unsigned short evi = 0; evi < e->m_face_count; evi++) { const ON_SubDFace* f = e->Face(evi); + if (nullptr == f) + { + ON_SUBD_ERROR("Edge has null face."); + return ON_SubD::EdgeTag::Unset; + } if (f->m_status.RuntimeMark()) - { - bThisEdgeMoves = true; - break; - } + ++moved_face_count; + else + ++stationary_face_count; + } + + if (moved_face_count > 0 && stationary_face_count > 0) + { + // This is edge is between a moved face and a stationary face. + // It will "split" into two edges + ++split_count; + } + else if (moved_face_count > 0) + { + // This edge and all faced currently attached to it are moving. + if (bExtrudeBoundaries && bMovedBoundaryEdge) + ++moved_to_smooth_count; + else + ++moved_to_crease_count; + } + else if (stationary_face_count > 0) + { + // This edge is moving and all faced currently attached to it are stationary. + // A new face will be attached to this edge + if (bExtrudeBoundaries && bMovedBoundaryEdge) + { + // This boundary edge will move and a new face will be createed between it + // and the face the edge is currently attached to. + ++moved_to_crease_count; + } + else + ++stationary_count; // this crease (interior or boundary) and its attached face or faces do not move } - if (bThisEdgeMoves) - ++moved_crease_count; - else - ++stationary_crease_count; } - - if (ON_SubD::VertexTag::Corner == v->m_vertex_tag && (sep_crease_count + moved_crease_count + stationary_crease_count) >= 2) - expected_crease_count = (sep_crease_count + moved_crease_count + stationary_crease_count); - - if (expected_crease_count != (sep_crease_count + moved_crease_count + stationary_crease_count)) - ON_SUBD_ERROR("Unexpected tags - input is probably invalid."); - - ON_SubD::EdgeTag connecting_edge_tag; - if (0 == stationary_crease_count || 0 == moved_crease_count) + if (total_count != wire_count + split_count + moved_to_smooth_count + moved_to_crease_count + stationary_count) { - if (sep_crease_count >= 2 && ON_SubD::VertexTag::Corner == v->m_vertex_tag) - { - stationary_vertex_tag = ON_SubD::VertexTag::Corner; - moved_vertex_tag = ON_SubD::VertexTag::Corner; - } - else - { - switch (sep_crease_count + stationary_crease_count) - { - case 0: - stationary_vertex_tag = ON_SubD::VertexTag::Smooth; - break; - case 1: - stationary_vertex_tag = ON_SubD::VertexTag::Dart; - break; - case 2: - stationary_vertex_tag = ON_SubD::VertexTag::Crease; - break; - default: - stationary_vertex_tag = ON_SubD::VertexTag::Corner; - break; - } + ON_SUBD_ERROR("Bug in counting code above or invalid topology near this vertex."); + return ON_SubD::EdgeTag::Unset; + } - switch (sep_crease_count + moved_crease_count) - { - case 0: - moved_vertex_tag = ON_SubD::VertexTag::Smooth; - break; - case 1: - moved_vertex_tag = ON_SubD::VertexTag::Dart; - break; - case 2: - moved_vertex_tag = ON_SubD::VertexTag::Crease; - break; - default: - moved_vertex_tag = ON_SubD::VertexTag::Corner; - break; - } - } - - if ( - (0 == stationary_crease_count && 0 == moved_crease_count) - || ON_SubD::VertexTag::Smooth == stationary_vertex_tag - || ON_SubD::VertexTag::Dart == stationary_vertex_tag - || ON_SubD::VertexTag::Smooth == moved_vertex_tag - || ON_SubD::VertexTag::Dart == moved_vertex_tag + if (ON_SubD::VertexTag::Dart == v->m_vertex_tag) + { + if ( + 1 == total_count + && 1 == interior_count + && 0 == bdry_count + && 0 == wire_count + && 0 == nonman_count ) - connecting_edge_tag = ON_SubD::EdgeTag::Smooth; - else - connecting_edge_tag = ON_SubD::EdgeTag::Crease; + { + if (0 == split_count) + { + if (1 == moved_to_crease_count && 0 == moved_to_smooth_count && 0 == stationary_count) + { + // dart crease and both attached faces are moving + moved_vertex_tag = ON_SubD::VertexTag::Dart; + stationary_vertex_tag = ON_SubD::VertexTag::Smooth; + return ON_SubD::EdgeTag::Smooth; + } + else if (0 == moved_to_crease_count && 0 == moved_to_smooth_count && 1 == stationary_count) + { + // dart crease and both attached faces are stationary + moved_vertex_tag = ON_SubD::VertexTag::Smooth; + stationary_vertex_tag = ON_SubD::VertexTag::Dart; + return ON_SubD::EdgeTag::Smooth; + } + } + else if (1 == split_count) + { + // Along the dart crease, one attached face is moving and the other is stationary. + // The dart crease will get split into two darts and the new edge will be smooth. + moved_vertex_tag = ON_SubD::VertexTag::Dart; + stationary_vertex_tag = ON_SubD::VertexTag::Dart; + return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision + } + } + ON_SUBD_ERROR("Unexpected dart vertex edge tags - bug in counting code above or current tags or topology are not invalid."); + return ON_SubD::EdgeTag::Unset; } - else + + + ///////////////////////////////////////////////////////////////////////////////////////////// + // + // The rest of this function is for the case when ON_SubD::VertexTag::Crease == v->m_vertex_tag + // + if (1 == wire_count) { - // Corner "upgrade" check is below. - stationary_vertex_tag = ON_SubD::VertexTag::Crease; moved_vertex_tag = ON_SubD::VertexTag::Crease; - connecting_edge_tag = ON_SubD::EdgeTag::Crease; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::Crease; } - - - if (ON_SubD::EdgeTag::Smooth == connecting_edge_tag) + if (2 == wire_count) { - if (ON_SubD::VertexTag::Smooth != stationary_vertex_tag && ON_SubD::VertexTag::Smooth!= moved_vertex_tag) - connecting_edge_tag = ON_SubD::EdgeTag::X; // at level 0, both ends are tagged. - } - else - { - // connecting edge is crease - if (sep_crease_count + stationary_crease_count + 1 > 2) - stationary_vertex_tag = ON_SubD::VertexTag::Corner; - if (sep_crease_count + moved_crease_count + 1 > 2) - moved_vertex_tag = ON_SubD::VertexTag::Corner; + moved_vertex_tag = ON_SubD::VertexTag::Crease; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision } - return connecting_edge_tag; + if (0 != wire_count || 0 != nonman_count) + { + // If extrusions involving nonmanifold regions need to be supported, lots of changes are required. + ON_SUBD_ERROR("Currently, non-manifold cases are not supported."); + return ON_SubD::EdgeTag::Unset; + } + + for(;;) + { + if ( + ON_SubD::VertexTag::Crease == v->m_vertex_tag + && 2 == (split_count + moved_to_crease_count + moved_to_smooth_count + stationary_count) + ) + { + if (2 == interior_count && 0 == bdry_count) + break; + if (0 == interior_count && 2 == bdry_count) + break; + } + ON_SUBD_ERROR("Unexpected crease vertex edge tags - bug in counting code above or current tags or topology are not invalid."); + return ON_SubD::EdgeTag::Unset; + } + + if (0 == interior_count && 2 == bdry_count) + { + // The vertex v is currently a boundary crease vertex and split_count always 0 in this case. + // + // Since 2 = split_count + stationary_count + moved_to_crease_count + moved_to_smooth_count, + // stationary_count = 0,1,2 is used as the next "case reduction" filter. + + if (0 != split_count || 2 != (stationary_count + moved_to_crease_count + moved_to_smooth_count) ) + { + ON_SUBD_ERROR("Bug in boundary crease case counting code above."); + return ON_SubD::EdgeTag::Unset; + } + + + if (0 == stationary_count) + { + // both attached crease edges are moving + // 3 possibilities (moved_to_crease_count,moved_to_smooth_count) = (2,0), (0,2), or (1,1) + if (2 == moved_to_crease_count ) + { + moved_vertex_tag = ON_SubD::VertexTag::Crease; + stationary_vertex_tag = ON_SubD::VertexTag::Smooth; + return ON_SubD::EdgeTag::Smooth; + } + + if (2 == moved_to_smooth_count ) + { + // attached faces are moving and moved edges will be copied. + // The moved edges will convert from crease to smooth and the stationary copies will become creases. + // New faces will be created between the moved edge and the stationary copy. + moved_vertex_tag = ON_SubD::VertexTag::Smooth; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::Smooth; + } + + if ( 1 == moved_to_crease_count && 1 == moved_to_smooth_count ) + { + // new edge runs from moved crease to crease that replaces moved to smooth edge + moved_vertex_tag = ON_SubD::VertexTag::Crease; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::Crease; + } + } + else if (1 == stationary_count) + { + // 1 attached crease edge is moving and the other attached crease edge is stationary + // 2 possibilities (moved_to_crease_count,moved_to_smooth_count) = (1,0) or (0,1) + if (1 == moved_to_crease_count && 0 == moved_to_smooth_count) + { + // moved edge continues to be a boundary (and crease) edge. + // The new edge connecting the stationary crease and the moved edge is a crease. + moved_vertex_tag = ON_SubD::VertexTag::Crease; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::Crease; + } + if (0 == moved_to_crease_count && 1 == moved_to_smooth_count) + { + // moved crease becomes interior smooth edge + // The new edge connecting the stationary crease and the moved edge is a crease. + moved_vertex_tag = ON_SubD::VertexTag::Smooth; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::Smooth; + } + } + else if (2 == stationary_count) + { + // neither attached crease edge is moving + // 1 possibility (moved_to_crease_count,moved_to_smooth_count) = (0,0) + moved_vertex_tag = ON_SubD::VertexTag::Smooth; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::Smooth; + } + } + else if (2 == interior_count && 0 == bdry_count) + { + // The vertex v is currently an interior crease vertex and moved_to_smooth_count always 0 in this case. + // (move_to_smooth_count>0 requires a boundary edge) + // + // Since 2 = split_count + stationary_count + moved_to_crease_count + moved_to_smooth_count, + // split_count = 0,1,2 is used as the next "case reduction" filter. + + if (0 != moved_to_smooth_count || 2 != (split_count + stationary_count + moved_to_crease_count) ) + { + ON_SUBD_ERROR("Bug in interior crease case counting code above."); + return ON_SubD::EdgeTag::Unset; + } + + if (0 == split_count) + { + // stationary_count can be 0, 1 or 2. + if (0 == stationary_count) + { + // both creases and all attached faces are moving + moved_vertex_tag = ON_SubD::VertexTag::Crease; + stationary_vertex_tag = ON_SubD::VertexTag::Smooth; + return ON_SubD::EdgeTag::Smooth; + } + else if (1 == stationary_count) + { + // TODO + } + else if (2 == stationary_count) + { + // both creases and all attached faces are stationary + moved_vertex_tag = ON_SubD::VertexTag::Smooth; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::Smooth; + } + } + else if (1 == split_count) + { + // stationary_count can be 0 or 1 + if (0 == stationary_count) + { + // 1 = moved_to_crease_count, 0 = moved_to_smooth_count. + moved_vertex_tag = ON_SubD::VertexTag::Dart; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision + } + else if (1 == stationary_count) + { + // moved_to_crease_count = 0 and moved_to_smooth_count = 0 + moved_vertex_tag = ON_SubD::VertexTag::Dart; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision + } + } + else if (2 == split_count) + { + // stationary_count = 0, moved_to_crease_count = 0, and moved_to_smooth_count = 0 + moved_vertex_tag = ON_SubD::VertexTag::Crease; + stationary_vertex_tag = ON_SubD::VertexTag::Crease; + return ON_SubD::EdgeTag::SmoothX; // will X becomes Smooth on 1st subdivision + } + } + + ON_SUBD_ERROR("Unexpected crease vertex edge tags - bug in counting code above or current tags or topology are not invalid."); + return ON_SubD::EdgeTag::Unset; } static bool Internal_NonManifoldEdgeWillBeCreated( const ON_SubDVertex* v ) @@ -10875,22 +12800,87 @@ static bool Internal_NonManifoldEdgeWillBeCreated( const ON_SubDVertex* v ) return false; } +unsigned int ON_SubD::ExtrudeComponents( + const ON_Xform& xform, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count +) +{ + const bool bExtrudeBoundaries = true; + const bool bPermitNonManifoldEdgeCreation = false; + const ON_SubD::EdgeTag original_edge_tag = ON_SubD::EdgeTag::Unset; + const ON_SubD::EdgeTag moved_edge_tag = ON_SubD::EdgeTag::Unset; + return ExtrudeComponents(xform, cptr_list, cptr_count, bExtrudeBoundaries, bPermitNonManifoldEdgeCreation, original_edge_tag, moved_edge_tag); +} + unsigned int ON_SubD::ExtrudeComponents( const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, + bool bExtrudeBoundaries, bool bPermitNonManifoldEdgeCreation, ON_SubD::EdgeTag original_edge_tag, ON_SubD::EdgeTag moved_edge_tag ) { + if (nullptr == cptr_list || cptr_count <= 0) + return 0; + return Internal_ExtrudeComponents( + xform, + cptr_list, + cptr_count, + bExtrudeBoundaries, + bPermitNonManifoldEdgeCreation, + original_edge_tag, + moved_edge_tag + ); +} + +unsigned int ON_SubD::Extrude(const ON_Xform & xform) +{ + if (IsSolid()) + return 0; + const bool bExtrudeBoundaries = true; + const bool bPermitNonManifoldEdgeCreation = false; + return Internal_ExtrudeComponents( + xform, + nullptr, + 0, + bExtrudeBoundaries, + bPermitNonManifoldEdgeCreation, + ON_SubD::EdgeTag::Unset, ON_SubD::EdgeTag::Unset + ); +} + +unsigned int ON_SubD::Internal_ExtrudeComponents( + const ON_Xform& xform, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + bool bExtrudeBoundaries, + bool bPermitNonManifoldEdgeCreation, + ON_SubD::EdgeTag original_edge_tag, + ON_SubD::EdgeTag moved_edge_tag +) +{ + const bool bHaveCptrList = (cptr_count > 0 && nullptr != cptr_list); + bool bExtrudeAll = false; + if (false == bHaveCptrList && 0 == cptr_count) + { + bool bIsManifold = false; + bool bIsOriented = false; + bool bHasBoundary = false; + int solid_orientation = 0; + ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation); + bExtrudeAll = bHasBoundary; + } if ( false == xform.IsValidAndNotZeroAndNotIdentity() - || cptr_count <= 0 - || nullptr == cptr_list + || (false == bHaveCptrList && false == bExtrudeAll) ) return 0; + // The extrusion is initially calculated as if bot original_edge_tag and moved_edge_tag + // are Unset. A post process (will be added later to) sets the tags as specified if (ON_SubD::EdgeTag::Crease != original_edge_tag && ON_SubD::EdgeTag::Smooth != original_edge_tag) original_edge_tag = ON_SubD::EdgeTag::Unset; @@ -10901,61 +12891,58 @@ unsigned int ON_SubD::ExtrudeComponents( // Marks very vertex touching a component in the cptr_list. // Skips applying the transform because it is the identity. - const unsigned int v_count = Internal_MarkVertices(*this, cptr_list, cptr_count, ON_Xform::IdentityTransformation); - - if (false == bPermitNonManifoldEdgeCreation) - { - } - - unsigned int f_count = 0; + + unsigned int list_vertex_count = 0; + unsigned int list_edge_count = 0; + unsigned int list_face_count = 0; + const unsigned int marked_vertex_count = Internal_MarkExtrudeComponents( + *this, + cptr_list, cptr_count, + bExtrudeBoundaries, + list_vertex_count, list_edge_count, list_face_count + ); + + unsigned int moved_face_count = 0; // moved face count + unsigned int new_face_count = 0; // number of new faces on moved boundary for (;;) { - if (0 == v_count) + if (0 == marked_vertex_count) break; - // Mark the faces that will be moved. - ON_SimpleArray marked_faces(128); + // Set moved_faces[] = list of faces that will move. + ON_SimpleArray moved_faces(list_face_count + 128); ON_SubDFaceIterator fit(*this); for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) { const unsigned int face_vertex_count = f->m_edge_count; - if (face_vertex_count < 3) + if (face_vertex_count < 3 || false == f->m_status.RuntimeMark()) continue; - for ( unsigned int fvi = 0; fvi < face_vertex_count; fvi++) - { - const ON_SubDVertex* v = f->Vertex(fvi); - if (nullptr != v && v->m_status.RuntimeMark()) - continue; - f = nullptr; - break; - } - if (nullptr != f) - { - f->m_status.SetRuntimeMark(); - marked_faces.Append(f); - } + moved_faces.Append(f); } - f_count = marked_faces.UnsignedCount(); - - if (0 == f_count) - { - // No faces are moving. - break; - } - - if (f_count == FaceCount()) - { - // Every face is moving. - Transform(xform); - break; - } + moved_face_count = moved_faces.UnsignedCount(); // Mark edges on the boundary of the moved subset. + + // Set moved_edges[] = list of edges that are either + // 1) between a moved face and a stationary face + // 2) are a boundary edge to be extruded. + ON_SimpleArray moved_edges(list_edge_count + list_vertex_count); + ON_SimpleArray new_sides(64); ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { + const bool bExtrudedBoundaryEdge + = bExtrudeBoundaries + && e->m_status.RuntimeMark() + && 1 == e->m_face_count + && ON_SubD::EdgeTag::Crease == e->m_edge_tag + ; + bool bExtrudeEdge = bExtrudedBoundaryEdge; + + e->m_status.ClearRuntimeMark(); + bool bMarkedFace = false; bool bUnmarkedFace = false; const unsigned int edge_face_count = e->m_face_count; @@ -10972,48 +12959,82 @@ unsigned int ON_SubD::ExtrudeComponents( if (bMarkedFace && bUnmarkedFace) { // e is on the boundary between the subset of moved faces and stationary faces. - e->m_status.SetRuntimeMark(); - new_sides.AppendNew().m_marked_edge = const_cast(e); + bExtrudeEdge = true; break; } } + + if (bExtrudeEdge) + { + if (false == bMarkedFace) + moved_edges.Append(e); // e is not part of a moved face's boundary + e->m_status.SetRuntimeMark(); + ON_Internal_ExtrudedSide& newside = new_sides.AppendNew(); + newside.m_marked_edge = const_cast(e); + newside.m_bExtrudedBoundaryEdge = bExtrudedBoundaryEdge; + newside.m_bHasMovedFace = bMarkedFace; + newside.m_bHasStationaryFace = bUnmarkedFace; + newside.m_original_marked_edge_tag = e->m_edge_tag; + } } - const unsigned int e_count = new_sides.UnsignedCount(); - if (e_count <= 0) + new_face_count = new_sides.UnsignedCount(); + + if (0 == new_face_count) { - // no edges between moved and stationary faces - Transform(xform); + // no new faces will be made + if (moved_face_count > 0) + { + // Every face is moving + Transform(xform); + } break; } if (false == bPermitNonManifoldEdgeCreation) { - bool bPermitNonManifoldEdgeWillBeCreated = false; - for (unsigned int fi = 0; fi < f_count; fi++) + bool bNonManifoldEdgeWillBeCreated = false; + for (unsigned int fi = 0; fi < moved_face_count; fi++) { - const ON_SubDFace* f = marked_faces[fi]; + const ON_SubDFace* f = moved_faces[fi]; const unsigned int face_vertex_count = f->m_edge_count; for (unsigned int fvi = 0; fvi < face_vertex_count; fvi++) { - bPermitNonManifoldEdgeWillBeCreated = Internal_NonManifoldEdgeWillBeCreated(f->Vertex(fvi)); - if (bPermitNonManifoldEdgeWillBeCreated) + bNonManifoldEdgeWillBeCreated = Internal_NonManifoldEdgeWillBeCreated(f->Vertex(fvi)); + if (bNonManifoldEdgeWillBeCreated) break; } - if (bPermitNonManifoldEdgeWillBeCreated) + if (bNonManifoldEdgeWillBeCreated) break; } - if (bPermitNonManifoldEdgeWillBeCreated) - break; + if (bNonManifoldEdgeWillBeCreated) + break; // not allowd to create non-manifold edges + + for (unsigned int ei = 0; ei < moved_edges.UnsignedCount(); ei++) + { + const ON_SubDEdge* e = moved_edges[ei]; + for (unsigned int evi = 0; evi < 2; evi++) + { + bNonManifoldEdgeWillBeCreated = Internal_NonManifoldEdgeWillBeCreated(e->m_vertex[evi]); + if (bNonManifoldEdgeWillBeCreated) + break; + } + if (bNonManifoldEdgeWillBeCreated) + break; + } + + if (bNonManifoldEdgeWillBeCreated) + break; // not allowd to create non-manifold edges } // clear vertex marks. ClearComponentMarks(true, false, false, nullptr); - // Duplicate vertices that are on a edge between a marked and unmarked face. - ON_SimpleArray vertex_pairs(e_count+8); - for (unsigned int i = 0; i < e_count; i++) + // Duplicate vertices that are on an edge between a marked and unmarked face + // or a moved boundary edge + ON_SimpleArray vertex_pairs(new_face_count+8); + for (unsigned int i = 0; i < new_face_count; i++) { const ON_SubDEdge* e = new_sides[i].m_marked_edge; for (unsigned int evi = 0; evi < 2; evi++) @@ -11029,16 +13050,16 @@ unsigned int ON_SubD::ExtrudeComponents( ON_Internal_ExtrudedVertexPair& vpair = vertex_pairs.AppendNew(); vpair.m_marked_vertex = v; - ON_SubD::VertexTag moved_vertex_tag; // the original vertex gets moved - ON_SubD::VertexTag stationary_vertex_tag; // (this one get allocated) - ON_SubD::EdgeTag connecting_edge_tag; // from stationary to moved vertex + ON_SubD::VertexTag moved_vertex_tag = ON_SubD::VertexTag::Unset; // the original vertex gets moved + ON_SubD::VertexTag stationary_vertex_tag = ON_SubD::VertexTag::Unset; // (this one get allocated) + ON_SubD::EdgeTag connecting_edge_tag = ON_SubD::EdgeTag::Unset; // from stationary to moved vertex switch (v->m_vertex_tag) { case ON_SubD::VertexTag::Dart: - connecting_edge_tag = Internal_ConnectingEdgeTagAtVertex(v, moved_vertex_tag, stationary_vertex_tag); + connecting_edge_tag = Internal_ConnectingEdgeTagAtVertex(bExtrudeBoundaries, v, moved_vertex_tag, stationary_vertex_tag); break; case ON_SubD::VertexTag::Crease: - connecting_edge_tag = Internal_ConnectingEdgeTagAtVertex(v, moved_vertex_tag, stationary_vertex_tag); + connecting_edge_tag = Internal_ConnectingEdgeTagAtVertex(bExtrudeBoundaries, v, moved_vertex_tag, stationary_vertex_tag); break; case ON_SubD::VertexTag::Corner: moved_vertex_tag = v->m_vertex_tag; @@ -11100,7 +13121,7 @@ unsigned int ON_SubD::ExtrudeComponents( // build new side edges ON_Internal_ExtrudedVertexPair key[2]; - for (unsigned int i = 0; i < e_count; i++) + for (unsigned int i = 0; i < new_face_count; i++) { const ON_SubDEdge* e = new_sides[i].m_marked_edge; for (unsigned int evi = 0; evi < 2; evi++) @@ -11118,11 +13139,8 @@ unsigned int ON_SubD::ExtrudeComponents( key[evi] = vertex_pairs[i0]; } - const ON_SubD::EdgeTag unmoved_edge_tag - = (ON_SubD::EdgeTag::Crease == e->m_edge_tag) - ? ON_SubD::EdgeTag::Crease - : ON_SubD::EdgeTag::Unset; - new_sides[i].m_unmarked_edge = this->AddEdge(unmoved_edge_tag, key[0].m_unmarked_vertex, key[1].m_unmarked_vertex); + const ON_SubD::EdgeTag unmarked_edge_tag = new_sides[i].UnmarkedEdgeTag(key[0].m_unmarked_vertex, key[1].m_unmarked_vertex); + new_sides[i].m_unmarked_edge = this->AddEdge(unmarked_edge_tag, key[0].m_unmarked_vertex, key[1].m_unmarked_vertex); new_sides[i].m_new_side0 = key[0].m_new_side; new_sides[i].m_new_side1 = key[1].m_new_side; } @@ -11130,11 +13148,11 @@ unsigned int ON_SubD::ExtrudeComponents( // Mark everything a moved face touches // including interior edges and vertices. // Transform any vertices that are not already marked. - for (unsigned int i = 0; i < f_count; i++) + for (unsigned int i = 0; i < moved_face_count; i++) { - const ON_SubDFace* f = marked_faces[i]; + const ON_SubDFace* f = moved_faces[i]; const unsigned int face_edge_count = f->m_edge_count; - for (unsigned int fei = 0; fei < face_edge_count; fei++) + for (unsigned int fei = 0; fei < face_edge_count; ++fei) { const ON_SubDEdge* e = f->Edge(fei); if (nullptr == e) @@ -11152,6 +13170,27 @@ unsigned int ON_SubD::ExtrudeComponents( } } + // Mark everything vertex a moved edge touches + // Transform any vertices that are not already marked. + for (unsigned int i = 0; i < moved_edges.UnsignedCount(); i++) + { + const ON_SubDEdge* e = moved_edges[i]; + const unsigned int edge_face_count = e->m_face_count; + for (unsigned int efi = 0; efi < edge_face_count; ++efi) + { + e->m_status.SetRuntimeMark(); + for (unsigned int evi = 0; evi < 2; evi++) + { + ON_SubDVertex* v = const_cast(e->m_vertex[evi]); + if (nullptr !=v && false == v->m_status.RuntimeMark()) + { + v->Transform(false, xform); + v->m_status.SetRuntimeMark(); + } + } + } + } + // For the original boundary vertrex, move unmarked edges to use the new vertex. for (unsigned int i = 0; i < vertex_pairs.UnsignedCount(); i++) { @@ -11159,7 +13198,7 @@ unsigned int ON_SubD::ExtrudeComponents( } // build new side faces - for (unsigned int i = 0; i < e_count; i++) + for (unsigned int i = 0; i < new_face_count; i++) { Internal_AddNewFace(*this, new_sides[i]); } @@ -11199,7 +13238,15 @@ unsigned int ON_SubD::ExtrudeComponents( IsValid(); #endif - return f_count; + // TODO - run through new_sides[] array and attempt to adjust tags + //if (ON_SubD::EdgeTag::Unset != original_edge_tag || ON_SubD::EdgeTag::Unset != moved_edge_tag) + //{ + // + //} + + + // number of moved faces and new boundary faces + return moved_face_count + new_face_count; } unsigned int ON_SubD::SetVertexTags( @@ -11270,37 +13317,39 @@ unsigned int ON_SubD::SetVertexTags( if (nullptr == vertex->m_edges || vertex->m_edge_count < 2) continue; - unsigned short min_face_count = 0xFFFFU; - unsigned short max_face_count = 0xFFFEU; - for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++) + const ON_SubDVertexEdgeProperties ep = vertex->EdgeProperties(); + + if (ep.m_nonmanifold_edge_count > 0) + continue; // nonmanifold edge + + if (ep.m_boundary_edge_count + ep.m_wire_edge_count > 2) + continue; + + if (ON_SubD::VertexTag::Smooth == vertex_tag) { - const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); - if (nullptr == edge) + if (ep.m_interior_edge_count != vertex->m_edge_count) continue; -#pragma ON_PRAGMA_WARNING_PUSH -#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wtautological-compare") -#pragma ON_PRAGMA_WARNING_DISABLE_GNU("-Wtautological-compare") - // The next if (min_face_count > min_face_count) should be if (min_face_count > max_face_count) - // and is a bug that will not be fixed in 6.x because it exposes other bugs below. - // Without the fix, max_face_count is always 0xFFFEU and the continue always occurs. - // It will be fixed in 7.x. See RH-51598. - if (min_face_count > min_face_count) - { - min_face_count = edge->m_face_count; - max_face_count = edge->m_face_count; - } - else if (edge->m_face_count < min_face_count) - min_face_count = edge->m_face_count; - else if (edge->m_face_count > max_face_count) - max_face_count = edge->m_face_count; -#pragma ON_PRAGMA_WARNING_POP } - - if (max_face_count > 2) - continue; - - if (bNewVertexTagIsSmooth && 2 != min_face_count ) - continue; + else if (ON_SubD::VertexTag::Crease == vertex_tag) + { + if (2 == ep.m_crease_edge_count) + { + // attempt change - further refinement may be needed here + } + else if (2 == ep.m_boundary_edge_count) + { + // attempt change - further refinement may be needed here + } + else if (2 == ep.m_wire_edge_count) + { + // attempt change - further refinement may be needed here + } + else + { + // dont' attempt change - further refinement may be needed here + continue; + } + } } candidate_count++; @@ -11510,7 +13559,7 @@ unsigned int ON_SubD::SetVertexTags( continue; ON_SubD::EdgeTag edge_tag; - if (v[0]->IsCreaseOrCornerOrDart() && v[1]->IsCreaseOrCornerOrDart()) + if (v[0]->IsDartOrCreaseOrCorner() && v[1]->IsDartOrCreaseOrCorner()) edge_tag = ON_SubD::EdgeTag::Crease; else edge_tag = ON_SubD::EdgeTag::Smooth; @@ -11542,9 +13591,10 @@ unsigned int ON_SubD::SetVertexTags( if (vertex->m_vertex_tag == vtag) continue; const_cast(vertex)->m_vertex_tag = vtag; + vertex->ClearSavedSubdivisionPoints(); } - ClearLimitSurfaceMesh(); + ClearEvaluationCache(); UpdateAllTagsAndSectorCoefficients(false); return changed_vertex_count; @@ -11611,20 +13661,21 @@ unsigned int ON_SubD::SetEdgeTags( changed_edge_count++; edge->m_edge_tag = edge_tag; - edge->UnsetSectorCoefficients(); + edge->UnsetSectorCoefficientsForExperts(); for (int evi = 0; evi < 2; evi++) { ON_SubDVertex* v = const_cast(edge->m_vertex[evi]); if (nullptr == v) continue; v->m_vertex_tag = ON_SubD::VertexTag::Unset; + v->ClearSavedSubdivisionPoints(); } } if (0 == changed_edge_count) return 0; - ClearLimitSurfaceMesh(); + ClearEvaluationCache(); ON_SubDVertexIterator vit(*this); for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) @@ -11664,6 +13715,7 @@ unsigned int ON_SubD::SetEdgeTags( if (v->m_vertex_tag != vertex_tag) { const_cast(v)->m_vertex_tag = vertex_tag; + v->ClearSavedSubdivisionPoints(); } } @@ -11680,7 +13732,8 @@ unsigned int ON_SubD::SetEdgeTags( if (e_tag != e->m_edge_tag) { const_cast(e)->m_edge_tag = e_tag; - e->UnsetSectorCoefficients(); + e->UnsetSectorCoefficientsForExperts(); + e->ClearSavedSubdivisionPoints(); } } @@ -11688,11 +13741,50 @@ unsigned int ON_SubD::SetEdgeTags( return changed_edge_count; } +unsigned int ON_SubD::RemoveAllCreases() +{ + unsigned int changed_count = 0; + ON_SubDEdgeIterator eit(*this); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + if ( false == e->IsCrease() || 2 != e->m_face_count) + continue; + const_cast(e)->m_edge_tag = ON_SubD::EdgeTag::Smooth; + e->UnsetSectorCoefficientsForExperts(); + for (int evi = 0; evi < 2; evi++) + { + if (nullptr == e->m_vertex[evi]) + continue; + const_cast(e->m_vertex[evi])->m_vertex_tag = ON_SubD::VertexTag::Unset; + } + ++changed_count; + } + + if (changed_count > 0) + { + this->DestroyRuntimeCache(true); + this->UpdateAllTagsAndSectorCoefficients(true); + } + + return changed_count; +} + +const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor(ON_SubDEdgePtr starting_edge, ON_ChainDirection search_direction, ON_SubD::ChainType chain_type) +{ + return ON_SubDEdgeChain::EdgeChainNeighbor( + starting_edge, + search_direction, + chain_type, + false, + ON_ComponentStatus::NoneSet, + ON_ComponentStatus::NoneSet + ); +} const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor( ON_SubDEdgePtr starting_edge, ON_ChainDirection search_direction, - bool bStopAtBreak, + ON_SubD::ChainType chain_type, bool bEnableStatusCheck, ON_ComponentStatus status_pass, ON_ComponentStatus status_fail @@ -11715,11 +13807,11 @@ const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor( break; const bool bIsSmooth = edge->IsSmooth(); - const bool bIsCrease = edge->IsCrease(); + const bool bIsCrease = edge->IsCrease() || 2 != edge->m_face_count; if (bIsSmooth != (bIsCrease?false:true)) break; - if (bStopAtBreak) + if (ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type) { if (bIsSmooth) { @@ -11798,8 +13890,11 @@ const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor( } if (nullptr != nxt) { - if (bStopAtBreak && bIsSmooth != nxt->IsSmooth()) - break; + if (bIsSmooth != nxt->IsSmooth()) + { + if ( ON_SubD::ChainType::EqualEdgeTag == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type) + break; + } if (false == bEnableStatusCheck || ON_ComponentStatus::StatusCheck(nxt->m_status, status_pass, status_fail)) return ON_SubDEdgePtr::Create(nxt, (v == nxt->m_vertex[nxt_connecting_vertex_index]) ? 0 : 1); } @@ -12036,7 +14131,27 @@ bool ON_SubDEdgeChain::InChain( bool ON_SubDEdgeChain::IsClosedLoop() const { const unsigned int count = m_edge_chain.UnsignedCount(); - return (count >= 2 && m_edge_chain[0].RelativeVertex(0) == m_edge_chain[count - 1].RelativeVertex(1)); + return (count >= 3 && m_edge_chain[0].RelativeVertex(0) == m_edge_chain[count - 1].RelativeVertex(1)); +} + +bool ON_SubDEdgeChain::IsConvexLoop(bool bStrictlyConvex) const +{ + if (false == IsClosedLoop()) + return false; + + const unsigned int count = m_edge_chain.UnsignedCount(); + ON_SimpleArray points(count); + for (unsigned int i = 0; i < count; ++i) + { + const ON_SubDVertex* v = m_edge_chain[i].RelativeVertex(0); + if (nullptr == v) + return false; + points.Append(v->ControlNetPoint()); + } + if ( false == (points[0] != points[count - 1]) ) + return false; + + return ON_IsConvexPolyline(points, bStrictlyConvex); } @@ -12053,13 +14168,13 @@ unsigned int ON_SubDEdgeChain::BeginEdgeChain( const ON_SimpleArray& initial_edge_chain ) { - return BeginEdgeChain(subd_ref, initial_edge_chain.UnsignedCount(), initial_edge_chain.Array()); + return BeginEdgeChain(subd_ref, initial_edge_chain.Array(), initial_edge_chain.UnsignedCount()); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_SubDRef subd_ref, - size_t edge_count, - const ON_SubDEdge*const* initial_edge_chain + const ON_SubDEdge*const* initial_edge_chain, + size_t edge_count ) { ClearEdgeChain(); @@ -12106,7 +14221,7 @@ unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_SubDEdgePtr eptr ) { - return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, 1, &eptr); + return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, &eptr, 1); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( @@ -12114,14 +14229,14 @@ unsigned int ON_SubDEdgeChain::BeginEdgeChain( const ON_SimpleArray& initial_edge_chain ) { - return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, initial_edge_chain.UnsignedCount(), initial_edge_chain.Array() ); + return ON_SubDEdgeChain::BeginEdgeChain(subd_ref, initial_edge_chain.Array(), initial_edge_chain.UnsignedCount() ); } unsigned int ON_SubDEdgeChain::BeginEdgeChain( ON_SubDRef subd_ref, - size_t edge_count, - const ON_SubDEdgePtr* initial_edge_chain -) + const ON_SubDEdgePtr* initial_edge_chain, + size_t edge_count + ) { ClearEdgeChain(); @@ -12161,7 +14276,7 @@ unsigned int ON_SubDEdgeChain::BeginEdgeChain( continue; if (v[1] != first_vertex) { - if (m_unique_tester.AddToList((ON__UINT_PTR)v[1])) + if (false == m_unique_tester.AddToList((ON__UINT_PTR)v[1])) continue; } } @@ -12262,7 +14377,7 @@ const ON_SubDVertex* ON_SubDEdgeChain::Vertex(int vertex_index) const unsigned int ON_SubDEdgeChain::AddOneNeighbor( ON_ChainDirection direction, - bool bStopAtTagChange + ON_SubD::ChainType chain_type ) { const unsigned int count0 = m_edge_chain.UnsignedCount(); @@ -12277,7 +14392,7 @@ unsigned int ON_SubDEdgeChain::AddOneNeighbor( eptr = (ON_ChainDirection::Previous != direction) - ? ON_SubDEdgeChain::EdgeChainNeighbor(LastEdgePtr(), ON_ChainDirection::Next, bStopAtTagChange, m_bEnableStatusCheck, m_status_check_pass, m_status_check_fail) + ? ON_SubDEdgeChain::EdgeChainNeighbor(LastEdgePtr(), ON_ChainDirection::Next, chain_type, m_bEnableStatusCheck, m_status_check_pass, m_status_check_fail) : ON_SubDEdgePtr::Null; e = eptr.Edge(); v = eptr.RelativeVertex(0); @@ -12293,7 +14408,7 @@ unsigned int ON_SubDEdgeChain::AddOneNeighbor( eptr = (ON_ChainDirection::Next != direction) - ? ON_SubDEdgeChain::EdgeChainNeighbor(FirstEdgePtr(), ON_ChainDirection::Previous, bStopAtTagChange, m_bEnableStatusCheck, m_status_check_pass, m_status_check_fail) + ? ON_SubDEdgeChain::EdgeChainNeighbor(FirstEdgePtr(), ON_ChainDirection::Previous, chain_type, m_bEnableStatusCheck, m_status_check_pass, m_status_check_fail) : ON_SubDEdgePtr::Null; e = eptr.Edge(); v = eptr.RelativeVertex(1); @@ -12312,7 +14427,7 @@ unsigned int ON_SubDEdgeChain::AddOneNeighbor( unsigned int ON_SubDEdgeChain::AddAllNeighbors( ON_ChainDirection direction, - bool bStopAtTagChange + ON_SubD::ChainType chain_type ) { const unsigned int count0 = m_edge_chain.UnsignedCount(); @@ -12320,9 +14435,9 @@ unsigned int ON_SubDEdgeChain::AddAllNeighbors( return 0; if (ON_ChainDirection::Previous != direction) - while (1 == AddOneNeighbor(ON_ChainDirection::Next, bStopAtTagChange)) {} + while (1 == AddOneNeighbor(ON_ChainDirection::Next, chain_type)) {} if (ON_ChainDirection::Next != direction) - while (1 == AddOneNeighbor(ON_ChainDirection::Previous, bStopAtTagChange)) {} + while (1 == AddOneNeighbor(ON_ChainDirection::Previous, chain_type)) {} return m_edge_chain.UnsignedCount() - count0; } @@ -12433,13 +14548,13 @@ void ON_SubDEdgeChain::ReverseEdgeChain( ON_SimpleArray< ON_SubDEdgePtr >& edge_chain ) { - ON_SubDEdgeChain::ReverseEdgeChain(edge_chain.UnsignedCount(), edge_chain.Array()); + ON_SubDEdgeChain::ReverseEdgeChain(edge_chain.Array(), edge_chain.UnsignedCount()); } void ON_SubDEdgeChain::ReverseEdgeChain( - size_t edge_count, - ON_SubDEdgePtr* edge_chain -) + ON_SubDEdgePtr* edge_chain, + size_t edge_count + ) { if (edge_count <= 0 || nullptr == edge_chain) return; @@ -12462,12 +14577,12 @@ bool ON_SubDEdgeChain::IsValidEdgeChain( bool bCheckForDuplicateEdges ) { - return ON_SubDEdgeChain::IsValidEdgeChain(edge_chain.UnsignedCount(), edge_chain.Array(), bCheckForDuplicateEdges); + return ON_SubDEdgeChain::IsValidEdgeChain(edge_chain.Array(), edge_chain.UnsignedCount(), bCheckForDuplicateEdges); } bool ON_SubDEdgeChain::IsValidEdgeChain( - size_t edge_count, const ON_SubDEdgePtr* edge_chain, + size_t edge_count, bool bCheckForDuplicateEdges ) { @@ -12519,17 +14634,563 @@ bool ON_SubDEdgeChain::IsValidEdgeChain( return true; } -class ON_SubDLimitMeshImpl* ON_SubDLimitMesh::SubLimple() const +class ON_SubDMeshImpl* ON_SubDMesh::SubLimple() const { return m_impl_sp.get(); } -unsigned int ON_SubDLimitMesh::SubLimpleUseCount() const +unsigned int ON_SubDMesh::SubLimpleUseCount() const { return (unsigned int)(m_impl_sp.use_count()); } +bool ON_SubD::IsSolid() const +{ + bool bIsManifold = false; + bool bIsOriented = false; + bool bHasBoundary = false; + int solid_orientation = 0; + ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation); + return (bIsManifold && bIsOriented && false == bHasBoundary); +} + +int ON_SubD::SolidOrientation() const +{ + bool bIsManifold = false; + bool bIsOriented = false; + bool bHasBoundary = false; + int solid_orientation = 0; + ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation); + return solid_orientation; +} + +bool ON_SubD::IsManifold( bool& bIsOriented, bool& bHasBoundary ) const +{ + bool bIsManifold = false; + bIsOriented = false; + bHasBoundary = false; + int solid_orientation = 0; + ActiveLevel().GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation); + return bIsManifold; +} + +bool ON_SubD::IsManifold() const +{ + bool bIsOriented = false; + bool bHasBoundary = false; + return IsManifold(bIsOriented, bHasBoundary); +} + +class ON_SubDEdgePtrLink +{ +public: + ON_SubDEdgePtrLink() = default; + ~ON_SubDEdgePtrLink() = default; + ON_SubDEdgePtrLink(const ON_SubDEdgePtrLink&) = default; + ON_SubDEdgePtrLink& operator=(const ON_SubDEdgePtrLink&) = default; + +public: + static const ON_SubDEdgePtrLink Empty; + +public: + ON_SubDEdgePtr m_ep = ON_SubDEdgePtr::Null; + unsigned int m_index = 0; + unsigned int m_nbr_index = 0; + + static int CompareVertex( + const ON_SubDEdgePtrLink* lhs, + const ON_SubDEdgePtrLink* rhs + ) + { + const ON_SubDVertex* lhs_v = lhs->m_ep.RelativeVertex(0); + const ON_SubDVertex* rhs_v = rhs->m_ep.RelativeVertex(0); + if (lhs_v < rhs_v) + return -1; + if (lhs_v > rhs_v) + return 1; + return 0; + } + + static int CompareIndex( + const ON_SubDEdgePtrLink* lhs, + const ON_SubDEdgePtrLink* rhs + ) + { + const unsigned int lhs_i = lhs->m_index; + const unsigned int rhs_i = rhs->m_index; + if (lhs_i < rhs_i) + return -1; + if (lhs_i > rhs_i) + return 1; + return 0; + } + + static void Resolve3OrMoreEdges( + const unsigned int unset_nbr1_index, + unsigned int count, + const ON_SubDVertex* v, + ON_SubDEdgePtrLink* links + ); +}; + +const ON_SubDEdgePtrLink ON_SubDEdgePtrLink::Empty; + +void ON_SubDEdgePtrLink::Resolve3OrMoreEdges( + const unsigned int unset_nbr1_index, + unsigned int count, + const ON_SubDVertex* v, + ON_SubDEdgePtrLink* links +) +{ + // If the case can't be resolved by Resolve3OrMoreEdges(), + // then the vertex will not appear in the middle of a chain. + + if (count < 3 || nullptr == v || count != (unsigned int)v->m_edge_count) + return; + + switch (count) + { + case 3: + if (false == v->IsCrease() && false == v->IsDart()) + return; + break; + + case 4: + if (false == v->IsCrease() && false == v->IsSmooth()) + return; + break; + + default: + if (false == v->IsCrease()) + return; + break; + } + + const ON_SubDEdge* link_edges[4] = {}; + const ON_SubDEdge* vertex_edges[4] = {}; + unsigned int crease_edge_count = 0; + unsigned int smooth_edge_count = 0; + unsigned int smooth_edge_link_index[4] = {}; + unsigned int crease_edge_link_index[4] = {}; + for (unsigned int j = 0; j < count; j++) + { + const ON_SubDEdge* e = links[j].m_ep.Edge(); + if (nullptr == e) + return; + const ON_SubDEdge* ve = v->Edge(j); + if (nullptr == ve) + return; + if (j < 4) + { + link_edges[j] = e; + vertex_edges[j] = ve; + } + + if (e->IsSmooth() && 2 == e->m_face_count) + { + if ( smooth_edge_count < 4) + smooth_edge_link_index[smooth_edge_count] = j; + ++smooth_edge_count; + } + else if (e->IsCrease()) + { + if (crease_edge_count < 4) + crease_edge_link_index[crease_edge_count] = j; + ++crease_edge_count; + } + else + return; + } + + if ( 2 == crease_edge_count && v->IsCrease() ) + { + // Link the two creased edges. + // The vertex will be interior in a chain and the edges + // will be next to each other. + links[crease_edge_link_index[0]].m_nbr_index = links[crease_edge_link_index[1]].m_index; + links[crease_edge_link_index[1]].m_nbr_index = links[crease_edge_link_index[0]].m_index; + if (1 == smooth_edge_count) + { + // this edge will be at the end of a chain. + links[smooth_edge_link_index[0]].m_nbr_index = unset_nbr1_index; + } + } + + if (2 == smooth_edge_count) + { + // Link the two smooth edges. + // The vertex will be interior in a chain and the edges + // will be next to each other. + links[smooth_edge_link_index[0]].m_nbr_index = links[smooth_edge_link_index[1]].m_index; + links[smooth_edge_link_index[1]].m_nbr_index = links[smooth_edge_link_index[0]].m_index; + if (1 == crease_edge_count) + { + // this edge will be at the end of a chain + links[crease_edge_link_index[0]].m_nbr_index = unset_nbr1_index; + } + } + + if ( + 4 != count + || 4 != smooth_edge_count + || 0 != crease_edge_count + || 4 != v->m_face_count + || 4 != v->m_edge_count + || false == v->IsSmooth() + ) + return; + + // make sure vertex_edges[] and link_edges[] are the same list. + unsigned int match_count = 0; + for (unsigned int j = 0; j == match_count && j < count; j++) + { + for (unsigned int k = 0; k < count; k++) + { + if (vertex_edges[k] == link_edges[j]) + { + vertex_edges[k] = nullptr; + match_count++; + break; + } + } + } + if (match_count != count) + return; + + // vertex has 4 faces and 4 smooth edges. Link opposite edges. + const ON_SubDFace* edge_faces[4][2]; + for (unsigned int j = 0; j < 4; j++) + { + edge_faces[j][0] = link_edges[j]->Face(0); + edge_faces[j][1] = link_edges[j]->Face(1); + if (nullptr == edge_faces[j][0] || nullptr == edge_faces[j][1]) + return; + } + ON_2udex pairs[2]; + unsigned int pair_count = 0; + ON_2udex pair; + for (pair.i = 0; pair.i < 4; ++pair.i) for (pair.j = pair.i+1; pair.j < 4; ++pair.j) + { + if ( + edge_faces[pair.i][0] != edge_faces[pair.j][0] + && edge_faces[pair.i][0] != edge_faces[pair.j][1] + && edge_faces[pair.i][1] != edge_faces[pair.j][0] + && edge_faces[pair.i][1] != edge_faces[pair.j][1] + ) + { + // the associated edges share no faces. + if ( pair_count < 2) + pairs[pair_count] = pair; + if (++pair_count > 2) + break; + } + } + if (2 == pair_count) + { + links[pairs[0].i].m_nbr_index = links[pairs[0].j].m_index; + links[pairs[0].j].m_nbr_index = links[pairs[0].i].m_index; + links[pairs[1].i].m_nbr_index = links[pairs[1].j].m_index; + links[pairs[1].j].m_nbr_index = links[pairs[1].i].m_index; + } + + return; +} + +unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( + const ON_SimpleArray< const ON_SubDEdge* >& unsorted_edges, + unsigned int minimum_chain_length, + ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges +) +{ + const unsigned int unsorted_edge_count = unsorted_edges.Count(); + ON_SimpleArray< ON_SubDEdgePtr > unsorted_eptrs(unsorted_edge_count); + for (unsigned i = 0; i < unsorted_edge_count; ++i) + { + const ON_SubDEdge* e = unsorted_edges[i]; + if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] || e->m_vertex[0] == e->m_vertex[1]) + continue; + ON_SubDEdgePtr eptr = ON_SubDEdgePtr::Create(e, 0); + if (1 == e->m_face_count && 0 == ON_SUBD_FACE_DIRECTION(e->m_face2[0].m_ptr)) + eptr = eptr.Reversed(); + unsorted_eptrs.Append(eptr); + } + return ON_SubDEdgeChain::SortEdgesIntoEdgeChains(unsorted_eptrs, minimum_chain_length, sorted_edges); +} + +unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( + const ON_SimpleArray< ON_SubDEdgePtr >& unsorted_edges, + unsigned int minimum_chain_length, + ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges +) +{ + // NOTE: + // unsorted_edges[] and sorted_edges[] may reference the same array. + ////const ON_SubDEdge* ee[2] = {}; + ////const ON_SubDVertex* vv[2] = {}; + + const unsigned int unsorted_edge_count = unsorted_edges.Count(); + if (unsorted_edge_count <= 0) + { + sorted_edges.SetCount(0); + return 0; + } + + ON_SimpleArray< ON_SubDEdgePtrLink > links(2*unsorted_edge_count); + const unsigned int unset_nbr1_index = 0xFFFFFFFEU; + const unsigned int unset_nbrX_index = unset_nbr1_index+1; + ON_SubDEdgePtrLink epl; + epl.m_nbr_index = unset_nbrX_index; + for (unsigned int i = 0; i < unsorted_edge_count; i++) + { + ON_SubDEdgePtr ep = unsorted_edges[i]; + const ON_SubDEdge* e = ep.Edge(); + if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] || e->m_vertex[0] == e->m_vertex[1]) + continue; + + // unsorted_edges[i] has 2 links with m_index = 2*i and m_index = 2*+1. + + // links with even m_index have opposite orientation as unsorted_edges[]. + // links[2*i].m_ep.RelativeVertex(0) = unsorted_edges[i].RelativeVertex(1) + epl.m_ep = ep.Reversed(); + links.Append(epl); + ++epl.m_index; + ////ee[0] = epl.m_ep.Edge(); + ////vv[1] = epl.m_ep.RelativeVertex(0); + + // links with odd m_index have same orientation as unsorted_edges[]. + // links[2*i+1].m_ep.RelativeVertex(0) = unsorted_edges[i].RelativeVertex(0) + epl.m_ep = ep; + links.Append(epl); + ++epl.m_index; + ////ee[1] = epl.m_ep.Edge(); + ////vv[0] = epl.m_ep.RelativeVertex(0); + ////if (e != ee[0] || e != ee[1] || vv[0] == vv[1]) + //// return false; + } + + // NOTE: + // unsorted_edges[] and sorted_edges[] may reference the same array. + // At this point, I'm finished with unsorted_edges[] so it's ok to + // modify sorted_edges[] here. + sorted_edges.SetCount(0); + sorted_edges.Reserve(unsorted_edge_count); + + // sort links by ON_SubDEdgePtr.RelativeVertex(0) and set m_nbr_index + links.QuickSort(ON_SubDEdgePtrLink::CompareVertex); + // link_count = even number + const unsigned int link_count = links.UnsignedCount(); + unsigned int i1 = link_count; + for (unsigned int i0 = 0; i0 < link_count; i0 = i1) + { + ON_SubDEdgePtrLink& epl0 = links[i0]; + const ON_SubDVertex* v = epl0.m_ep.RelativeVertex(0); + for (i1 = i0 + 1; i1 < link_count; ++i1) + { + if (0 != ON_SubDEdgePtrLink::CompareVertex(&epl0, &links[i1])) + break; + } + if (nullptr == v) + { + ON_SUBD_ERROR("Bug in code that creates the links[] array."); + continue; + } + if (v->IsCorner()) + { + // These edges will be at the ends of chains. + while (i0 < i1) + links[i0++].m_nbr_index = unset_nbr1_index; + continue; + } + + if (i0 + 1 == i1) + { + // The vertex is referenced by exactly 1 edge in unsorted_edges[] + // This edge will appear in sorted_edges[] at the start or end of a chain. + epl0.m_nbr_index = unset_nbr1_index; + continue; + } + if (i0 + 2 == i1) + { + // This vertex is referenced by exactly 2 edges in unsorted_edges[]. + // The vertex will be in the interior of a chain and the edges will + // appear in sorted_edges[] next to each other in the same chain. + ON_SubDEdgePtrLink& epl1 = links[i0 + 1]; + epl0.m_nbr_index = epl1.m_index; + epl1.m_nbr_index = epl0.m_index; + continue; + } + + // The vertex referenced by 3 or more edges in unsorted_edges[]. + // If the case cannot be resolved by Resolve3OrMoreEdges(), + // then this vertex will not be in the interior of a chain. + ON_SubDEdgePtrLink::Resolve3OrMoreEdges( + unset_nbr1_index, + i1 - i0, + v, + links.Array() + i0 + ); + } + + // Sort links[] by m_index valut to restore links[] to its original order. + links.QuickSort(ON_SubDEdgePtrLink::CompareIndex); + ON_SubDEdgePtrLink* links_array = links.Array(); + + unsigned chain_count = 0; + ON_SimpleArray chain(unsorted_edge_count); + + for (unsigned int i = 0; i < link_count; ++i) + { + // epl0 and epl1 are the links for edges[i/2] + const ON_SubDEdgePtrLink epl0 = links_array[i]; + links_array[i].m_ep = ON_SubDEdgePtr::Null; + const ON_SubDEdgePtrLink epl1 = links_array[++i]; + links_array[i].m_ep = ON_SubDEdgePtr::Null; + + if (nullptr == epl0.m_ep.Edge()) + continue; // this edge has already been inserted in sorted_edges[]. + + chain.SetCount(0); + + // Add edges that come "before" edges[i/2] to chain[] + epl = epl1; + for (;;) + { + if (epl.m_nbr_index >= unset_nbr1_index) + break; + unsigned int j = epl.m_nbr_index; + unsigned int j1 = (0 == (j % 2)) ? (j + 1) : (j - 1); + // epl = "previous" link + epl = links_array[j1]; + links_array[j].m_ep = ON_SubDEdgePtr::Null; + links_array[j1].m_ep = ON_SubDEdgePtr::Null; + if (nullptr == epl.m_ep.Edge()) + break; + ////ee[0] = epl.m_ep.Edge(); + ////vv[0] = epl.m_ep.RelativeVertex(0); + ////vv[1] = epl.m_ep.RelativeVertex(1); + ////if (vv[0] == vv[1] || nullptr == ee[0]) + //// return false; + chain.Append(epl.m_ep); + } + + const bool bClosedChain = (epl.m_index == epl1.m_index); + const bool bReverseFinalChain + = false == bClosedChain + && unset_nbr1_index == epl.m_nbr_index + && (0 == (epl.m_index % 2)); + if (false == bClosedChain) + { + chain.Reverse(); + } + + ////ee[0] = epl1.m_ep.Edge(); + ////vv[0] = epl1.m_ep.RelativeVertex(0); + ////vv[1] = epl1.m_ep.RelativeVertex(1); + ////if (vv[0] == vv[1] || nullptr == ee[0]) + //// return false; + chain.Append(epl1.m_ep); // matches input edge orientation + + if (bClosedChain) + { + // put edges[i/2] at the start of the closed chain. + chain.Reverse(); + } + else + { + // Add edges that come "after" edges[i/2] to chain[] + epl = epl0; + for (;;) + { + if (epl.m_nbr_index >= unset_nbr1_index) + break; + unsigned int j = epl.m_nbr_index; + unsigned int j1 = (0 == (j % 2)) ? (j + 1) : (j - 1); + // epl = "next" link + epl = links_array[j1]; + links_array[j].m_ep = ON_SubDEdgePtr::Null; + links_array[j1].m_ep = ON_SubDEdgePtr::Null; + if (nullptr == epl.m_ep.Edge()) + break; + ////ee[0] = epl.m_ep.Edge(); + ////vv[0] = epl.m_ep.RelativeVertex(1); + ////vv[1] = epl.m_ep.RelativeVertex(0); + ////if (vv[0] == vv[1] || nullptr == ee[0]) + //// return false; + chain.Append(epl.m_ep.Reversed()); + } + + if (bReverseFinalChain) + chain.Reverse(); + } + + const unsigned int chain_edge_count = chain.UnsignedCount(); + if (chain_edge_count > 0) + { + for (;;) + { + if (chain_edge_count < 3) + break; + const ON_SubDVertex* c0 = chain[0].RelativeVertex(0); + if (nullptr == c0) + break; + if (c0->IsCorner()) + break; + const ON_SubDVertex* c1 = chain[chain_edge_count-1].RelativeVertex(1); + if (c0 != c1) + break; + const ON_SubDEdge* e0 = chain[0].Edge(); + if (nullptr == e0) + break; + const ON_SubDEdge* e1 = chain[chain_edge_count-1].Edge(); + if (nullptr == e1) + break; + const bool bSmooth = e0->IsSmooth(); + if (bSmooth != e1->IsSmooth()) + break; + if (bSmooth && c0->IsCrease()) + break; + // Check for an embedded crease vertex. + for (unsigned int k = 1; k < chain_edge_count; ++k) + { + const ON_SubDVertex* v = chain[k].RelativeVertex(0); + if (nullptr == v) + break; + const ON_SubDEdge* e = chain[k].Edge(); + if (nullptr == e) + break; + if ( + bSmooth != e->IsSmooth() + || (bSmooth && v->IsCreaseOrCorner()) + ) + { + // shift chain[] so it begins at chain[k]; + ON_SimpleArray tail; + tail.Append(k, chain.Array()); + for (unsigned n = k; n < chain_edge_count; ++n) + chain[n - k] = chain[n]; + chain.SetCount(chain_edge_count - k); + chain.Append(tail.Count(), tail.Array()); + break; + } + } + break; + } + if (chain.UnsignedCount() >= minimum_chain_length) + { + ++chain_count; + sorted_edges.Append(chain.Count(), chain.Array()); + } + } + + if ( link_count == 2*sorted_edges.UnsignedCount() ) + break; // we've used all the links - no need to "skip over the rest". + } + + return chain_count; +} + + #if defined(ON_SUBD_CENSUS) ////////////////////////////////////////////////////////////////////////// // @@ -12713,10 +15374,10 @@ void ON_CensusCounter::CensusReport( sClassName = "ON_SubDimple"; break; case ON_CensusCounter::Class::subd_limit_mesh: - sClassName = "ON_SubDLimitMesh"; + sClassName = "ON_SubDMesh"; break; case ON_CensusCounter::Class::subd_limit_mesh_impl: - sClassName = "ON_SubDLimitMeshImpl"; + sClassName = "ON_SubDMeshImpl"; break; case ON_CensusCounter::Class::subd_ref: sClassName = "ON_SubDef"; @@ -12766,26 +15427,26 @@ void ON_CensusCounter::CensusReport( case ON_CensusCounter::Class::subd_limit_mesh: { - const ON_SubDLimitMesh* subd_limit_mesh = (const ON_SubDLimitMesh*)e->m_ptr; - if ( subd_limit_mesh == &ON_SubDLimitMesh::Empty ) - text_log.Print("ON_SubDLimitMesh::Empty (%u) ", sn); + const ON_SubDMesh* subd_limit_mesh = (const ON_SubDMesh*)e->m_ptr; + if ( subd_limit_mesh == &ON_SubDMesh::Empty ) + text_log.Print("ON_SubDMesh::Empty (%u) ", sn); else - text_log.Print("ON_SubDLimitMesh(%u) ", sn); - const class ON_SubDLimitMeshImpl* limple = subd_limit_mesh->SubLimple(); + text_log.Print("ON_SubDMesh(%u) ", sn); + const class ON_SubDMeshImpl* limple = subd_limit_mesh->SubLimple(); if ( nullptr == limple ) - text_log.Print(" ON_SubDLimitMeshImpl(nullptr)\n"); + text_log.Print(" ON_SubDMeshImpl(nullptr)\n"); else { unsigned int limple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_limit_mesh_impl, (ON__UINT_PTR)limple); - text_log.Print(" ON_SubDLimitMeshImpl(%u)x%u\n", limple_sn, subd_limit_mesh->SubLimpleUseCount()); + text_log.Print(" ON_SubDMeshImpl(%u)x%u\n", limple_sn, subd_limit_mesh->SubLimpleUseCount()); } } break; case ON_CensusCounter::Class::subd_limit_mesh_impl: { - const ON_SubDLimitMeshImpl* subd_limple = (const ON_SubDLimitMeshImpl*)e->m_ptr; - text_log.Print("ON_SubDLimitMeshImpl(%u)", sn); + const ON_SubDMeshImpl* subd_limple = (const ON_SubDMeshImpl*)e->m_ptr; + text_log.Print("ON_SubDMeshImpl(%u)", sn); std::shared_ptr limple_sp(subd_limple->m_subdimple_wp.lock()); const ON_SubDimple* dimple = limple_sp.get(); @@ -12869,17 +15530,17 @@ ON_SubDImpleCensusCounter::ON_SubDImpleCensusCounter(const ON_SubDImpleCensusCou -ON_SubDLimitMeshCensusCounter::ON_SubDLimitMeshCensusCounter() ON_NOEXCEPT +ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); } -ON_SubDLimitMeshCensusCounter::~ON_SubDLimitMeshCensusCounter() ON_NOEXCEPT +ON_SubDMeshCensusCounter::~ON_SubDMeshCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); } -ON_SubDLimitMeshCensusCounter::ON_SubDLimitMeshCensusCounter(const ON_SubDLimitMeshCensusCounter&) ON_NOEXCEPT +ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); } @@ -12887,21 +15548,20 @@ ON_SubDLimitMeshCensusCounter::ON_SubDLimitMeshCensusCounter(const ON_SubDLimitM -ON_SubDLimitMeshImplCensusCounter::ON_SubDLimitMeshImplCensusCounter() ON_NOEXCEPT +ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); } -ON_SubDLimitMeshImplCensusCounter::~ON_SubDLimitMeshImplCensusCounter() ON_NOEXCEPT +ON_SubDMeshCensusCounter::~ON_SubDMeshCensusCounter() ON_NOEXCEPT { ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); } -ON_SubDLimitMeshImplCensusCounter::ON_SubDLimitMeshImplCensusCounter(const ON_SubDLimitMeshImplCensusCounter&) ON_NOEXCEPT +ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT { ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); } #endif -#endif diff --git a/opennurbs_subd.h b/opennurbs_subd.h index a7f317e1..a0470ff4 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -25,7 +25,7 @@ // The classes have no size but effect performance. Use only in // debugging situations. Never ship a release with ON_SUBD_CENSUS defined. // -class ON_SUBD_CLASS ON_CensusCounter +class ON_CLASS ON_CensusCounter { public: enum class Class : unsigned int @@ -51,7 +51,7 @@ public: static void Clear(); }; -class ON_SUBD_CLASS ON_SubDCensusCounter +class ON_CLASS ON_SubDCensusCounter { public: ON_SubDCensusCounter() ON_NOEXCEPT; @@ -62,7 +62,7 @@ public: //ON_SubDCensusCounter& operator=( ON_SubDCensusCounter&& ) ON_NOEXCEPT; }; -class ON_SUBD_CLASS ON_SubDRefCensusCounter +class ON_CLASS ON_SubDRefCensusCounter { public: ON_SubDRefCensusCounter() ON_NOEXCEPT; @@ -74,7 +74,7 @@ public: }; -class ON_SUBD_CLASS ON_SubDImpleCensusCounter +class ON_CLASS ON_SubDImpleCensusCounter { public: ON_SubDImpleCensusCounter() ON_NOEXCEPT; @@ -85,27 +85,27 @@ public: //ON_SubDImplCensusCounter& operator=( ON_SubDImplCensusCounter&& ) ON_NOEXCEPT; }; -class ON_SUBD_CLASS ON_SubDLimitMeshCensusCounter +class ON_CLASS ON_SubDMeshCensusCounter { public: - ON_SubDLimitMeshCensusCounter() ON_NOEXCEPT; - ~ON_SubDLimitMeshCensusCounter() ON_NOEXCEPT; - ON_SubDLimitMeshCensusCounter(const ON_SubDLimitMeshCensusCounter&) ON_NOEXCEPT; - ON_SubDLimitMeshCensusCounter& operator=(const ON_SubDLimitMeshCensusCounter&) = default; - //ON_SubDLimitMeshCensusCounter( ON_SubDLimitMeshCensusCounter&& ) ON_NOEXCEPT; - //ON_SubDLimitMeshCensusCounter& operator=( ON_SubDLimitMeshCensusCounter&& ) ON_NOEXCEPT; + ON_SubDMeshCensusCounter() ON_NOEXCEPT; + ~ON_SubDMeshCensusCounter() ON_NOEXCEPT; + ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT; + ON_SubDMeshCensusCounter& operator=(const ON_SubDMeshCensusCounter&) = default; + //ON_SubDMeshCensusCounter( ON_SubDMeshCensusCounter&& ) ON_NOEXCEPT; + //ON_SubDMeshCensusCounter& operator=( ON_SubDMeshCensusCounter&& ) ON_NOEXCEPT; }; -class ON_SUBD_CLASS ON_SubDLimitMeshImplCensusCounter +class ON_CLASS ON_SubDMeshCensusCounter { public: - ON_SubDLimitMeshImplCensusCounter() ON_NOEXCEPT; - ~ON_SubDLimitMeshImplCensusCounter() ON_NOEXCEPT; - ON_SubDLimitMeshImplCensusCounter(const ON_SubDLimitMeshImplCensusCounter&) ON_NOEXCEPT; - ON_SubDLimitMeshImplCensusCounter& operator=(const ON_SubDLimitMeshImplCensusCounter&) ON_NOEXCEPT; - ON_SubDLimitMeshImplCensusCounter( ON_SubDLimitMeshImplCensusCounter&& ) ON_NOEXCEPT; - ON_SubDLimitMeshImplCensusCounter& operator=( ON_SubDLimitMeshImplCensusCounter&& ) ON_NOEXCEPT; + ON_SubDMeshCensusCounter() ON_NOEXCEPT; + ~ON_SubDMeshCensusCounter() ON_NOEXCEPT; + ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT; + ON_SubDMeshCensusCounter& operator=(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT; + ON_SubDMeshCensusCounter( ON_SubDMeshCensusCounter&& ) ON_NOEXCEPT; + ON_SubDMeshCensusCounter& operator=( ON_SubDMeshCensusCounter&& ) ON_NOEXCEPT; }; #endif @@ -120,6 +120,44 @@ public: #if !defined(OPENNURBS_SUBD_INC_) #define OPENNURBS_SUBD_INC_ +/// +/// ON_SubDTextureDomainType identifies the way face 2d texture coordinate domains are set. +/// +enum class ON_SubDTextureDomainType : unsigned char +{ + /// + /// Texture domains are not set. + /// + Unset = 0, + + /// + /// Each face texture domain is a unique rectangle of normlized texture space. + /// + PerFace = 1, + + /// + /// Each face texture domain is a unique rectangle of normlized texture space. + /// When possible, faces are partitioned into quad groups. Adjactent members + /// of the group are assigned adjacent rectangles in texture space. + /// + Packed = 2, + + /// + /// All face texture domain values are zero. + /// + Zero = 3, + + /// + /// All face texture domain values are nan. + /// + Nan = 4, + + /// + /// Code outside of opennurbs set the values. No other information is available. + /// + Custom = 7, +}; + class ON_CLASS ON_SubDVertexPtr { public: @@ -131,34 +169,96 @@ public: static const ON_SubDVertexPtr Null; bool IsNull() const; + bool IsNotNull() const; + + /* + Returns: + True if this vertex is active in its parent subd or other + relevent context. + Remarks: + When a component is in use, IsActive() = true. + If was used and then deleted, IsActive() is false. + */ + bool IsActive() const; class ON_SubDVertex* Vertex() const; - ON__UINT_PTR VertexPtrMark() const; + ON__UINT_PTR VertexDirection() const; - ON_ComponentStatus Status() const; + const ON_ComponentStatus Status() const; - static - class ON_SubDVertexPtr Create( + static const ON_SubDVertexPtr Create( const class ON_SubDVertex* vertex ); /* Parameters: vertex - [in] - mark - [in] + vertex_direction - [in] zero or one */ - static - class ON_SubDVertexPtr Create( + static const ON_SubDVertexPtr Create( const class ON_SubDVertex* vertex, - ON__UINT_PTR vertex_mark + ON__UINT_PTR vertex_direction ); - static - class ON_SubDVertexPtr Create( + static const ON_SubDVertexPtr Create( const class ON_SubDComponentPtr& vertex_component ); + + /* + Returns: + The current value of the component mark ( m_status->RuntimeMark() ). + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + */ + bool Mark() const; + + /* + Description: + Clears (sets to false) the value of the component mark. + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool ClearMark() const; + + /* + Description: + Sets (sets to true) the value of the component mark. + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool SetMark() const; + + /* + Description: + Sets the value of the component mark to bMark. + Parameter: + bMark - [in] + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool SetMark( + bool bMark + ) const; }; @@ -176,14 +276,88 @@ public: static const ON_SubDEdgePtr Null; + /* + Returns: + True if this->Edge() is nullptr. + */ bool IsNull() const; + /* + Returns: + True if this->Edge() is not nullptr. + */ + bool IsNotNull() const; + + + /* + Returns: + True if this->Edge() is not nullptr and both vertex pointers are not null as well. + */ + bool IsNotNullAndVerticesAreNotNull() const; + + /* + Returns: + True if this edge is active in its parent subd or other + relevent context. + Remarks: + When a component is in use, IsActive() = true. + If was used and then deleted, IsActive() is false. + */ + bool IsActive() const; + + + /* + Returns: + The ON_SubDEdge this points at. + */ class ON_SubDEdge* Edge() const; /* Returns: - 0: the edge is oriented from Edge()->Vertex(0) to Edge()->Vertex(1). - 1: the edge is oriented from Edge()->Vertex(1) to Edge()->Vertex(0). + If Edge() is not nullptr, Edge()->m_id is returned. + Otherwise, 0 is returned. + */ + unsigned int EdgeId() const; + + /* + Returns: + If Edge() is not nullptr, Edge()->FaceCount() is returned. + Otherwise, 0 is returned. + */ + unsigned int EdgeFaceCount() const; + + /* + Returns: + If Edge() is not nullptr, Edge()->IsSmooth() is returned. + Otherwise, false is returned. + */ + bool EdgeIsSmooth() const; + + /* + Returns: + If Edge() is not nullptr, Edge()->IsCrease() is returned. + Otherwise, false is returned. + */ + bool EdgeIsCrease() const; + + /* + Returns: + If Edge() is not nullptr, Edge()->IsHardCrease() is returned. + Otherwise, false is returned. + */ + bool EdgeIsHardCrease() const; + + /* + Returns: + If Edge() is not nullptr, Edge()->IsDartCrease() is returned. + Otherwise, false is returned. + */ + bool EdgeIsDartCrease() const; + + /* + Returns: + 0: this ON_SubDEdgePtr is oriented from Edge()->Vertex(0) to Edge()->Vertex(1). + 1: this ON_SubDEdgePtr is oriented from Edge()->Vertex(1) to Edge()->Vertex(0). */ ON__UINT_PTR EdgeDirection() const; @@ -193,30 +367,114 @@ public: 0: return Edge()->Vertex(EdgeDirection()) 1: return Edge()->Vertex(1-EdgeDirection()) Returns: - The vertex with EdgeDirection() taken into account. + The requested vertex with EdgeDirection() taken into account. nullptr if relative_vertex_index, Edge() is nullptr, or Edge()->Vertex() is nullptr. */ const class ON_SubDVertex* RelativeVertex( - int relative_vertex_index + int relative_vertex_index ) const; - - ON_ComponentStatus Status() const; - /* Returns: - A pointer to the same edge with the direction flipped + The vector from RelativeVertex(0)->ControlNetPoint() to RelativeVertex(1)->ControlNetPoint(), + or ON_3dVector::NanVector if the relative vertex pointers are nullptr. */ - ON_SubDEdgePtr Reversed() const; + const ON_3dVector RelativeDirection() const; - static ON_SubDEdgePtr Create( + /* + Returns: + this->Edge()->m_status. + */ + const ON_ComponentStatus Status() const; + + /* + Returns: + A ON_SubDEdgePtr pointing at the same edge with the direction reversed from this. + */ + const ON_SubDEdgePtr Reversed() const; + + + /* + Parameters: + edge - [in] + Returns: + An ON_SubDEdgePtr pointing at edge with direction = 0 (not reversed). + */ + static const ON_SubDEdgePtr Create( + const class ON_SubDEdge* edge + ); + + /* + Parameters: + edge - [in] + direction - [in] + 0: not reversed + 1: reversed + Returns: + An ON_SubDEdgePtr pointing at edge with the specified direction. + */ + static const ON_SubDEdgePtr Create( const class ON_SubDEdge* edge, ON__UINT_PTR direction ); - static ON_SubDEdgePtr Create( + static const ON_SubDEdgePtr Create( const class ON_SubDComponentPtr& edge_component ); + + /* + Returns: + The current value of the component mark ( m_status->RuntimeMark() ). + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + */ + bool Mark() const; + + /* + Description: + Clears (sets to false) the value of the component mark. + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool ClearMark() const; + + /* + Description: + Sets (sets to true) the value of the component mark. + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool SetMark() const; + + /* + Description: + Sets the value of the component mark to bMark. + Parameter: + bMark - [in] + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool SetMark( + bool bMark + ) const; }; #if defined(ON_DLL_TEMPLATE) @@ -235,21 +493,111 @@ public: bool IsNull() const; - class ON_SubDFace* Face() const; + bool IsNotNull() const; + /* + Returns: + True if this face is active in its parent subd or other + relevent context. + Remarks: + When a component is in use, IsActive() = true. + If was used and then deleted, IsActive() is false. + */ + bool IsActive() const; + + + class ON_SubDFace* Face() const; + + /* + Returns: + If Face() is not nullptr, Face()->m_id is returned. + Otherwise, 0 is returned. + */ + unsigned int FaceId() const; + + /* + Returns: + If Face() is not nullptr, Face()->EdgeCount() is returned. + Otherwise, 0 is returned. + */ + unsigned int FaceEdgeCount() const; + ON__UINT_PTR FaceDirection() const; - ON_ComponentStatus Status() const; + const ON_ComponentStatus Status() const; - static - class ON_SubDFacePtr Create( + static const ON_SubDFacePtr Create( const class ON_SubDFace* face, ON__UINT_PTR direction ); - static ON_SubDFacePtr Create( + static const ON_SubDFacePtr Create( const class ON_SubDComponentPtr& face_component ); + + static int Compare( + const ON_SubDFacePtr* lhs, + const ON_SubDFacePtr* rhs + ); + + static int CompareFacePointer( + const ON_SubDFacePtr* lhs, + const ON_SubDFacePtr* rhs + ); + + /* + Returns: + The current value of the component mark ( m_status->RuntimeMark() ). + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + */ + bool Mark() const; + + /* + Description: + Clears (sets to false) the value of the component mark. + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool ClearMark() const; + + /* + Description: + Sets (sets to true) the value of the component mark. + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool SetMark() const; + + /* + Description: + Sets the value of the component mark to bMark. + Parameter: + bMark - [in] + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool SetMark( + bool bMark + ) const; }; #if defined(ON_DLL_TEMPLATE) @@ -267,6 +615,10 @@ public: static const ON_SubDComponentPtr Null; // // nullptr, type = unset, mark = 0 + /// + /// ON_SubDComponentPtr::Type identifies the type of subdivision component referenced by + /// the ON_SubDComponentPtr. + /// enum class Type : unsigned char { Unset = 0, @@ -297,10 +649,24 @@ public: const ON_SubDComponentPtr* b ); - static int Compare( + /* + Description: + Dictionary compares type and ComponentBase() pointer as an unsigned. + */ + static int CompareComponent( const ON_SubDComponentPtr* a, const ON_SubDComponentPtr* b - ); + ); + + /* + Description: + Dictionary compares type, ComponentBase() pointer as an unsigned, + and ComponentDirection(). + */ + static int CompareComponentAndDirection( + const ON_SubDComponentPtr* a, + const ON_SubDComponentPtr* b + ); /* @@ -310,6 +676,16 @@ public: bool IsNull() const; bool IsNotNull() const; + /* + Returns: + True if this component is active in its parent subd or other + relevent context. + Remarks: + When a component is in use, IsActive() = true. + If was used and then deleted, IsActive() is false. + */ + bool IsActive() const; + ON_SubDComponentPtr::Type ComponentType() const; class ON_SubDComponentBase* ComponentBase() const; @@ -317,25 +693,70 @@ public: class ON_SubDEdge* Edge() const; class ON_SubDFace* Face() const; - ON_SubDVertexPtr VertexPtr() const; - ON_SubDEdgePtr EdgePtr() const; - ON_SubDFacePtr FacePtr() const; + const ON_SubDVertexPtr VertexPtr() const; + const ON_SubDEdgePtr EdgePtr() const; + const ON_SubDFacePtr FacePtr() const; - ON_COMPONENT_INDEX ComponentIndex() const; + unsigned int ComponentId() const; + + const ON_COMPONENT_INDEX ComponentIndex() const; + + const ON_3dPoint ControlNetCenterPoint() const; + const ON_BoundingBox ControlNetBoundingBox() const; + + /* + Returns: + A value suitable for hash table used based on the component type and id. + */ + ON__UINT16 Hash16FromTypeAndId() const; + + + /* + Returns: + A value suitable for hash table used based on the value of ComponentBase(). + */ + ON__UINT32 Hash32FromPointer() const; /* Returns: 0 or 1. - The interpretation of the mark varies depending on the context. - For vertices, this is the vertex mark. - For edges, this is generally an index into ON_SubDEdge.m_vertex[] - or a direction flag with 1 indicating a reversed direction. - For face, this is generally an orientation flag with 1 indicating - a reversed (clockwise) orientation. + A runtime bit property on this ON_SubDComponentPtr. + The use of this value varies depending on the context. + Frequently, 0 means the referenced component is being used with its + natural orientation and 1 means the referenced component is being used + with the reverse of its natrual oreientation. */ - ON__UINT_PTR ComponentMark() const; + ON__UINT_PTR ComponentDirection() const; - ON_ComponentStatus Status() const; + /* + Returns: + An ON_SubDComponentPtr referencing the same ON_SubDComponentBase + with ON_SubDComponentPtr.ComponentDirection() = 0. + */ + const ON_SubDComponentPtr ClearComponentDirection() const; + + /* + Returns: + An ON_SubDComponentPtr referencing the same ON_SubDComponentBase + with ON_SubDComponentPtr.ComponentDirection() = 1. + */ + const ON_SubDComponentPtr SetComponentDirection() const; + + /* + Returns: + An ON_SubDComponentPtr referencing the same ON_SubDComponentBase + with ON_SubDComponentPtr.ComponentDirection() = 1. + */ + const ON_SubDComponentPtr SetComponentDirection(ON__UINT_PTR dir) const; + + /* + Returns: + An ON_SubDComponentPtr referencing the same ON_SubDComponentBase + with ComponentDirection() = 1 - this->ComponentDirection(). + */ + const ON_SubDComponentPtr ToggleComponentDirection() const; + + const ON_ComponentStatus Status() const; /* Returns: @@ -368,16 +789,64 @@ public: ON_ComponentStatus status ); - ON_SubDComponentPtr ClearMark() const; + /* + Returns: + The current value of the component mark ( m_status->RuntimeMark() ). + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + */ + bool Mark() const; - ON_SubDComponentPtr SetMark() const; + /* + Description: + Clears (sets to false) the value of the component mark. + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool ClearMark() const; - ON_SubDComponentPtr ToggleMark() const; + /* + Description: + Sets (sets to true) the value of the component mark. + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool SetMark() const; + + /* + Description: + Sets the value of the component mark to bMark. + Parameter: + bMark - [in] + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool SetMark( + bool bMark + ) const; static const ON_SubDComponentPtr CreateNull( ON_SubDComponentPtr::Type component_type, - bool bMark + ON__UINT_PTR component_direction ); static @@ -398,19 +867,19 @@ public: static const ON_SubDComponentPtr Create( const class ON_SubDVertex* vertex, - ON__UINT_PTR vertex_mark + ON__UINT_PTR vertex_direction ); static const ON_SubDComponentPtr Create( const class ON_SubDEdge* edge, - ON__UINT_PTR edge_mark + ON__UINT_PTR edge_direction ); static const ON_SubDComponentPtr Create( const class ON_SubDFace* face, - ON__UINT_PTR face_mark + ON__UINT_PTR face_direction ); static @@ -441,18 +910,190 @@ public: ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; #endif +class ON_CLASS ON_SubDComponentPtrPair +{ +public: + // For performance reasons, m_ptrpair is not initialized and no constructors are declared + // or implemented. If you require initialization, then use x = ON_SubDComponentPtrPair::Null + // or x = ON_SubDComponentPtr::Create(first_ptr,second_ptr). + ON_SubDComponentPtr m_pair[2]; -#if defined(OPENNURBS_SUBD_WIP) +public: + static const ON_SubDComponentPtrPair Create(ON_SubDComponentPtr first_ptr, ON_SubDComponentPtr second_ptr); -#if 1 -// SuD is exported from opennurbs DLL -#define ON_SUBD_CLASS ON_CLASS -#else -// SuD is not exported from opennurbs DLL -#define ON_SUBD_CLASS + /* + Description: + Dictionary order compare using ON_SubDComponentPtr::CompareComponent() on each element. + */ + static int CompareComponent(const ON_SubDComponentPtrPair * lhs, const ON_SubDComponentPtrPair * rhs); + + /* + Description: + Dictionary order compare using ON_SubDComponentPtr::CompareComponentAndDirection() on each element. + */ + static int CompareComponentAndDirection(const ON_SubDComponentPtrPair * lhs, const ON_SubDComponentPtrPair * rhs); + + /* + Description: + Compare first pointer value. + */ + static int CompareFirstPointer(const ON_SubDComponentPtrPair * lhs, const ON_SubDComponentPtrPair * rhs); + + /* + Returns: + A pair with components in the opposite order + */ + const ON_SubDComponentPtrPair SwapPair() const; + + /* + Returns: + First ON_SubDComponentPt in the pair. + */ + const ON_SubDComponentPtr First() const; + + /* + Returns: + Second ON_SubDComponentPt in the pair. + */ + const ON_SubDComponentPtr Second() const; + + /* + Returns: + If both points have the same type, that type is returned. + Otherwise ON_SubDComponentPtr::Type::Unset is returned. + */ + ON_SubDComponentPtr::Type ComponentType() const; + +public: + const static ON_SubDComponentPtrPair Null; + +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; #endif -class ON_SUBD_CLASS ON_SubDComponentRegionIndex +class ON_CLASS ON_SubDComponentPtrPairHashTable : ON_Hash32Table +{ +public: + ON_SubDComponentPtrPairHashTable(); + + ON_SubDComponentPtrPairHashTable(const class ON_SubD& subd); + + ~ON_SubDComponentPtrPairHashTable() = default; + + bool AddComponentPair( + ON_SubDComponentPtr first_component, + ON_SubDComponentPtr second_component + ); + + bool AddVertexPair( + const class ON_SubDVertex* first_v, + const class ON_SubDVertex* second_v + ); + + bool AddEdgePair( + const class ON_SubDEdge* first_e, + const class ON_SubDEdge* second_e + ); + + bool AddEdgePair( + const class ON_SubDEdge* first_e, + const ON_SubDEdgePtr second_eptr + ); + + bool AddFacePair( + const class ON_SubDFace* first_f, + const class ON_SubDFace* second_f + ); + + const ON_SubDComponentPtrPair PairFromSecondComponentPtr( + ON_SubDComponentPtr second_component + ); + + const ON_SubDComponentPtrPair PairFromSecondVertex( + const class ON_SubDVertex* second_v + ); + + const ON_SubDComponentPtrPair PairFromSecondEdge( + const class ON_SubDEdge* second_e + ); + +private: + ON_FixedSizePool m_pairs_fsp; + +private: + ON_SubDComponentPtrPairHashTable(const ON_SubDComponentPtrPairHashTable&) = delete; + ON_SubDComponentPtrPairHashTable& operator=(const ON_SubDComponentPtrPairHashTable&) = delete; +}; + + +class ON_CLASS ON_SubDVertexSurfacePointCoefficient +{ +public: + ON_SubDVertexSurfacePointCoefficient() = default; + ~ON_SubDVertexSurfacePointCoefficient() = default; + ON_SubDVertexSurfacePointCoefficient(const ON_SubDVertexSurfacePointCoefficient&) = default; + ON_SubDVertexSurfacePointCoefficient& operator=(const ON_SubDVertexSurfacePointCoefficient&) = default; + +public: + + // ON_SubDVertexSurfacePointCoefficient::Zero.m_c = 0.0 + static const ON_SubDVertexSurfacePointCoefficient Zero; + + // ON_SubDVertexSurfacePointCoefficient::Nan.m_c = ON_DBL_QNAN + static const ON_SubDVertexSurfacePointCoefficient Nan; + + // ON_SubDVertexSurfacePointCoefficient::Unset.m_c = ON_UNSET_VALUE + static const ON_SubDVertexSurfacePointCoefficient Unset; + + static const ON_SubDVertexSurfacePointCoefficient Create( + const ON_SubDVertex* limit_point_vertex, + const ON_SubDVertex* ring_vertex, + double x + ); + +public: + static int CompareSurfacePointVertexId( + const ON_SubDVertexSurfacePointCoefficient* lhs, + const ON_SubDVertexSurfacePointCoefficient* rhs + ); + + static int CompareRingVertexId( + const ON_SubDVertexSurfacePointCoefficient* lhs, + const ON_SubDVertexSurfacePointCoefficient* rhs + ); + + static int CompareSurfacePointAndRingVertexId( + const ON_SubDVertexSurfacePointCoefficient* lhs, + const ON_SubDVertexSurfacePointCoefficient* rhs + ); + + static int CompareRingAndSurfacePointVertexId( + const ON_SubDVertexSurfacePointCoefficient* lhs, + const ON_SubDVertexSurfacePointCoefficient* rhs + ); + + +public: + const ON_SubDVertex* m_limit_point_vertex = nullptr; + const ON_SubDVertex* m_ring_vertex = nullptr; + // The limit point of m_limit_point_vertex + // = sum of m_c*m_ring_vertex->ControlNetPoint() + // for every point in the ring of m_limit_point_vertex, including m_limit_point_vertex. + double m_c = 0.0; + +public: + unsigned int SurfacePointVertexId() const; + unsigned int RingVertexId() const; + double Coefficient() const; +}; + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray; +#endif + +class ON_CLASS ON_SubDComponentRegionIndex { public: ON_SubDComponentRegionIndex() = default; @@ -586,7 +1227,7 @@ public: ); }; -class ON_SUBD_CLASS ON_SubDComponentRegion +class ON_CLASS ON_SubDComponentRegion { public: ON_SubDComponentRegion() = default; @@ -614,7 +1255,7 @@ public: that run from the level N edge subdivision point to the level N face subdivision point. - m_level0_component = ON_SubDComponentPtr::CreateNull(component_type, bComponentMark), + m_level0_component = ON_SubDComponentPtr::CreateNull(component_type, bComponentDirection?1:0), (m_level0_component.IsNull() will be true) m_level0_component_id = ON_SubDComponentRegion::NewTransientId() m_subdivision_count = subdivision_count, @@ -623,7 +1264,7 @@ public: */ static const ON_SubDComponentRegion CreateSubdivisionRegion( ON_SubDComponentPtr::Type component_type, - bool bComponentMark, + bool bComponentDirection, unsigned short subdivision_count, bool bAssignTransientId ); @@ -657,7 +1298,7 @@ public: Compares m_level0_component.ComponentType(), m_level0_component_id, - m_level0_component.ComponentMark(), + m_level0_component.ComponentDirection(), the entire sub region, and m_level0_component.m_ptr. */ @@ -671,9 +1312,9 @@ public: Compares m_level0_component.ComponentType(), m_level0_component_id, - m_level0_component.ComponentMark(). + m_level0_component.ComponentDirection(). */ - static int CompareTypeIdMark( + static int CompareTypeIdDirection( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ); @@ -683,11 +1324,11 @@ public: Compares m_level0_component.ComponentType(), m_level0_component_id, - m_level0_component.ComponentMark(), + m_level0_component.ComponentDirection(), and the m_region_index[] values for the minimum subdivision count lhs and rhs. */ - static int CompareTypeIdMarkMinimumSubregion( + static int CompareTypeIdDirectionMinimumSubregion( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ); @@ -697,10 +1338,10 @@ public: Compares m_level0_component.ComponentType(), m_level0_component_id, - m_level0_component.ComponentMark(), + m_level0_component.ComponentDirection(), and the entire sub region. */ - static int CompareTypeIdMarkSubregion( + static int CompareTypeIdDirectionSubregion( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ); @@ -837,7 +1478,7 @@ public: static bool IsPersistentId(unsigned int id); }; -class ON_SUBD_CLASS ON_SubDFaceRegion +class ON_CLASS ON_SubDFaceRegion { public: ON_SubDFaceRegion() = default; @@ -884,7 +1525,7 @@ public: }; -class ON_SUBD_CLASS ON_SubDFaceRegionAndNurbs +class ON_CLASS ON_SubDFaceRegionAndNurbs { public: ON_SubDFaceRegionAndNurbs() = default; @@ -901,25 +1542,13 @@ public: }; -/* -Description: - The ON_SubDComponentLocation enum is used when an ON_SubD component - is referenced and it is important to distinguish between the - component's location in the SubD control net and its location - in the SubD limit surface. -*/ -enum class ON_SubDComponentLocation : unsigned char -{ - Unset = 0, - ControlNet = 1, - LimitSurface = 2 -}; + ////////////////////////////////////////////////////////////////////////// // // ON_SubD // -class ON_SUBD_CLASS ON_SubD : public ON_Geometry +class ON_CLASS ON_SubD : public ON_Geometry { ON_OBJECT_DECLARE(ON_SubD); @@ -948,7 +1577,59 @@ public: */ ON__UINT64 RuntimeSerialNumber() const; -#pragma region RH_C_SHARED_ENUM [ON_SubD::VertexTag] [Rhino.Geometry.SubD.SubDVertexTag] [nested:byte] + /* + Returns: + A runtime serial number that is incremented every time a the active level, + vertex location, vertex or edge flag, or subd topology is changed. + */ + ON__UINT64 ContentSerialNumber() const; + + /* + Description: + Change the content serial number. + This should be done ONLY when the active level, + vertex location, vertex or edge flag, or subd topology is changed. + Returns: + The new value of ConentSerialNumber(). + Remarks: + The value can change by any amount and core editing + functions typically take care of changing the content serial number. + A "top level" user of ON_SubD should never need to call this function. + */ + ON__UINT64 ChangeContentSerialNumberForExperts(); + + /* + Description: + Get the SubD appearance (surface or control net); + Returns: + ON_SubDComponentLocation::Surface or ON_SubDComponentLocation::ControlNet. + */ + ON_SubDComponentLocation SubDAppearance() const; + + /* + Description: + Set the SubD appearance (surface or control net). + Parameters: + subd_appearance - [in] + ON_SubDComponentLocation::Surface or ON_SubDComponentLocation::ControlNet. + Remarks: + This makes no changes to the information that defines the SubD. + It does not require regeneration of the ON_SubDMeshFragments. + Application display will need to be updated. + */ + void SetSubDAppearance(ON_SubDComponentLocation subd_appearance) const; + + /* + Description: + Get the SubD appearance (surface or control net); + Returns: + ON_SubDComponentLocation::Surface or ON_SubDComponentLocation::ControlNet. + */ + static ON_SubDComponentLocation ToggleSubDAppearanceValue(ON_SubDComponentLocation subd_appearance); + static ON_SubDComponentLocation DefaultSubDAppearance; // = ON_SubDComponentLocation::Surface + +public: +#pragma region RH_C_SHARED_ENUM [ON_SubD::VertexTag] [Rhino.Geometry.SubDVertexTag] [byte] /// /// SubD::VertexTag identifies the type of subdivision vertex. Different tags use /// different subdivision algorithms to determine where the subdivision point and @@ -958,15 +1639,17 @@ public: enum class VertexTag : unsigned char { /// - /// Not a valid vertex tag and the default value for ON_SubDVertex::m_vertex_tag. - /// This encourages developers to thoughtfully initialize ON_SubDVertex::m_vertex_tag. + /// Not a valid vertex tag and the default value for ON_SubDVertex.m_vertex_tag. + /// This encourages developers to thoughtfully initialize ON_SubDVertex.m_vertex_tag + /// or use ON_SubD.UpdateAllTagsAndSectorCoefficients() to automatically set the + /// m_vertex_tag values at an appropriate time. /// Unset = 0, /// /// Must be an interior vertex. - /// All edges ending at a smooth vertex must be tagged as ON_SubD::EdgeTag::Smooth - /// and have 2 faces. + /// All edges attached to a smooth vertex must be tagged as ON_SubD::EdgeTag::Smooth + /// and must have 2 faces. /// Smooth = 1, @@ -975,6 +1658,9 @@ public: /// Exactly two edges ending at a crease vertex must be tagged as ON_SubD::EdgeTag::Crease and may /// have 1 or 2 faces. /// All other edges ending at a crease must be tagged as tagON_SubD::EdgeTag::Smooth and have 2 faces. + /// Below P = ON_SubDVertex.ControlNetPoint() and Ai = ON_SubDVertex.Edge(i)->OtherEndVertex()->ControlNetPoint(). + /// A crease vertex subdivision point is (6*P + A1 + A2)/8. + /// A crease vertex limit surface point is (4*P + A1 + A2)/6. /// Crease = 2, @@ -983,14 +1669,16 @@ public: /// The location of a corner vertex is fixed. /// The all subdivision points and the limit point are at the initial vertex location. /// The edges ending at a corner vertex can be smooth or crease edges. + /// A corner vertex subdivision point is P where P = ON_SubDVertex.ControlNetPoint(). + /// A corner vertex limit surface point is P where P = ON_SubDVertex.ControlNetPoint(). /// Corner = 3, /// /// Must be an interior vertex. - /// Every edge ending at a dart vertex must have 2 faces. - /// Exactly one edge ending at a dart vertex must be tagged as ON_SubD::EdgeTag::Crease - /// and every otherr edge must be tagged as tagON_SubD::EdgeTag::smooth. + /// Every edge attached to a dart vertex must have 2 faces. + /// Exactly one edge attached to a dart vertex must be tagged as ON_SubD::EdgeTag::Crease + /// and every other attached edge must be tagged as tagON_SubD::EdgeTag::smooth. /// Dart = 4 }; @@ -1011,31 +1699,9 @@ public: ON_SubD::VertexTag vertex_tag ); -#pragma region RH_C_SHARED_ENUM [ON_SubD::EdgeTag] [Rhino.Geometry.SubD.SubDEdgeTag] [nested:byte] - /// - /// SubD::EdgeTag identifies the type of subdivision edge. Different tags use - /// different subdivision algorithms to determine where the subdivision point is located. - /// - enum class EdgeTag : unsigned char - { - /// - /// Not a valid edge tag and the default value for ON_SubDEdge::m_edge_tag. - /// This encourages developers to thoughtfully initialize ON_SubDEdge::m_edge_tag. - /// - Unset = 0, - - /// - /// One or two of the edge's vertices must be tagged as ON_SubD::VertexTag::Smooth. - /// The edge must have exactly two faces. - /// - Smooth = 1, - - /// - /// Both of the edge's vertices must be tagged as not ON_SubD::VertexTag::Smooth. - /// The edge can have any number of faces. - /// - Crease = 2, + #if 0 + // .NET code that generates enums in hash pragma region R-H_C_S-H-ARED_ENUM cannot handle if 0. /// /// Reserved for version 2 of the ON_SubD project. /// Currently this tag is not used and is invalid. @@ -1049,24 +1715,57 @@ public: /// ON_SubDEdge::m_sharpness = 1 is identical to ON_SubD::EdgeTag::Crease. /// Sharp = 3, +#endif + +#pragma region RH_C_SHARED_ENUM [ON_SubD::EdgeTag] [Rhino.Geometry.SubDEdgeTag] [byte] + /// + /// SubD::EdgeTag identifies the type of subdivision edge. Different tags use + /// different subdivision algorithms to calculate the subdivision point. + /// + enum class EdgeTag : unsigned char + { + /// + /// Not a valid edge tag and the default value for ON_SubDEdge.m_edge_tag. + /// This encourages developers to thoughtfully initialize ON_SubDEdge.m_edge_tag. + /// or use ON_SubD.UpdateAllTagsAndSectorCoefficients() to automatically set the + /// m_edge_tag values at an appropriate time. + /// + Unset = 0, + + /// + /// At least one the edge's vertices must be tagged as ON_SubD::VertexTag::Smooth. + /// The edge must have exactly two faces. + /// The edge's subdivision point is (A1 + A2 + S(f1) + S(f2))/4, where + /// Ai = ON_SubDEdge.Vertex(i)->ControlNetPoint() and + /// S(fi) = ON_SubDEdge.Face(i)->SubdivisionPoint(). + /// + Smooth = 1, + + /// + /// Both of the edge's vertices must be tagged as ON_SubD::VertexTag::Dart, + /// ON_SubD::VertexTag::Crease, or ON_SubD::VertexTag::Corner. + /// (The vertex tags can be different.) The edge can have any number of faces. + /// The edge's subdivision point is (A1+A2)/2 where Ai = ON_SubDEdge.Vertex(i)->ControlNetPoint(). + /// + Crease = 2, /// - /// This tag appears only on edges that have exactly two neighboring faces - /// and neither end vertex is tagged as ON_SubD::VertexTag::Smooth. - /// The level 1 subdivision point for a level 0 edge tagged as ON_SubD::EdgeTag::X + /// This tag appears only on level 0 edges that have exactly two neighboring faces + /// and both of the edge's vertices are tagged as ON_SubD::VertexTag::Dart, + /// ON_SubD::VertexTag::Crease, or ON_SubD::VertexTag::Corner. + /// The level 1 subdivision point for a level 0 edge tagged as ON_SubD::EdgeTag::SmoothX /// is the standard smooth edge subdivision point. /// When subdivided, the new subdivision vertex will be tagged /// as ON_SubD::VertexTag::Smooth and the subdivided edges will - /// be tagged as ON_SubD::EdgeTag::Smooth. Thus, the tag ON_SubD::EdgeTag::X - /// should only appear at level 0. + /// be tagged as ON_SubD::EdgeTag::Smooth. + /// The tag ON_SubD::EdgeTag::SmoothX can only appear on a level 0 edge. /// This tag exists because the ON_SubD subdivision /// algorithm requires any edge with both end vertices /// tagged as not smooth must be subdivided at its midpoint. - /// Sector iterators treat "X" edges as smooth. - /// Both edge end weights must be set so the smooth - /// subdivided edges will be valid. + /// Sector iterators treat "SmoothX" edges as smooth. + /// Both edge m_sector_coefficient[] values must be set so the smooth subdivided edges will be valid. /// - X = 4 + SmoothX = 4 }; #pragma endregion @@ -1087,27 +1786,6 @@ public: ); -#pragma region RH_C_SHARED_ENUM [ON_SubD::FacetType] [Rhino.Geometry.SubD.SubDFacetType] [nested:byte] - /// - /// SubD::FacetType reports the default facet type for subdivision algorithms. - /// - enum class FacetType : unsigned char - { - /// Not a valid facet type. - Unset = 0, - - /// Triangle - Tri = 3, - - /// Quadrangle - Quad = 4 - }; -#pragma endregion - - static ON_SubD::FacetType FacetTypeFromUnsigned( - unsigned int facet_type_as_unsigned - ); - //enum class VertexEdgeOrder : unsigned char //{ // unset = 0, @@ -1124,7 +1802,7 @@ public: // unsigned int vertex_edge_order_as_unsigned // ); -#pragma region RH_C_SHARED_ENUM [ON_SubD::VertexFacetType] [Rhino.Geometry.SubD.VertexFacetType] [nested:byte] +#pragma region RH_C_SHARED_ENUM [ON_SubD::VertexFacetType] [Rhino.Geometry.SubDVertexFacetType] [byte] ///Summarizes the number of edges in faces in the whole object. enum class VertexFacetType : unsigned char @@ -1149,69 +1827,40 @@ public: static ON_SubD::VertexFacetType VertexFacetTypeFromUnsigned( unsigned int vertex_facet_type_as_unsigned ); - -#pragma region RH_C_SHARED_ENUM [ON_SubD::SubDType] [Rhino.Geometry.SubD.SubDType] [nested:byte] + +#pragma region RH_C_SHARED_ENUM [ON_SubD::ChainType] [Rhino.Geometry.SubDChainType] [byte] /// - /// Subdivision algorithm. - /// - enum class SubDType : unsigned char + /// SubD::ChainType specifies what edge and vertex tag tests are used when creating edge chains. + /// + enum class ChainType : unsigned char { /// - /// Not a valid subdivision type. + /// Unset. /// Unset = 0, /// - /// Built-in Loop-Warren triangle with Bernstein-Levin-Zorin creases and darts. + /// All types of edges and vertices can be in the chain. /// - TriLoopWarren = 3, + MixedTag = 1, /// - /// Built-in Catmull-Clark quad with Bernstein-Levin-Zorin creases and darts. + /// Every edge in an edge chain has the same smooth/crease property. /// - QuadCatmullClark = 4, + EqualEdgeTag = 2, /// - /// Custom triangle face algorithm. (Not built-in. Provided for use by 3rd party developers.) + /// Every edge in an edge chain has the same smooth/crease edge tag + /// and interior vertices have the corresponding smooth/crease vertex tag. /// - CustomTri = 5, - - /// - /// Custom quad facet algorithm. (Not built-in. Provided for use by 3rd party developers.) - /// - CustomQuad = 6, - - /// - /// Custom algorithm. (Not built-in. Provided for use by 3rd party developers.) - /// - Custom = 7 - - // All values must be <= 15; i.e., (((unsigned char)0xF0U) & subd_type)) must be zero. + EqualEdgeAndVertexTag = 3 }; #pragma endregion - static ON_SubD::SubDType SubDTypeFromUnsigned( - unsigned int subd_type_as_unsigned - ); - - static ON_SubD::SubDType DefaultSubDType(); - - static unsigned int FacetEdgeCount( - ON_SubD::FacetType facet_type - ); - - static unsigned int FacetEdgeCount( - ON_SubD::SubDType subd_type - ); - /* Parameters: sit - [in] vertex sector iterator - component_ring_capacity - [in] - capacity of component_ring[] array - 1 + center_vertex.m_edge_count + center_vertex.m_face_count - will be large enough. component_ring - [out] A sorted list of ON_SubDComponentPtr values are returned in component_ring[] component_ring[0] is the central vertex. @@ -1219,6 +1868,10 @@ public: component_ring[2] and subsequent components with even indices are sector faces. For edge components (i is odd), component_ring[i].ComponentMark() is the index of the center vertex in ON_SubDEge.m_vertex[]. + component_ring_capacity - [in] + capacity of component_ring[] array + 1 + center_vertex.m_edge_count + center_vertex.m_face_count + will be large enough. Returns: Number of components set in component_ring[]. @@ -1246,8 +1899,8 @@ public: */ static unsigned int GetSectorComponentRing( const class ON_SubDSectorIterator& sit, - size_t component_ring_capacity, - ON_SubDComponentPtr* component_ring + ON_SubDComponentPtr* component_ring, + size_t component_ring_capacity ); /* @@ -1306,8 +1959,8 @@ public: ); static bool ComponentRingIsValid( - size_t component_ring_count, - const ON_SubDComponentPtr* component_ring + const ON_SubDComponentPtr* component_ring, + size_t component_ring_count ); /* @@ -1315,33 +1968,29 @@ public: Number of points in the subdivision ring or 0 if the call fails. */ static unsigned int GetSectorSubdivsionPointRing( - ON_SubD::SubDType subd_type, - size_t component_ring_count, const ON_SubDComponentPtr* component_ring, + size_t component_ring_count, + double* point_ring, size_t point_ring_capacity, - size_t point_ring_stride, - double* point_ring + size_t point_ring_stride ); static unsigned int GetSectorSubdivisionPointRing( - ON_SubD::SubDType subd_type, - size_t component_ring_count, const ON_SubDComponentPtr* component_ring, + size_t component_ring_count, ON_SimpleArray& subd_point_ring ); static unsigned int GetSectorPointRing( - ON_SubD::SubDType subd_type, bool bSubdivideIfNeeded, - size_t component_ring_count, const ON_SubDComponentPtr* component_ring, + size_t component_ring_count, + double* subd_point_ring, size_t subd_point_ring_capacity, - size_t subd_point_ring_stride, - double* subd_point_ring + size_t subd_point_ring_stride ); static unsigned int GetSectorPointRing( - ON_SubD::SubDType subd_type, bool bSubdivideIfNeeded, size_t component_ring_count, const ON_SubDComponentPtr* component_ring, @@ -1349,16 +1998,14 @@ public: ); static unsigned int GetSectorPointRing( - ON_SubD::SubDType subd_type, bool bSubdivideIfNeeded, const class ON_SubDSectorIterator& sit, + double* point_ring, size_t point_ring_capacity, - size_t point_ring_stride, - double* point_ring + size_t point_ring_stride ); static unsigned int GetSectorPointRing( - ON_SubD::SubDType subd_type, bool bSubdivideIfNeeded, const class ON_SubDSectorIterator& sit, ON_SimpleArray& point_ring @@ -1400,66 +2047,19 @@ public: you want a crash proof call. */ static unsigned int GetQuadSectorPointRing( - ON_SubD::SubDType subd_type, bool bFirstPass, bool bSecondPass, const class ON_SubDVertex* vertex0, - size_t component_ring_count, const class ON_SubDComponentPtr* component_ring, - size_t point_ring_stride, - double* point_ring - ); - - /* - Parameters: - subd_type - [in] - A tri based subdivision algorithm. - bFirstPass - [in] - If bFirstPass is true and the components are in standard form for the vertex - and subdivision type, then locations of the component vertices opposite the - center vertex are returned in the point ring. - bSecondPass - [in] - If bSecondPass is true and the first pass is disable or does not succeed, - then the component subdivision locations are returned in the point ring. - vertex0 - [in] - If not null, then vertex0->m_edges and vertex0->m_faces must - be radially sorted and span a single sector and component_ring[] - is ignored. - component_ring_count - [in] - If vertex0 is null, then component_ring_count specifies the number - of components in the component_ring[] array. - component_ring[] - [in] - If vertex0 is null, then component_ring[0] is the central vertex, - component_ring[1] and subsequent components with odd indices are - sector edges, component_ring[2] and subsequent components with even - indices are sector faces, all sorted radially. - point_ring_stride - [in] - point_ring - [out] - point locations are returned here. - Returns: - Number of points in the subdivision ring or 0 if the call fails. - The number of points is 1 + ON_SubD::ComponentRingEdgeCount(component_ring_count). - Remarks: - No validation checking is performed. This function will crash - if the input is not valid. Call GetSubdivisionPointRing() if - you want a crash proof call. - */ - static unsigned int GetTriSectorPointRing( - ON_SubD::SubDType subd_type, - bool bFirstPass, - bool bSecondPass, - const class ON_SubDVertex* vertex0, size_t component_ring_count, - const class ON_SubDComponentPtr* component_ring, - size_t point_ring_stride, - double* point_ring + double* point_ring, + size_t point_ring_stride ); static const class ON_SubDVertex* SubdivideSector( - ON_SubD::SubDType subd_type, const class ON_SubDVertex* center_vertex, - size_t component_ring_count, const class ON_SubDComponentPtr* component_ring, + size_t component_ring_count, class ON_SubD_FixedSizeHeap& fsh ); @@ -1477,46 +2077,82 @@ public: unsigned int sector_face_count ); - /* - Returns: - Type of facets the basic subdivision algorithm requires. - ON_SubD::FacetType::Quad if subd_type is ON_SubD::SubDType::TriLoopWarren. - ON_SubD::FacetType::Tri if subd_type is ON_SubD::SubDType::QuadCatmullClark. - ON_SubD::FacetType::Unset otherwise. - Remark: - All built in subdivision algorithm will handle faces with 3 or more edges. - */ - static ON_SubD::FacetType FacetTypeFromSubDType( - ON_SubD::SubDType subd_type - ); +#pragma region RH_C_SHARED_ENUM [ON_SubD::SubDFriendlyKnotType] [Rhino.Geometry.SubDFriendlyKnotType] [byte] + /// + /// ON_SubD::SubDFriendlyKnotType identifies the types of subd friendly NURBS knot vectors. + /// SubD friendly NURBS curves and surfacaes are always cubic and nonrational. + /// Any time there is a multiple knot, the 2nd derivative is zero at the corresponding parameter. + /// SubD friendly NURBS curves are either periodic or have zero 2nd derivative at the ends. + /// + enum class SubDFriendlyKnotType : unsigned char + { + /// + /// Not a valid type. Used to indicate the type has not been set and to encourage developers to explicitly specify a type. + /// + Unset = 0, - static ON_SubD::SubDType SubDTypeFromFacetType( - ON_SubD::FacetType facet_type - ); + /// + /// NURBS knot vector is an unclamped uniform cubic knot vector. + /// Every knot interval has the same length. + /// Every knot has multiplicity 1. + /// + UnclampedUniform = 1, - static bool PointRingHasFacePoints( - ON_SubD::SubDType subd_type - ); + /// + /// NURBS knot vector is a clamped uniform cubic knot vector. + /// Every interior knot interval has the same length. + /// End knots have multiplicity 3 and interior knots have multiplicity 1. + /// + ClampedUniform = 2, + + /// + /// NURBS knot vector is a clamped piecewise uniform cubic knot vector. + /// All nonzero knot intervals have the same length. + /// End knots have multiplicity 3 and interior knots have multiplicity 1 or 3. + /// Interior knots with multiplicity 3 correspond to interior SubD creases. + /// + ClampedPiecewiseUniform = 4, + + /// + /// NURBS knot vector is not subd friendly. + /// + Unfriendly = 127 + }; + #pragma endregion /* + Parameters: + order - [in] + NURBS knot vector order. + cv_count - [in] + Number of NURBS knot vector control points. + knots - [in] + NURBS knot vector. This is an array of (cv_count+2) knot values. Returns: - true if facet_type is ON_SubD::FacetType::Tri or ON_SubD::FacetType::Quad. - */ - static bool IsQuadOrTriFacetType( - ON_SubD::FacetType facet_type - ); + SubD friendly knot vector type. + Remarks: + If order is not 4, cv_count is not valid, or knot is nullptr, then + ON_SubD::SubDFriendlyKnotType::Unfriendly is returned. + */ + static ON_SubD::SubDFriendlyKnotType NurbsKnotType( + int order, + int cv_count, + const double* knots + ); + + static ON_SubD::SubDFriendlyKnotType NurbsKnotType( + int order, + int cv_count, + const double* knots, + ON_SimpleArray* triple_knots + ); - /* - Returns: - true if subd_type is ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark. - */ - static bool IsQuadOrTriSubDType( - ON_SubD::SubDType subd_type - ); ON_SubD() ON_NOEXCEPT; virtual ~ON_SubD(); + + /* Description: Creates an independent copy of src. @@ -1700,16 +2336,147 @@ public: level_zero_mesh - [in] from_mesh_parameters - [in] To get the smoothest possible result, pass nullptr - or ON_SubDFromMeshOptions::Smooth. To get a sub-D with interior - creases use other static ON_SubDFromMeshOptions values or + or ON_ToSubDParameters::Smooth. To get a sub-D with interior + creases use other static ON_ToSubDParameters values or create one with custom settings. */ static ON_SubD* CreateFromMesh( const class ON_Mesh* level_zero_mesh, - const class ON_SubDFromMeshOptions* from_mesh_parameters, + const class ON_ToSubDParameters* from_mesh_parameters, ON_SubD* subd ); + /* + Description: + Creates a SubD box + Parameters: + corners - [in] + Box corners. + The bottom quad is specified by the first 4 points + and the top quad specified by the last 4 points. + edge_tag - [in] + If edge_tag = ON_SubD::EdgeTag::Crease, then the box will have + creases and corners. Otherwise the box will be smooth. + facecount_x - [in] Number of faces in x direction + facecount_y - [in] Number of faces in y direction + facecount_z - [in] Number of faces in z direction + destination_subd [out] - + If destination_subd is not null, make the SubD box there + Returns: + Pointer to the resulting SubD if successful + Null for error + */ + static ON_SubD* CreateSubDBox( + const ON_3dPoint corners[8], + ON_SubD::EdgeTag edge_tag, + unsigned int facecount_x, + unsigned int facecount_y, + unsigned int facecount_z, + ON_SubD* destination_subd + ); + + /* + Description: + Creates a SubD sphere with 24 quad faces + Parameters: + sphere - [in] + Location, size and orientation of the sphere + destination_subd [out] - + If destination_subd is not null, make the SubD box there + Returns: + Pointer to the resulting SubD if successful + Null for error + */ + //static ON_SubD* CreateSubDSphere( + // const ON_Sphere sphere, + // ON_SubD* destination_subd); + + /* + Description: + Creates a SubD cylinder + Parameters: + box - [in] + Location, size and orientation of the cylinder + facecount_around - [in] Number of faces around the cylinder + facecount_length - [in] Number of faces in the axis direction + facecouont_z - [in] Number of faces in z direction + destination_subd [out] - + If destination_subd is not null, make the SubD box there + Returns: + Pointer to the resulting SubD if successful + Null for error + */ + //static ON_SubD* CreateSubDCylinder( + // const ON_Cylinder& cylinder, + // unsigned int facecount_around, + // unsigned int facecount_length, + // ON_SubD* destination_subd); + + /* + Description: + Creates a SubD cone + Parameters: + cone - [in] + Location, size and orientation of the cone + facecount_around - [in] Number of faces around the cone + facecount_length - [in] Number of faces in the axis direction + destination_subd [out] - + If destination_subd is not null, make the SubD cone there + Returns: + Pointer to the resulting SubD if successful + Null for error + */ + //static ON_SubD* CreateSubDCone( + // const ON_Cone& cone, + // unsigned int facecount_around, + // unsigned int facecount_length, + // ON_SubD* destination_subd); + + /* + Description: + Creates a SubD truncated cone + Parameters: + cone - [in] + Location, size and orientation of the cone + truncate_param - [in] 0.0 < truncate_param <= 1.0 + Normalized parameter for truncation + 0.0: Base of cone + 1.0: Tip of cone + facecount_around - [in] Number of faces around the cone + facecount_length - [in] Number of faces in the axis direction + destination_subd [out] - + If destination_subd is not null, make the SubD cone there + Returns: + Pointer to the resulting SubD if successful + Null for error + */ + //static ON_SubD* CreateSubDTruncatedCone( + // const ON_Cone& cone, + // const double truncate_param, + // unsigned int facecount_around, + // unsigned int facecount_length, + // ON_SubD* destination_subd); + + /* + Description: + Creates a SubD torus + Parameters: + torus - [in] + Location, size and orientation of the torus + major_facecount - [in] Number of faces around the major axis + minor_facecount - [in] Number of faces around the minor axis + destination_subd [out] - + If destination_subd is not null, make the SubD torus there + Returns: + Pointer to the resulting SubD if successful + Null for error + */ + //static ON_SubD* CreaptSubDTorus( + // ON_Torus& torus, + // unsigned int major_facecount, + // unsigned int minor_facecount, + // ON_SubD* destination_subd); + unsigned int DumpTopology( ON_TextLog& ) const; @@ -1735,8 +2502,6 @@ public: */ void Destroy(); - ON_SubD::SubDType ActiveLevelSubDType() const; - /* Returns: The number of explicitly computed levels that are currently available. @@ -1781,15 +2546,15 @@ public: Description: Get aggregate edge demographics for the subd. Returns: - Bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the subd. + Bitwise or of ON_ComponentAttributes::EdgeAttributes values for every edge in the subd. */ - unsigned int EdgeFlags() const; + unsigned int AggregateEdgeAttributes() const; ///////////////////////////////////////////////////////// // // Component (Vertex, Edge, Face) access // - ON_SubDComponentPtr ComponentPtrFromComponentIndex( + const ON_SubDComponentPtr ComponentPtrFromComponentIndex( ON_COMPONENT_INDEX component_index ) const; @@ -1816,9 +2581,23 @@ public: unsigned int VertexCount() const; const class ON_SubDVertex* FirstVertex() const; + const class ON_SubDVertex* LastVertex() const; + /* + Example: + ON_SubDVertexIterator vit = subd.VertexIterator(); + for ( const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + { + ... + } + */ class ON_SubDVertexIterator VertexIterator() const; + /* + Description: + Avoid using this class. It is more efficient to use + an ON_SubDVertexIterator returned by VertexIterator(). + */ class ON_SubDVertexArray VertexArray() const; /* @@ -1841,9 +2620,23 @@ public: unsigned int EdgeCount() const; const class ON_SubDEdge* FirstEdge() const; + const class ON_SubDEdge* LastEdge() const; + /* + Example: + ON_SubDEdgeIterator eit = subd.EdgeIterator(); + for ( const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + ... + } + */ class ON_SubDEdgeIterator EdgeIterator() const; + /* + Description: + Avoid using this class. It is more efficient to use + an ON_SubDEdgeIterator returned by EdgeIterator(). + */ class ON_SubDEdgeArray EdgeArray() const; /* @@ -1866,9 +2659,23 @@ public: unsigned int FaceCount() const; const class ON_SubDFace* FirstFace() const; + const class ON_SubDFace* LastFace() const; + /* + Example: + ON_SubDFaceIterator fit = subd.FaceIterator(); + for ( const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + ... + } + */ class ON_SubDFaceIterator FaceIterator() const; + /* + Description: + Avoid using this class. It is more efficient to use + an ON_SubDFaceIterator returned by FaceIterator(). + */ class ON_SubDFaceArray FaceArray() const; /* @@ -1966,22 +2773,143 @@ public: ON_ComponentStatus status_to_copy ) const; + /* + Description: + Delete components in cptr_list[]. + If a vertex is in cptr_list[], the vertex and every edge and face attached + to the vertex are deleted. + If an edge is in cptr_list[], the edge and every face attached + to the edge are deleted. + If a face is in cptr_list[], the face is deleted. + Parameters: + cptr_list - [in] + cptr_count - [in] + length of cptr_list[] array. + bMarkDeletedFaceEdges - [in] + If true, surviving edges attached to delete faces + have their runtmime mark set. + Returns: + 1: some state settings changed on the component. + 1: some state setting changed on the component. + */ bool DeleteComponents( const ON_SubDComponentPtr* cptr_list, - size_t cptr_count + size_t cptr_count, + bool bMarkDeletedFaceEdges ); + bool DeleteComponentsForExperts( + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + bool bDeleteIsolatedEdges, + bool bUpdateTagsAndCoefficients, + bool bMarkDeletedFaceEdges + ); + + + ///////////////////////////////////////////////////////// + // + // Topology Queries + // + + /* + Description: + Determine solid orientation of the active level. + Returns: + +2 subd is a solid but orientation cannot be computed + +1 subd is a solid with outward facing normals + -1 subd is a solid with inward facing normals + 0 subd is not a solid + See Also: + ON_SubD::IsSolid + */ + int SolidOrientation() const; + + /* + Description: + Test subd to see if the active level is a solid. + A "solid" is a closed oriented manifold. + Returns: + true subd is a solid + fals subd is not a solid + See Also: + ON_SubDp::SolidOrientation + ON_SubDp::IsManifold + */ + bool IsSolid() const; + + /* + Description: + Test subd to see if the active level is an oriented manifold. + Parameters: + bIsOriented - [out] + True if every edge that has two faces is oriented. + Note that non-manifold edges are ignored. + bHasBoundary - [in] + True if there is at least one edge with a single face. + Returns: + True if the subd is a manifold (has at lease one face and every edge has 1 or 2 faces). + False if the subd is not a manifold (has no faces or at least one edge with 0 or 3 or more faces) + See Also: + ON_SubDp::IsSolid + */ + bool IsManifold( + bool& bIsOriented, + bool& bHasBoundary + ) const; + + bool IsManifold() const; + + + + /* + Description: + Automatically get a boundary from a seed edge. + Parameters: + first_edge - [in] + An edge with FaceCount() <= 1. + The search for the second edge occurs and first_edge.RelativeVertex(1) + and all edges added to boundary_edge_chain[] have FaceCount() = first_edge.Edge()->FaceCount(). + bUseEdgeMarks -[in] + If true, only unmarked edges will be added to boundary_edge_chain[] + and they will be marked when added to boundary_edge_chain[]. + boundary_edge_chain - [out] + Edge chain beginning with first_edge. + When true is returned, boundary_edge_chain[] has 3 or more edges and is a closed loop. + When false is returned, boundary_edge_chain[] will contain an open chain with 0 or more edges. + Returns: + true if boundary_edge_chain[] is a closed loop of 3 or more boundary edges. + */ + static bool GetBoundaryEdgeChain( + ON_SubDEdgePtr first_edge, + bool bUseEdgeMarks, + ON_SimpleArray< ON_SubDEdgePtr >& boundary_edge_chain + ); + ///////////////////////////////////////////////////////// // // Editing tools // unsigned int MergeColinearEdges( + bool bMergeBoundaryEdges, + bool bMergeInteriorCreaseEdges, + bool bMergeInteriorSmoothEdges, double distance_tolerance, double maximum_aspect, double sin_angle_tolerance ); + /* + Description: + Merge consecutive edges into a single edge. + eptr0 - [in] + first edge (will not be deleted) + eptr1 - [in] + second edge (will be deleted if edges can be merged) + Returns: + Merged edge (eptr0) or ON_SubDEdgePtr::Null if edges could not be merged + */ ON_SubDEdgePtr MergeEdges( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 @@ -1993,28 +2921,13 @@ public: ); // returns true if all facets are consistently oriented - bool IsOriented( - unsigned int level_index - ) const; + bool IsOriented() const; // reverses the orientation of all facets - bool ReverseOrientation( - unsigned int level_index - ) const; + bool ReverseOrientation() const; // Attempts to orient all facet to match the first facet. - bool Orient( - unsigned int level_index - ) const; - - /* - Description: - Interior vertices (smooth and dart) must have at least three faces. - Concave corner vertices must have at least two faces. - */ - bool RepairInvalidSectors( - unsigned int level_index - ); + bool Orient() const; /* Description: @@ -2030,6 +2943,9 @@ public: then the edge's midpoint is used. Returns: A pointer to the new edge or nullptr if the input is not valid. + Remarks: + After all editing operations are completed, you must call this->UpdateEdgeSectorCoefficients(true) before + evaluation. */ const class ON_SubDEdge* SplitEdge( class ON_SubDEdge* edge, @@ -2051,8 +2967,7 @@ public: The inserted edge runs from face->Vertex(fvi0) to face->Vertex(fvi1). ON_SubDEdge.Face(0) is the original face and ON_SubDEdge::Face(1) is the added face. - The first edge of the input face remains the first edge of face. - The inserted edge is the first edge of the added face. + The first edge of both faces is the inserted edge. */ const class ON_SubDEdge* SplitFace( class ON_SubDFace* face, @@ -2060,15 +2975,56 @@ public: unsigned int fvi1 ); - const class ON_SubDVertex* TriangulateFace( - class ON_SubDFace* face + /* + Description: + Split a face into two faces by inserting and edge connecting the + specified vertices. + Parameters: + face - [in] + A face with at least four edges. + v0 - [in] + v1 - [in] + Vertices on the face boundary. + Returns: + A pointer to the inserted edge. + The inserted edge runs from v0 to v1. + ON_SubDEdge.Face(0) is the original face and ON_SubDEdge::Face(1) is + the added face. + The first edge of the input face remains the first edge of face. + The inserted edge is the first edge of the added face. + */ + const class ON_SubDEdge* SplitFace( + class ON_SubDFace* face, + const class ON_SubDVertex* v0, + const class ON_SubDVertex* v1 ); - const class ON_SubDFace* MergeFaces( - class ON_SubDEdge* edge + /* + Description: + Replace a face with a triangle fan by adding a single new vertex at fan_center_point + and adding tringle made from the face's edes to the center point. + Parameters: + face - [in] + This face is replaced with a triangle fan and becomes the first triangle in the fan. + fan_center_point - [in] + If valid, this point is used as the fan's center. + Otherwise the centriod of the face's vertices is used s the fan's center. + When in doubt, pass ON_3dPoint::UnsetPoint. + bMarkFaces - [in] + If true, face and new triangles are marked. + Existing marks are not modified. + Returns: + If successfull, the new vertex at the center of the triangle fan. + Otherwise, nullptr is returned. + */ + const class ON_SubDVertex* ReplaceFaceWithTriangleFan( + class ON_SubDFace* face, + ON_3dPoint fan_center_point, + bool bMarkFaces ); + /* Description: When finished editing a SubD, call this function to delete all cached evaluation @@ -2175,9 +3131,54 @@ public: bool bUnsetSectorCoefficientsOnly ); + /* Descripiton: - Clears the ON_ComponentState + Clears the m_status.RuntimeMark() for every vertex, edge and face. + Returns: + Number of marks that were cleared. + */ + unsigned int ClearComponentMarks() const; + + /* + Descripiton: + Clears the m_status.RuntimeMark() for every vertex. + Returns: + Number of marks that were cleared. + */ + unsigned int ClearVertexMarks() const; + + /* + Descripiton: + Clears the m_status.RuntimeMark() for every edge. + Returns: + Number of marks that were cleared. + */ + unsigned int ClearEdgeMarks() const; + + /* + Descripiton: + Clears the m_status.RuntimeMark() for every face. + Returns: + Number of marks that were cleared. + */ + unsigned int ClearFaceMarks() const; + + /* + Descripiton: + Selectively clear m_status.RuntimeMark() + Parameters: + bClearVertexMarks - [in] + If true, m_status.ClearRuntimeMark() is called for every vertex. + bClearEdgeMarks - [in] + If true, m_status.ClearRuntimeMark() is called for every edge. + bClearFaceMarks - [in] + If true, m_status.ClearRuntimeMark() is called for every face. + marked_component_list - [out] + If not nullptr, then pointer to components that were marked + are returned in this marked_component_list[] + Returns: + Number of marks that were cleared. */ unsigned int ClearComponentMarks( bool bClearVertexMarks, @@ -2198,6 +3199,43 @@ public: ON_SimpleArray< const class ON_SubDComponentBase* >& marked_component_list ) const; + unsigned int UnselectComponents( + bool bUnselectAllVertices, + bool bUnselectAllEdges, + bool bUnselectAllFaces + ) const; + + /* + Description: + Save the current component status of the indictated subd components. + Parameters: + bGetVertexStatus - [in] + bGetEdgeStatus - [in] + bGetFaceStatus - [in] + bClearStatus - [in] + If true, the bits in status_mask will also be cleared for the components. + status_mask - [in] + Status bits to save. + component_list - [out] + status_list - [out] + component_list[] and status_list[] are parallel arrays for components with + a matching status bit set. + */ + unsigned int GetComponentStatus( + bool bGetVertexStatus, + bool bGetEdgeStatus, + bool bGetFaceStatus, + bool bClearStatus, + ON_ComponentStatus status_mask, + ON_SimpleArray< const class ON_SubDComponentBase* >& component_list, + ON_SimpleArray< ON_ComponentStatus >& status_list + ) const; + + unsigned int SetComponentStatus( + ON_ComponentStatus status_mask, + const ON_SimpleArray< const class ON_SubDComponentBase* >& component_list, + const ON_SimpleArray< ON_ComponentStatus >& status_list + ) const; /* Description: @@ -2206,16 +3244,41 @@ public: xform - [in] ci_list - [in] ci_count - [in] + component_location - [in] + Select between applying the tranform to the control net (faster) + or the surface points (slower). Returns: Number of vertex locations that changed. */ unsigned int TransformComponents( const ON_Xform& xform, const ON_COMPONENT_INDEX* ci_list, - size_t ci_count + size_t ci_count, + ON_SubDComponentLocation component_location ); unsigned int TransformComponents( + const ON_Xform& xform, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + ON_SubDComponentLocation component_location + ); + + /* + Description: + Extrude entire subd bay adding a ring of faces around the boundary and moving the original subd. + */ + unsigned int Extrude( + const ON_Xform& xform + ); + + unsigned int ExtrudeComponents( + const ON_Xform& xform, + const ON_COMPONENT_INDEX* ci_list, + size_t ci_count + ); + + unsigned int ExtrudeComponents( const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, size_t cptr_count @@ -2225,6 +3288,7 @@ public: const ON_Xform& xform, const ON_COMPONENT_INDEX* ci_list, size_t ci_count, + bool bExtrudeBoundaries, bool bPermitNonManifoldEdgeCreation, ON_SubD::EdgeTag original_edge_tag, ON_SubD::EdgeTag moved_edge_tag @@ -2234,11 +3298,25 @@ public: const ON_Xform& xform, const ON_SubDComponentPtr* cptr_list, size_t cptr_count, + bool bExtrudeBoundaries, bool bPermitNonManifoldEdgeCreation, ON_SubD::EdgeTag original_edge_tag, ON_SubD::EdgeTag moved_edge_tag ); +private: + unsigned int Internal_ExtrudeComponents( + const ON_Xform& xform, + const ON_SubDComponentPtr* cptr_list, + size_t cptr_count, + bool bExtrudeBoundaries, + bool bPermitNonManifoldEdgeCreation, + ON_SubD::EdgeTag original_edge_tag, + ON_SubD::EdgeTag moved_edge_tag + ); + +public: + /* Parameters: ci_list - [in] @@ -2301,56 +3379,19 @@ public: ON_SubD::EdgeTag edge_tag ); - /////* - ////Description: - //// Apply the built-in triangle subdivision subdivision algorithm globally. - ////Returns: - //// New level. - ////*/ - ////unsigned int TriSubdivision(); - - ////unsigned int GetSector( - //// const class ON_SubDFace* face, - //// ON__UINT_PTR face_vertex_index, - //// class ON_SubDVertex& sector - //// ) const; - - ////unsigned int GetSector( - //// const class ON_SubDVertex* vertex, - //// const ON_SubDFace* face, - //// class ON_SubDVertex& sector - //// ) const; - - ////unsigned int GetSector( - //// const ON_SubDVertex* vertex, - //// ON_SubDFacePtr face_ptr, - //// class ON_SubDVertex& sector - //// ) const; - - ////unsigned int GetSector( - //// const class ON_SubDVertex* vertex, - //// const class ON_SubDEdge* smooth_edge, - //// ON_SubDVertex& sector - //// ) const; - - ////unsigned int GetSector( - //// const ON_SubDEdge* smooth_edge, - //// ON__UINT_PTR smooth_edge_end_index, - //// ON_SubDVertex& sector - //// ) const; - - ////unsigned int GetSector( - //// ON_SubDEdgePtr smooth_edge_ptr, - //// class ON_SubDVertex& sector - //// ) const; + /* + Description: + Remove all interior creases. + Returns: + Number of edges converted from crease to smooth. + */ + unsigned int RemoveAllCreases(); /* Description: - Apply the built-in subdivision algorithm and save the results + Apply the Catmull-Clark subdivision algorithm and save the results in this ON_SubD. Parameters: - subd_type - [in] - unset will use the current subdivision type. level_index - [in] Level where subdivision starts count - [in] > 0 @@ -2359,19 +3400,35 @@ public: Number of subdivision steps that succeeded. (= count when everything works, < count when input is not valid) */ - bool Subdivide( - ON_SubD::SubDType subd_type, - unsigned int level_index, + bool GlobalSubdivide( unsigned int count ); + bool GlobalSubdivide(); + /* + Description: + Apply the Catmull-Clark subdivision algorithm to the faces in face_list[]. + Parameters: + face_list - [in] + faces to subdivide + face_count - [in] + number of components. Returns: - Active level subdivison type. + true if successful. */ - bool SetSubDType( - ON_SubD::SubDType subd_type - ); + bool LocalSubdivide( + class ON_SubDFace const*const* face_list, + size_t face_count + ); + + bool LocalSubdivide( + const ON_SimpleArray< const class ON_SubDFace* >& face_list + ); + + bool LocalSubdivide( + const ON_SimpleArray& face_list + ); /* Description: @@ -2390,6 +3447,45 @@ public: const double* P ); + /* + Description: + Search for a vertex with a specificed control net point. + Parameters: + control_net_point - [in] + Look for a vertex with this value for ControlNetPoint(). + tolerance - [in] + Use 0.0 when in doubt. + If > 0.0, then the vertex closest to control_net_point + will be returned if the distance from that vertex to control_net_point + is <= distance_tolerance. + Returns: + An ON_SubDVertex pointer or nullptr if none exists. + */ + const class ON_SubDVertex* FindVertex( + const double* control_net_point, + double distance_tolerance + ) const; + + /* + Description: + Search for a vertex with a specificed control net point. If one does not + exist, add a new one. + Parameters: + control_net_point - [in] + Look for a vertex with this value for ControlNetPoint(). + tolerance - [in] + Use 0.0 when in doubt. + If > 0.0, then the vertex closest to control_net_point + will be returned if the distance from that vertex to control_net_point + is <= distance_tolerance. + Returns: + An ON_SubDVertex pointer or nullptr if none exists. + */ + const class ON_SubDVertex* FindOrAddVertex( + const double* control_net_point, + double distance_tolerance + ); + /* Parameters: edge_face_count - [in] @@ -2410,7 +3506,7 @@ public: then ON_SubD::EdgeTag::Crease is returned. If edge_face_count is 2 and both vertex tags are set and both are not ON_SubD::VertexTag::Smooth, - then ON_SubD::EdgeTag::X is returned. + then ON_SubD::EdgeTag::SmoothX is returned. Otherwise, ON_SubD::EdgeTag::Unset is returned. */ @@ -2445,6 +3541,39 @@ public: class ON_SubDVertex* v1 ); + /* + Description: + Search for an edge connecting v0 and v1. + Parameters: + v0 - [in] + v1 - [in] + The edge begins at v0 and ends at v1. + The edge will be on the same level as the vertices. + Returns: + An ON_SubDEdgePtr to a connecting edge or ON_SubDEdgePtr::Null if none exists. + */ + const ON_SubDEdgePtr FindEdge( + const class ON_SubDVertex* v0, + const class ON_SubDVertex* v1 + ) const; + + /* + Description: + Search for an edge connecting v0 and v1. If none exists, then add one. + Parameters: + v0 - [in] + v1 - [in] + The edge begins at v0 and ends at v1. + The edge will be on the same level as the vertices. + Returns: + An ON_SubDEdgePtr to a connecting edge or ON_SubDEdgePtr::Null if none exists. + */ + const ON_SubDEdgePtr FindOrAddEdge( + class ON_SubDVertex* v0, + class ON_SubDVertex* v1 + ); + + /* Description: Add an edge to the subd. @@ -2454,7 +3583,7 @@ public: Edge tag is not known at this time. ON_SubD::EdgeTag::Smooth Smooth edge. If both vertices are tagged as not smooth, the - tag on the returned edge will be ON_SubD::EdgeTag::X. This + tag on the returned edge will be ON_SubD::EdgeTag::SmoothX. This tag is changed to ON_SubD::EdgeTag::Smooth on the first subdivision step. ON_SubD::EdgeTag::Crease. @@ -2496,38 +3625,239 @@ public: class ON_SubDVertex* v1, double v1_sector_coefficient ); - - class ON_SubDFace* AddFace( - unsigned int edge_count, - const class ON_SubDEdgePtr* edge - ); + /* + Parameters: + edge0 - [in] + edge1 - [in] + edge2 - [in] + The face will be oriented so the boundary has the edges + in the order (edge0, edge1, edge2). + Consecutive edges must have a single common vertex. + The orientations of the ON_SubDEdgePtr elements in the + face's edge list are automatically calculated. + Returns: + A pointer to the added face. + The returned face is managed by the subd. + */ class ON_SubDFace* AddTriangleFace( - class ON_SubDEdge* edge0, bool bReverseEdge0, - class ON_SubDEdge* edge1, bool bReverseEdge1, - class ON_SubDEdge* edge2, bool bReverseEdge2 + class ON_SubDEdge* edge0, + class ON_SubDEdge* edge1, + class ON_SubDEdge* edge2 ); + /* + Parameters: + edge_count - [in] + Must be >= 3. + edge0 - [in] + edge1 - [in] + edge2 - [in] + The ON_SubDEdgePtr parameters must be oriented so that for consecutive pairs of edges, + edge0.RelativeVertex(1) and edges1.RelativeVertex(0) are same vertex. + The face will be oriented so the boundary has the edges + in the order (edge0, edge1, edge2). + Returns: + A pointer to the added face. + The returned face is managed by the subd. + */ class ON_SubDFace* AddTriangleFace( ON_SubDEdgePtr edge0, ON_SubDEdgePtr edge1, ON_SubDEdgePtr edge2 ); + /* + Parameters: + edge0 - [in] + edge1 - [in] + edge2 - [in] + edge3 - [in] + The face will be oriented so the boundary has the edges + in the order (edge0, edge1, edge2. edge3). + Consecutive edges must have a single common vertex. + The orientations of the ON_SubDEdgePtr elements in the + face's edge list are automatically calculated. + Returns: + A pointer to the added face. + The returned face is managed by the subd. + */ class ON_SubDFace* AddQuadFace( - class ON_SubDEdge* edge0, bool bReverseEdge0, - class ON_SubDEdge* edge1, bool bReverseEdge1, - class ON_SubDEdge* edge2, bool bReverseEdge2, - class ON_SubDEdge* edge3, bool bReverseEdge3 + class ON_SubDEdge* edge0, + class ON_SubDEdge* edge1, + class ON_SubDEdge* edge2, + class ON_SubDEdge* edge3 ); - + + /* + Parameters: + edge_count - [in] + Must be >= 3. + edge0 - [in] + edge1 - [in] + edge2 - [in] + edge3 - [in] + The ON_SubDEdgePtr parameters must be oriented so that for consecutive pairs of edges, + edge0.RelativeVertex(1) and edges1.RelativeVertex(0) are same vertex. + The face will be oriented so the boundary has the edges + in the order (edge0, edge1, edge2,edge3). + Returns: + A pointer to the added face. + The returned face is managed by the subd. + */ class ON_SubDFace* AddQuadFace( ON_SubDEdgePtr edge0, ON_SubDEdgePtr edge1, ON_SubDEdgePtr edge2, ON_SubDEdgePtr edge3 ); + + /* + Parameters: + edges[] - [in] + edges[] must have 3 or more elements. + edges[i] and edges[(i+1)%edge_count] must have a single common vertex. + The face will be oriented so the boundary has the edges + in the order (edges[0], edges[1], ..., edges[edge_count-1]). + The orientations of the ON_SubDEdgePtr elements in the + face's edge list are automatically calculated. + Returns: + A pointer to the added face. + The returned face is managed by the subd. + */ + class ON_SubDFace* AddFace( + const ON_SimpleArray& edges + ); + + /* + Parameters: + edges[] - [in] + edges[] must have 3 or more elements. + The ON_SubDEdgePtr parameters must be oriented so that + edges[i].RelativeVertex(1) and edges[(i+1)%edge_count].RelativeVertex(0) + are the same vertex. + The face will be oriented so the boundary has the edges + in the order (edges[0], edges[1], ..., edges[edge_count-1]). + Returns: + A pointer to the added face. + The returned face is managed by the subd. + */ + class ON_SubDFace* AddFace( + const ON_SimpleArray& edges + ); + + /* + Parameters: + edge_count - [in] + Must be >= 3. + edges[] - [in] + edges[i] and edges[(i+1)%edge_count] must have a single common vertex. + The face will be oriented so the boundary has the edges + in the order (edges[0], edges[1], ..., edges[edge_count-1]). + The orientations of the ON_SubDEdgePtr elements in the + face's edge list are automatically calculated. + Returns: + A pointer to the added face. + The returned face is managed by the subd. + */ + class ON_SubDFace* AddFace( + class ON_SubDEdge * const * edges, + unsigned int edge_count + ); + + /* + Parameters: + edge_count - [in] + Must be >= 3. + edges[] - [in] + The ON_SubDEdgePtr parameters must be oriented so that + edges[i].RelativeVertex(1) and edges[(i+1)%edge_count].RelativeVertex(0) + are the same vertex. + The face will be oriented so the boundary has the edges + in the order (edges[0], edges[1], ..., edges[edge_count-1]). + Returns: + A pointer to the added face. + The returned face is managed by the subd. + */ + class ON_SubDFace* AddFace( + const class ON_SubDEdgePtr* edges, + unsigned int edge_count + ); + + /* + Parameters: + candidate_face - [in] + If this face was previously deleted and is not currently used, + it will be reused. This allows editing operations that merge + faces to have the result of the merge be returned in a face + that was part of the input set. + edge_count - [in] + Must be >= 3. + edge[] - [in] + The edge list must be sorted and correct oriented + (edge[i].RelativeVertex(1) == edge[(i+1)%edge_count].RelativeVertex(0)). + */ + class ON_SubDFace* AddFace( + const ON_SubDFace* candidate_face, + const class ON_SubDEdgePtr* edge, + unsigned int edge_count + ); + +public: + +#pragma region RH_C_SHARED_ENUM [ON_SubD::PatchStyle] [Rhino.Geometry.SubDPatchStyle] [byte] + /// + /// SubD::PatchStyle identifies the style of patch used to fill holes. + /// + enum class PatchStyle : unsigned char + { + /// + /// Not a valid style. + /// This encourages developers to thoughtfully select a patch style and can + /// be used to indicate a UI control is not initialized. + /// + Unset = 0, + + /// + /// Automatically choose a patch style that will generally create a good looking result. + /// If a hole boundary is not convex, it is triangulated. Otherwise: + /// If a hole has 3 edges, a single triangle face is used. + /// If a hole has 4 edges, a single quad face is used. + /// If a hole has 5 or more edges and an odd number of edges, a triangle fan is used. + /// If a hole has 6 or more edges and an even number of edges, a quad fan is used. + /// + Automatic = 1, + + /// + /// A single face is used under all conditions. + /// + SingleFace = 2, + + /// + /// A triangle fan used under all conditions. The center of the fan + /// is the average of the hole boundary vertex control net points. + /// + TriangleFan = 3, + + /// + /// If the hole boundary has an even mumber of edges, a quad fan is used. + /// Otherwise a triangle fan is used. The center of the fan + /// is the average of the hole boundary vertex control net points. + /// + QuadOrTriangleFan = 4, + + /// + /// The hole boundary is triangluated. + /// Typically this style is selected when a boundary not convex + /// and the other styles produce faces with overlapping regions. + /// + Triangulated = 5 + }; +#pragma endregion + + +public: /* Description: Expert user tool to insert an edge in the face's edge array. @@ -2631,6 +3961,21 @@ public: ON_SubDEdgePtr& removed_edge ); + + /* + Description: + Expert user tool to remove all edge and vertex connnections from a face + Parameters: + face - [in] + Remarks: + This tool is used during editing of a SubD and the + connections are removed even if the result is an invalid face or edge. + It is up to the expert user to make enough changes to create a valid SubD. + */ + bool RemoveFaceConnections( + ON_SubDFace* face + ); + bool GrowVertexEdgeArray( ON_SubDVertex* v, size_t capacity @@ -2648,12 +3993,64 @@ public: size_t capacity ); +////#if defined(OPENNURBS_PLUS) +//// /* +//// Description: +//// Get the limit surface point location and normal for +//// the face's center from the limit mesh grid. +//// Parameters: +//// face - [in] +//// A face in this SubD. +//// P - [out] +//// P = limit surface location or ON_3dPoint::NanPoint if not available. +//// N - [out] +//// N = limit surface unit normal or ON_3dVector::NanVector if not available. +//// Returns: +//// True if the point and normal were set from the limit mesh frament. +//// False if the limit mesh fragment was not found and nan values were returned. +//// */ +//// // TODO - remove this and remove cached limit mesh. +//// bool GetFaceCenterPointAndNormalX( +//// const class ON_SubDFace* face, +//// double* P, +//// double* N +//// ) const; +////#endif +////#if defined(OPENNURBS_PLUS) +//// /* +//// Description: +//// Get the limit surface point location and normal for +//// the edge's midpoint from the limit mesh grid. +//// Parameters: +//// edge - [in] +//// An edge in this SubD. +//// edge_face_index - [in] +//// Index of the face to use for the normal. If the edge is a crease, then +//// each attached face may have a different normal. Pass 0 when in doubt. +//// P - [out] +//// P = limit surface location or ON_3dPoint::NanPoint if not available. +//// N - [out] +//// N = limit surface unit normal or ON_3dVector::NanVector if not available. +//// Returns: +//// True if the point and normal were set from the limit mesh frament. +//// False if the limit mesh fragment was not found and nan values were returned. +//// */ +//// // TODO - remove this and remove cached limit mesh. +//// bool GetEdgeCenterPointAndNormalX( +//// const class ON_SubDEdge* edge, +//// unsigned int edge_face_index, +//// double* P, +//// double* N +//// ) const; +////#endif - - void ClearLimitSurfaceMesh() const; - + /* + Description: + Clear all cached evaluation information (meshes, surface points, boundiang boxes, ...) + that depends on edge tags, vertex tags, and the location of vertex control points. + */ void ClearEvaluationCache() const; @@ -2677,7 +4074,7 @@ public: #pragma region RH_C_SHARED_ENUM [ON_SubD::NurbsSurfaceType] [Rhino.Geometry.SubD.NurbsSurfaceType] [nested:byte] /// - /// ON_SubD::NurbsSurfaceType specifies what type of NURBS surfaces are returned by ON_SubD.GetLimitSurfaceNurbs() + /// ON_SubD::NurbsSurfaceType specifies what type of NURBS surfaces are returned by ON_SubD.GetSurfaceNurbsFragments() /// enum class NurbsSurfaceType : unsigned char { @@ -2719,6 +4116,184 @@ public: + + + +public: + /* + Parameters: + minimum_rectangle_count - [in] + >= 1 minimum number of rectangles in texture domain + image_width - [in] + image_height = [in] + If a texture image size is known, pass it here. Otherwise pass 0.0 for both parameters. + Returns: + Suggested way to partition a texture coodinate domain into rectangles. + ON_2udex.i = "x" count + ON_2udex.j = "y" count + For example (3,2) would mean divide the 2d texture domain into 3 segments across and 2 segments vertically. + */ + static const ON_2udex TextureDomainGridSize( + unsigned minimum_rectangle_count, + double image_width, + double image_height + ); + + static unsigned int TextureImageSuggestedMinimumSize( + ON_2udex grid_size + ); + + /* + Returns: + Suggesting minimum number of pixels for a texture image width and height for this SubD. + */ + unsigned int TextureImageSuggestedMinimumSize() const; + + /* + Parameters: + normalize_texture_domain_delta - [in] + > 0.0 and <= 1.0 + count - [in] + >= 1 number of rectangles + image_width - [in] + image_height = [in] + If a texture image size is known, pass it here. Otherwise pass 0.0 for both parameters. + When available, it is used to avoid have different rectangles share pixels. + quad_texture_domain - [out] + Each quad will have texture domain + minimum point = (x0,y0) + maximum point = (x0+quad_texture_domain,y0+quad_texture_domain) + where (x0,y0) is the normalized coord + quad_texture_delta - [out] + To move from one quad to the next in the same row, increment the x-coordinate by quad_texture_delta. + To move from the last quad in a row to the first quad in the next row, + set x-coordinate = to the next in the same row, increment the x-coordinate by quad_texture_delta. + + Returns: + number of quads per row and column in the region. + */ + static const ON_2udex GetTextureDomainAndDelta( + unsigned minimum_rectangle_count, + double image_width, + double image_height, + ON_2dVector& quad_texture_domain, + ON_2dVector& quad_texture_delta + ); + + + static ON_SubDTextureDomainType TextureDomainTypeFromUnsigned( + unsigned int texture_domain_type_as_unsigned + ); + + static const ON_wString TextureDomainTypeToString( + ON_SubDTextureDomainType texture_domain_type + ); + + + /* + Description: + Set the default texture coordinate domain on each face. + Parameters: + texture_domain_type - [in] + Type of texture coordinates. + If ON_SubDTextureDomainType::Unset or ON_SubDTextureDomainType::Custom, + is passed, the type setting is changed but no changes are made to texture coordinats. + When in doubt and performance may be an issue, pass false. + If true, then, where possible, texture domains are set so + quad grid regions have neighboring texture domains. + On subds with large regions quad grids, this produces a result that + looks better when default surface parameter texture coordinates are used. + However, this requires a calculation that can be slow on subs with lots of faces. + bLazy - [in] + If true and if texture_domain_type == TextureDomainType(), + then nothing is done and true is returned. + bSetFragmentTextureCoordinates + When in doubt, pass true. + If true and if the faces have cached fragments, then after the domains are set the + fragments texture domains and their texture coordinates are set as well. + Remarks: + SubD texture domains and coordinates are a mutable property. + They can be changed by rendering applications as needed. + Call SetTextureCoordinates() to restore them to the default values. + */ + bool SetTextureDomains( + ON_SubDTextureDomainType texture_domain_type, + bool bLazy, + bool bSetFragmentTextureCoordinates + ) const; + + ON_SubDTextureDomainType TextureDomainType() const; + + static bool SetTextureDomains( + ON_SubDFaceIterator& fit, + ON_SubDTextureDomainType texture_domain_type, + bool bSetFragmentTextureCoordinates + ); + + /* + Description: + Use the current default texture domain values on each face + to set the currently cached ON_MeshFragment texture coordinates. + The subd's TextureMappingTag() property is set to + ON_TextureMapping::SurfaceParameterTextureMapping. + Remarks: + SubD texture domains and coordinates are a mutable property. + They can be changed by rendering applications as needed. + Call SetTextureCoordinatesFromFaceDomains() to restore them to the default values. + */ + bool SetTextureCoordinatesFromFaceDomains() const; + + /* + Description: + Use the current default texture domain values on each face + to set the currently cached ON_MeshFragment texture coordinates. + Parameters: + fit - [in] + faces to set. + */ + static bool SetTextureCoordinatesFromFaceDomains( + ON_SubDFaceIterator& fit + ); + + /* + Description: + Use a texture mapping function to set currently cached + ON_SubDMeshFragment m_T[] values. + Parameters: + mapping - [in] + subd_xform - [in] + If not nullptr, the mapping calculation is performed as + if the subd were transformed by subd_xform; the + location of the subd is not changed. + bLazy - [in] + If true and the m_T[] values were set using the same + mapping parameters, then no calculation is performed. + Returns: + True if successful. + Remarks: + SubD texture domains and coordinates are a mutable property. + They can be changed by rendering applications as needed. + Call SetTextureCoordinatesFromFaceDomains() to restore them to the default values. + */ + bool SetTextureCoordinates( + const class ON_TextureMapping& mapping, + const class ON_Xform* subd_xform, + bool bLazy + ) const; + + /* + Returns: + The current texture mapping tag. This tag is set by SetTextureCoordinatesFromFaceDomains() + and SetTextureCoordinates(). + Remarks: + SubD texture domains and coordinates are a mutable property. + They can be changed by rendering applications as needed. + Call SetTextureCoordinatesFromFaceDomains() to restore them to the default values. + */ + const ON_MappingTag TextureMappingTag() const; + + void SetTextureMappingTag(const class ON_MappingTag&) const; + public: /* Description: @@ -2739,8 +4314,8 @@ public: void ShareDimple(const ON_SubD&); void SwapDimple(ON_SubD&); - void ShareDimple(const class ON_SubDLimitMeshImpl&); - void SwapDimple(class ON_SubDLimitMeshImpl& ); + void ShareDimple(const class ON_SubDMeshImpl&); + void SwapDimple(class ON_SubDMeshImpl& ); private: class ON_SubDimple* SubDimple(bool bCreateIfNeeded); @@ -2749,6 +4324,7 @@ private: void CopyHelper(const ON_SubD&); + private: friend class ON_SubDRef; #pragma ON_PRAGMA_WARNING_PUSH @@ -2763,14 +4339,13 @@ public: // The ON_SubD code increments ON_SubD::ErrorCount everytime something // unexpected happens. This is useful for debugging. static unsigned int ErrorCount; -}; - +}; ////////////////////////////////////////////////////////////////////////// // // ON_SubDRef // -class ON_SUBD_CLASS ON_SubDRef +class ON_CLASS ON_SubDRef { #if defined(ON_SUBD_CENSUS) private: @@ -2796,28 +4371,50 @@ public: /* Returns: - Number of references to the ON_SubD, including the one by this ON_SubDRef. + Number of references to the managed ON_SubD, including the one by this ON_SubDRef. */ unsigned int ReferenceCount() const; /* Description: - Allocates a new ON_SubD and has this ON_SubDRef reference it. + Allocates a new empty ON_SubD and has this ON_SubDRef reference it. */ class ON_SubD& NewSubD(); - /* Description: Allocates a new ON_SubD and has this ON_SubDRef reference it. + Parameters: + src - [in] + The new ON_SubD managed by this ON_SubDRef will be a copy of src.SubD(). + Returns: + A reference to the new ON_SubD managed by this ON_SubDRef. */ class ON_SubD& CopySubD( const ON_SubDRef& src ); + + /* + Description: + Allocates a new ON_SubD and has this ON_SubDRef reference it. + Parameters: + src - [in] + The new ON_SubD managed by this ON_SubDRef will be a copy of src. + Returns: + A reference to the new ON_SubD managed by this ON_SubDRef. + */ class ON_SubD& CopySubD( const ON_SubD& src ); + /* + Description: + If ReferenceCount() > 1, then have this ON_SubDRef reference a + new copy. Otherwise do nothing. The result being that this will + be the unique reference to the ON_SubD managed by this ON_SubDRef. + Returns: + A reference to the ON_SubD uniquely managed by this ON_SubDRef. + */ class ON_SubD& UniqueSubD(); /* @@ -2899,7 +4496,7 @@ private: class ON_SubDComponentMarksClearAndRestore { public: - // Constructor saves current component marks on subd. + // Constructor saves current component RuntimeMark() settings. ON_SubDComponentMarksClearAndRestore( ON_SubD& subd ); @@ -2921,15 +4518,17 @@ public: // Call DisableRestore() to prevent the destructor from restoring saved marks. void DisableRestore(); - const ON_SimpleArray< const class ON_SubDComponentBase* >& SavedMarkedComponentList() const; + const ON_SimpleArray< const class ON_SubDComponentBase* >& ComponentList() const; private: ON_SubD m_subd; - ON_SimpleArray< const class ON_SubDComponentBase* > m_saved_marked_component_list; + + ON_SimpleArray< const class ON_SubDComponentBase* > m_component_list; + bool m_bRestore = true; - bool m_bReserved1 = false; - bool m_bReserved2 = false; - bool m_bReserved3 = false; + unsigned char m_reserved1 = 0; + unsigned char m_reserved2 = 0; + unsigned char m_reserved3 = 0; unsigned int m_reserved4 = 0; private: @@ -2942,7 +4541,7 @@ private: // // ON_SubDSectorType // -class ON_SUBD_CLASS ON_SubDSectorType +class ON_CLASS ON_SubDSectorType { public: ON_SubDSectorType() = default; @@ -3051,10 +4650,6 @@ public: double SectorWeight() const; - ON_SubD::SubDType SubDType() const; - - ON_SubD::FacetType FacetType() const; - unsigned int FacetEdgeCount() const; ON_SubD::VertexTag VertexTag() const; @@ -3089,45 +4684,54 @@ public: /* Returns: - a value >= 0 and <= ON_SubDSectorType::MaximumAngleIndex + a value >= 0 and <= ON_SubDSectorType::MaximumCornerAngleIndex */ unsigned int CornerSectorAngleIndex() const; /* Description: - An angle index value of ON_SubDSectorType::MaximumAngleIndex indicates + An angle index value of ON_SubDSectorType::MaximumCornerAngleIndex indicates the angle is 2pi radians. */ - static const unsigned int MaximumAngleIndex; // = 72 + enum : unsigned int + { + MaximumCornerAngleIndex = 72 + }; + + // ON_SubDSectorType::MinimumCornerAngleRadians = (2.0*ON_PI)/((double)(ON_SubDSectorType::MaximumCornerAngleIndex)); + static const double MinimumCornerAngleRadians; + + // ON_SubDSectorType::MaximumCornerAngleRadians = 2.0*ON_PI - ON_SubDSectorType::MinimumCornerAngleRadians; + static const double MaximumCornerAngleRadians; /* Parameters: - angle_radians - [in] (0.0 <= angle_radians <= 2*ON_PI + angle_radians - [in] (0.0 <= angle_radians <= 2*ON_PI) The angle between the bounding crease edges Returns: - angle_index: >= 0 and <= ON_SubDSectorType::MaximumCornerSectorIndex - | angle_radians - angle_index/M * 2pi | <= 1/2 * 1/M * 2pi, - where M = ON_SubDSectorType::MaximumAngleIndex - ON_UNSET_UINT_INDEX - angle_radians is not valid and the calculation failed. + If angle_radians is valid input, then the value angle_index is returned. + The value angle_index is selected so that + (0 < angle_index < ON_SubDSectorType::MaximumCornerSectorIndex) and + fabs(angle_index*angle_quantum - angle_radians) is as small as possible, where + angle_quantum = (2.0*ON_PI)/ON_SubDSectorType::MaximumCornerSectorIndex. + Otherwise ON_UNSET_UINT_INDEX is returned. */ - static unsigned int AngleIndexFromAngleRadians( + static unsigned int CornerAngleIndexFromCornerAngleRadians( double angle_radians ); /* Convert and angle index into radians Parameters: - angle_index - [in] - 0 to ON_SubDSectorType::MaximumAngleIndex. + corner_angle_index - [in] + 0 to ON_SubDSectorType::MaximumCornerAngleIndex. Returns: If angle_index is valid, the corresponding angle in radians is returned. - = angle_index / ON_SubDSectorType::MaximumAngleIndex * 2 * ON_PI - (double division performed) + = (angle_index / ((double)ON_SubDSectorType::MaximumCornerAngleIndex)) * ON_2PI Otherwise ON_UNSET_VALUE is returned. */ - static double AngleRadiansFromAngleIndex( - unsigned int angle_index + static double AngleRadiansFromCornerAngleIndex( + unsigned int corner_angle_index ); /* @@ -3205,13 +4809,11 @@ public: (valence + 1) for tri subds */ static unsigned int SectorPointRingCountFromEdgeCount( - ON_SubD::SubDType subd_type, ON_SubD::VertexTag vertex_tag, unsigned int sector_edge_count ); static unsigned int SectorPointRingCountFromFaceCount( - ON_SubD::SubDType subd_type, ON_SubD::VertexTag vertex_tag, unsigned int sector_face_count ); @@ -3250,8 +4852,6 @@ public: 0: failed to caclulate weight ON_SubDSectorType::UnsetSectorWeight: - subd_type was set ON_SubD::SubDType::Unset - and was required to calculate the weight. This typically happens when a SubD control net is being created and a facet type is not specified. The weights will be calculated at the first subdivision. @@ -3263,17 +4863,14 @@ public: level is being constructed. */ static double CreaseSectorWeight( - ON_SubD::SubDType subd_type, unsigned int sector_face_count ); static double DartSectorWeight( - ON_SubD::SubDType subd_type, unsigned int sector_face_count ); static double CornerSectorWeight( - ON_SubD::SubDType subd_type, unsigned int sector_face_count, double corner_sector_angle_radians ); @@ -3352,7 +4949,6 @@ public: identify. */ static ON_SubDSectorType Create( - ON_SubD::SubDType subd_type, ON_SubD::VertexTag vertex_tag, unsigned int sector_face_count, double corner_sector_angle_radians @@ -3369,7 +4965,6 @@ public: An ON_SubDSectorType for the sector identified by sit. */ static ON_SubDSectorType Create( - ON_SubD::SubDType subd_type, const ON_SubDSectorIterator& sit ); @@ -3386,13 +4981,11 @@ public: An ON_SubDSectorType for the sector containing the face. */ static ON_SubDSectorType Create( - ON_SubD::SubDType subd_type, const class ON_SubDFace* face, unsigned int face_vertex_index ); static ON_SubDSectorType Create( - ON_SubD::SubDType subd_type, const class ON_SubDFace* face, const class ON_SubDVertex* vertex ); @@ -3410,7 +5003,6 @@ public: An ON_SubDSectorType for the sector containing the edge. */ static ON_SubDSectorType Create( - ON_SubD::SubDType subd_type, const class ON_SubDEdge* edge, unsigned int edge_vertex_index ); @@ -3427,7 +5019,6 @@ public: by the input parameters. */ static ON_SubDSectorType CreateSmoothSectorType( - ON_SubD::SubDType subd_type, unsigned int sector_face_count ); @@ -3443,7 +5034,6 @@ public: by the input parameters. */ static ON_SubDSectorType CreateCreaseSectorType( - ON_SubD::SubDType subd_type, unsigned int sector_face_count ); @@ -3459,7 +5049,6 @@ public: by the input parameters. */ static ON_SubDSectorType CreateDartSectorType( - ON_SubD::SubDType subd_type, unsigned int sector_face_count ); @@ -3477,7 +5066,6 @@ public: by the input parameters. */ static ON_SubDSectorType CreateCornerSectorType( - ON_SubD::SubDType subd_type, unsigned int sector_face_count, double sector_corner_angle_radians ); @@ -3498,34 +5086,27 @@ public: For an interior vertex (smooth or dart), the coefficents are ordered so that one iteration of subdivision is given by: - ON_SubD::SubDType::TriLoopWarren case: - S*Transpose(V, E[0], E[1], ..., E[N-1]) - ON_SubD::SubDType::QuadCatmullClark case: - S*Transpose(V, E[0], F[0], E[1], F[1], ..., E[N-1], F[N-1]). + S*Transpose(V, E[0], F[0], E[1], F[1], ..., E[N-1], F[N-1]). For a dart vertex, E[0] is the point at the end of the creased edge. For a boundary vertex (crease or corner), the coefficents are ordered so that one iteration of subdivision is given by: - ON_SubD::SubDType::TriLoopWarren case: - S*Transpose(V, E[0], E[1], ..., E[N-1]). - ON_SubD::SubDType::QuadCatmullClark case: - S*Transpose(V, E[0], F[0], E[1], F[1], ..., F[N-2], E[N-1]). + S*Transpose(V, E[0], F[0], E[1], F[1], ..., F[N-2], E[N-1]). N = edge valence = number of edges in the sector. E[i] = end of i-th edge radiating from V. - In the ON_SubD::SubDType::QuadCatmullClark case, F[i] = point on the quad - that is opposite V. + F[i] = point on the quad that is opposite V. The edges and faces are ordered radially so that the face for F[i] lies between the edges for E[i] and E[(i+1)%N]. Parameters: - matrix_capacity - [in] - S[] can store any RxR matrix with R <= matrix_capacity. S - [out] subdivision matrix Matrix coefficent (i,j) = S[i][j] 0 <= i < R, 0 <= j < R, R = ON_SubDSectorType.PointRingCount() + matrix_capacity - [in] + S[] can store any RxR matrix with R <= matrix_capacity. Returns: R > 0: @@ -3533,26 +5114,26 @@ public: 0: Invalid input */ unsigned int GetSubdivisionMatrix( - size_t matrix_capacity, - double** S + double** S, + size_t matrix_capacity ) const; /* Parameters: - S_capacity - [in] - Number of elements in S[] array S - [out] subdivision matrix. Matrix coefficent (i,j) = S[i*R + j], 0 <= i < R, 0 <= j < R, R = ON_SubDSectorType.PointRingCount() + S_capacity - [in] + Number of elements in S[] array Returns: 0: Invalid input. >0: Number of rows and columns in S. This number is always ON_SubDSectorType.PointRingCount(). */ unsigned int GetSubdivisionMatrix( - size_t S_capacity, - double* S + double* S, + size_t S_capacity ) const; /* @@ -3647,10 +5228,10 @@ public: subdominant eigenvalue. */ double GetSubdominantEigenvectors( - size_t E1_capacity, double* E1, - size_t E2_capacity, - double* E2 + size_t E1_capacity, + double* E2, + size_t E2_capacity ) const; /* @@ -3675,19 +5256,19 @@ public: >0: Number of evaluation coefficients in the L*ev[] arrays. This number is always ON_SubDSectorType.PointRingCount(). */ - unsigned int GetLimitSurfaceEvaluationCoefficients( - size_t LPev_capacity, + unsigned int GetSurfaceEvaluationCoefficients( double* LPev, - size_t LT0ev_capacity, + size_t LPev_capacity, double* LT0ev, - size_t LT1ev_capacity, - double* LT1ev + size_t LT0ev_capacity, + double* LT1ev, + size_t LT1ev_capacity ) const; - // LimitSurfaceNormalSign() is a debugging tool - slow and not useful in general - double LimitSurfaceNormalSign() const; + // SurfaceNormalSign() is a debugging tool - slow and not useful in general + double SurfaceNormalSign() const; - bool LimitEvaluationCoefficientsAvailable() const; + bool SurfaceEvaluationCoefficientsAvailable() const; /* Parameters: @@ -3707,28 +5288,27 @@ public: Invalid input or the eigenvalues for this sector typoe are not available. */ unsigned int GetAllEigenvalues( - size_t eigenvalues_capacity, - double* eigenvalues + double* eigenvalues, + size_t eigenvalues_capacity ); - /* - Description: - The subdivision matrix for all cases is known. - A complete set of eigenvalues are available for some cases. - Parameters: - facet_type - [in] - vertex_tag - [in] - sector_edge_count - [in] - The input parameters identify the subdivision case. - Returns: - R > 0: Eigenvalues are known. There subdivison matrix is R x R. - 0: Eigenvalues for this case are not known. - */ - static unsigned int AllEigenvaluesAvailableKnown( - ON_SubD::SubDType subd_type, - ON_SubD::VertexTag vertex_tag, - unsigned int sector_edge_count - ); + /////* + ////Description: + //// The subdivision matrix for all cases is known. + //// A complete set of eigenvalues are available for some cases. + ////Parameters: + //// facet_type - [in] + //// vertex_tag - [in] + //// sector_edge_count - [in] + //// The input parameters identify the subdivision case. + ////Returns: + //// R > 0: Eigenvalues are known. There subdivison matrix is R x R. + //// 0: Eigenvalues for this case are not known. + ////*/ + ////static unsigned int AllEigenvaluesAvailableKnown( + //// ON_SubD::VertexTag vertex_tag, + //// unsigned int sector_edge_count + //// ); /* Description: @@ -3763,12 +5343,11 @@ public: ) const; private: - ON_SubD::SubDType m_subd_type = ON_SubD::SubDType::Unset; - ON_SubD::FacetType m_facet_type = ON_SubD::FacetType::Unset; ON_SubD::VertexTag m_vertex_tag = ON_SubD::VertexTag::Unset; unsigned char m_reserved1 = 0; + unsigned short m_reserved2 = 0; unsigned int m_hash = 0; // SetHash() sets this field, SectorTypeHash() returns its value. - unsigned int m_corner_sector_angle_index = 0; // >= 0 and <= ON_SubDSectorType::MaximumAngleIndex + unsigned int m_corner_sector_angle_index = 0; // >= 0 and <= ON_SubDSectorType::MaximumCornerAngleIndex unsigned int m_sector_face_count = 0; double m_sector_weight = 0.0; double m_sector_theta = 0.0; @@ -3837,8 +5416,7 @@ private: /* Parameters: - subd_type - [in] - sector_theta - [in] + sector_theta - [in] 0 < sector_theta <= 2*ON_PI value from one of the sector theta functions. ON_SubDEdge::SectorTheta() ON_SubDEdge::SmoothSectorTheta() @@ -3848,47 +5426,43 @@ private: Returns: 0: failed to caclulate weight - ON_UNSET_VALUE: - subd_type was set ON_SubD::SubDType::Unset - and the tagged end weight cannot be calculated until - the facet type is known. This typically happens - when a SubD control net is being created and - a facet type is not specified. The weights will - be calculated at the first subdivision. + ON_SubDSectorType::ErrorSectorWeight: + sector_theta is not valid. 0 < w < 1: - If ON_SubD::SubDType::QuadCatmullClark == subd_type, - then the returned value is + The returned value is 1/2 + 1/3*cos(sector_angle_radians). (1/6 <= w <= 5/6) - If ON_SubD::SubDType::TriLoopWarren == subd_type, - then the returned value is - 1/3 + 1/3*cos(sector_angle_radians). (0 < w <= 2/3) Remarks: This is a useful tool when calling AddEdge while a subdivision level is being constructed. */ static double SectorWeightFromTheta( - ON_SubD::SubDType subd_type, double sector_theta ); }; ////////////////////////////////////////////////////////////////////////// // -// ON_SubDLimitMeshFragment +// ON_SubDMeshFragment // // Meshes of ON_SubD limit surface are calculated in fragments. // -class ON_SUBD_CLASS ON_SubDLimitMeshFragmentGrid +class ON_CLASS ON_SubDMeshFragmentGrid { public: // No construction for performance reasons. - // If you require initialization, use = ON_SubDLimitMeshFragmentGrid::Empty + // use = ON_SubDMeshFragmentGrid::Empty + // QuadGridFromSideSegmentCount(...) + // or QuadGridFromDisplayDensity(...) to initialize. // - //ON_SubDLimitMeshFragmentGrid() = default; - //~ON_SubDLimitMeshFragmentGrid() = default; - //ON_SubDLimitMeshFragmentGrid(const ON_SubDLimitMeshFragmentGrid&) = default; - //ON_SubDLimitMeshFragmentGrid& operator=(const ON_SubDLimitMeshFragmentGrid&) = default; - static const ON_SubDLimitMeshFragmentGrid Empty; + //ON_SubDMeshFragmentGrid() = default; + //~ON_SubDMeshFragmentGrid() = default; + //ON_SubDMeshFragmentGrid(const ON_SubDMeshFragmentGrid&) = default; + //ON_SubDMeshFragmentGrid& operator=(const ON_SubDMeshFragmentGrid&) = default; + + + static const ON_SubDMeshFragmentGrid Empty; + + static const ON_SubDMeshFragmentGrid OneQuadGrid; /* Description: @@ -3897,7 +5471,7 @@ public: side_segment_count - [in] number quads in each row and column of the quad grid. side_segment_count >= 1 - side_segment_count <= ON_SubDLimitMesh::MaximumSideSegmentCount + side_segment_count <= ON_SubDMesh::MaximumSideSegmentCount side_segment_count must be a power of 2 level_of_detail - [in] @@ -3907,22 +5481,17 @@ public: ... If 4^level_of_detail > maximum quad count, then a single quad is returned. */ - static ON_SubDLimitMeshFragmentGrid Quads( + static ON_SubDMeshFragmentGrid QuadGridFromSideSegmentCount( unsigned int side_segment_count, unsigned int level_of_detail ); - static ON_SubDLimitMeshFragmentGrid Tris( - unsigned int side_segment_count, - unsigned int level_of_detail - ); - - static ON_SubDLimitMeshFragmentGrid Facets( - ON_SubD::FacetType facet_type, - unsigned int side_segment_count, + static ON_SubDMeshFragmentGrid QuadGridFromDisplayDensity( + unsigned int display_density, unsigned int level_of_detail ); +private: /* Description: Get mesh facet quads that index into a grid of points. @@ -3946,22 +5515,28 @@ public: static unsigned int SetQuads( unsigned int side_segment_count, unsigned int level_of_detail, + unsigned int* quads, size_t quad_capacity, size_t quad_stride, - unsigned int* quads, + unsigned int* sides, size_t side_capacity, - size_t side_stride, - unsigned int* sides + size_t side_stride ); - +public: unsigned int SideSegmentCount() const; + /* + Returns: + SideSegmentCount() + 1; + */ + unsigned int SidePointCount() const; + /* Description: The GridId() is persistent and unique based on the contents of the grid. It is intended to be used in render applications that store - copies of ON_SubDLimitMeshFragmentGrid settings in things like + copies of ON_SubDMeshFragmentGrid settings in things like vertex object buffers and want a reliable way to index into them. The Empty grid has id = 0; Returns: @@ -3976,49 +5551,127 @@ public: */ unsigned int GridId() const; + + unsigned int GridFaceCount() const; + /* Returns: - 3 for tris, 4 for quads, 0 for unset. + Total number of points in the grid = SidePointCount()*SidePointCount(). */ - unsigned int GridFacetSideCount() const; + unsigned int GridPointCount() const; + /* + Parameters: + grid_point_index - [in] + 0 <= grid_point_index < GridPointCount(). + grid_parameters = [out] + normalize parameters for that point. + These could be used in the role of surface evaluation parameters + for texture mapping applications. + Returns: + True if grid_point_index was valid and grid_parameters was returned. + */ bool GetGridParameters( unsigned int grid_point_index, double grid_parameters[2] ) const; - + /* + Parameters: + grid_point_index - [in] + 0 <= grid_point_index < GridPointCount(). + Returns: + Grid (i,j) for that grid_point_index. + 0 <= i < SidePointCount() + 0 <= j < SidePointCount() + */ + const ON_2udex Grid2dexFromPointIndex( + unsigned int grid_point_index + ) const; + + /* + Parameters: + i - [in] + 0 <= i < SidePointCount() + j - [in] + 0 <= j < SidePointCount() + Returns: + 0 <= grid_point_index < GridPointCount(). + */ + unsigned int PointIndexFromGrid2dex( + unsigned int i, + unsigned int j + ) const; + + /* + Returns: + A number between 0 and 8 or ON_UNSET_INT_INDEX. + SideSegmentCount() = 2^DisplayDensity(). + */ + unsigned int DisplayDensity() const; + + unsigned int LevelOfDetail() const; + private: unsigned char m_reserved; public: - - ON_SubD::FacetType m_F_type; + unsigned char m_reserved1 = 0; unsigned char m_side_segment_count; // = 2^n for non-empty grids (0 <= n <= 8) - unsigned short m_F_count; // = m_side_count*m_side_count + unsigned short m_F_count; // = m_side_segment_count*m_side_segment_count unsigned short m_F_level_of_detail; // 0 = highest, > 0 = reduced unsigned short m_F_stride; const unsigned int* m_F; const unsigned int* m_S; // [4*m_side_segment_count + 1] indices that form the polyline boundary. - const ON_SubDLimitMeshFragmentGrid* m_prev_level_of_detail; // nullptr or the previous level with more facets. - const ON_SubDLimitMeshFragmentGrid* m_next_level_of_detail; // nullptr or the next level with fewer facets. + const ON_SubDMeshFragmentGrid* m_prev_level_of_detail; // nullptr or the previous level with more facets. + const ON_SubDMeshFragmentGrid* m_next_level_of_detail; // nullptr or the next level with fewer facets. }; -class ON_SUBD_CLASS ON_SubDLimitMeshFragment +class ON_CLASS ON_SubDMeshFragment { public: // No construction for performance reasons. - // If you require initialization, use = ON_SubDLimitMeshFragment::Empty + // If you require initialization, use = ON_SubDMeshFragment::Empty // - //ON_SubDLimitMeshFragment() = default; - //~ON_SubDLimitMeshFragment() = default; - //ON_SubDLimitMeshFragment(const ON_SubDLimitMeshFragment&) = default; - //ON_SubDLimitMeshFragment& operator=(const ON_SubDLimitMeshFragment&) = default; - - // Every field of ON_SubDLimitMeshFragment::Empty is zero. - static const ON_SubDLimitMeshFragment Empty; + //ON_SubDMeshFragment() = default; + //~ON_SubDMeshFragment() = default; + //ON_SubDMeshFragment(const ON_SubDMeshFragment&) = default; + //ON_SubDMeshFragment& operator=(const ON_SubDMeshFragment&) = default; - static const unsigned int MaximumSideSegmentCount; + bool CopyFrom( const ON_SubDMeshFragment& src_fragment ); + + /* + Parameters: + src_fragment - [in] + fragment to copy + display_density - [in] + The desired display density of the copy. + + If display_density = ON_UNSET_UINT_INDEX, then this->m_P_capacity must + be at least srf_fragment.m_P_count, all points are copied, and + this->m_grid = srf_fragment.m_grid. + + Otherwise, src_fragment must have enough points to provide + the specified denisity and this must have enough + point capacity to store the specified density. + + this - [out] + This must have a point capacity large enough to accomodate the + requested display density. + + */ + bool CopyFrom( + const ON_SubDMeshFragment& src_fragment, + unsigned int display_density + ); + + // Every field of ON_SubDMeshFragment::Empty is zero. + static const ON_SubDMeshFragment Empty; + + enum : unsigned int + { + MaximumSideSegmentCount = (1U << ON_SubDDisplayParameters::MaximumDensity) + }; /* Returns: @@ -4040,9 +5693,6 @@ public: /* Parameters: - facet_type - [in] - ON_SubD::FacetType::Quad or ON_SubD::FacetType::Tri - display_density - [in] >= 0 Returns: @@ -4053,7 +5703,6 @@ public: quads and the other is a trianglular collection of triangles. */ static unsigned int PointCountFromDisplayDensity( - ON_SubD::FacetType facet_type, unsigned int display_density ); @@ -4141,10 +5790,10 @@ public: static bool SealAdjacentSides( bool bTestNearEqual, bool bCopyNormals, - const ON_SubDLimitMeshFragment& src_fragment, + const ON_SubDMeshFragment& src_fragment, unsigned int i0, unsigned int i1, - ON_SubDLimitMeshFragment& dst_fragment, + ON_SubDMeshFragment& dst_fragment, unsigned int j0, unsigned int j1 ); @@ -4185,6 +5834,43 @@ public: double* dst ); + /* + Returns: + Number of mesh quads in a full sized fragment with the specified mesh density. + */ + static unsigned int FullFragmentMeshQuadCountFromDensity( + unsigned int mesh_density + ); + + /* + Returns: + Number of mesh quads in a half sized fragment with the specified mesh density. + */ + static unsigned int HalfFragmentMeshQuadCountFromDensity( + unsigned int mesh_density + ); + + /* + Returns: + Number of mesh points in a full sized fragment with the specified mesh density. + */ + static unsigned int FullFragmentMeshPointCountFromDensity( + unsigned int mesh_density + ); + + /* + Returns: + Number of mesh points in a half sized fragment with the specified mesh density. + */ + static unsigned int HalfFragmentMeshPointCountFromDensity( + unsigned int mesh_density + ); + +private: + // This field overlaps with ON_FixedSizePoolElement.m_next when a fixed size pool is managing the fragments. + // When m_reserved != 0, the framgment is uninitialized. + ON__UINT64 m_reserved; + public: const class ON_SubDFace* m_face; @@ -4205,6 +5891,201 @@ public: // for that fragment. m_face_vertex_index[0,1,3] = a value > ON_SubDFace::MaximumEdgeCount unsigned short m_face_vertex_index[4]; + const class ON_SubDFace* SubDFace() const; + + const bool HasValidPointAndNormalGrid() const; + + /* + Returns: + True if the fragment covers the entire SubD face. + */ + bool IsFullFaceFragment() const; + + /* + Returns: + True if the fragment covers a corner of the SubD face. + */ + bool IsFaceCornerFragment() const; + + /* + Returns: + If IsFaceCornerFragment() is true, then the index of the face's vertex for the corner is returned. + Otherwise, ON_UNSET_UINT_INDEX is returned. + */ + unsigned int FaceCornerIndex() const; + + /* + Returns: + Number of fragments that cover this face. 1 for quads and N>= 3 for faces with N sides when N != 4. + */ + unsigned int FaceFragmentCount() const; + + /* + Returns: + First fragment for this->m_face. + */ + const ON_SubDMeshFragment* FirstFaceFragment() const; + + /* + Returns: + Last fragment for this->m_face. + */ + const ON_SubDMeshFragment* LastFaceFragment() const; + + /* + Parameters: + bReturnLastFromFirstFirst - in + If false and this is the first fragment, then nullptr is returned. + If true and this is the first fragment, then LastFaceFragment() is returned. + Returns: + Previous fragment for this->m_face. + */ + const ON_SubDMeshFragment* PreviousFaceFragment( + bool bReturnLastFromFirstFirst + ) const; + + /* + Parameters: + bReturnFirstFromLast - in + If false and this is the last fragment, then nullptr is returned. + If true and this is the last fragment, then FirstFaceFragment() is returned. + Returns: + Next fragment for this->m_face. + */ + const ON_SubDMeshFragment* NextFaceFragment( + bool bReturnFirstFromLast + ) const; + + /* + Parameters: + fragments_capacity - in + Capacity of the fragments[] array. + fragments[] - out + Fragments are returned here. + */ + unsigned int GetFaceFragments( + const ON_SubDMeshFragment** fragments, + size_t fragments_capacity + ) const; + + unsigned int GetFaceFragments( + ON_SimpleArray& fragments + ) const; + + /* + Parameters: + grid2dex_i - [in] + 0 <= grid2dex_i < m_grid.SidePointCount() + grid2dex_j - [in] + 0 <= grid2dex_j < m_grid.SidePointCount() + Returns: + */ + const ON_3dPoint VertexPoint( + unsigned grid2dex_i, + unsigned grid2dex_j + ) const; + const ON_3dPoint VertexPoint( + ON_2udex grid2dex + ) const; + const ON_3dPoint VertexPoint( + unsigned grid_point_index + ) const; + + /* + Parameters: + grid2dex_i - [in] + 0 <= grid2dex_i < m_grid.SidePointCount() + grid2dex_j - [in] + 0 <= grid2dex_j < m_grid.SidePointCount() + Returns: + */ + const ON_3dVector VertexNormal( + unsigned grid2dex_i, + unsigned grid2dex_j + ) const; + const ON_3dVector VertexNormal( + ON_2udex grid2dex + ) const; + const ON_3dVector VertexNormal( + unsigned grid_point_index + ) const; + + /* + Parameters: + grid2dex_i - [in] + 0 <= grid2dex_i < m_grid.SidePointCount() + grid2dex_j - [in] + 0 <= grid2dex_j < m_grid.SidePointCount() + Returns: + The texture coordinates calculated by iterpolating the m_ctrlnetT[] values. + */ + const ON_3dPoint VertexTextureCoordinateFromCorners( + unsigned grid2dex_i, + unsigned grid2dex_j + ) const; + const ON_3dPoint VertexTextureCoordinateFromCorners( + ON_2udex grid2dex + ) const; + const ON_3dPoint VertexTextureCoordinateFromCorners( + unsigned grid_point_index + ) const; + + /* + Description: + Get the texture coordinate for the specified fragment grid point. + Parameters: + grid2dex_i - [in] + 0 <= grid2dex_i < m_grid.SidePointCount() + grid2dex_j - [in] + 0 <= grid2dex_j < m_grid.SidePointCount() + Returns: + The texture coordinate for the specified fragment grid point. + TextureCoordinateDimension() reports the number of coordinates to set. + When it is 2, the z coordinate of the returned point is 0.0. + */ + const ON_3dPoint VertexTextureCoordinate( + unsigned grid2dex_i, + unsigned grid2dex_j + ) const; + const ON_3dPoint VertexTextureCoordinate( + ON_2udex grid2dex + ) const; + const ON_3dPoint VertexTextureCoordinate( + unsigned grid_point_index + ) const; + + /* + Description: + Set the texture coordinate for the specified fragment grid point. + Parameters: + grid2dex_i - [in] + 0 <= grid2dex_i < m_grid.SidePointCount() + grid2dex_j - [in] + 0 <= grid2dex_j < m_grid.SidePointCount() + unsigned texture_coordinate_dimension - [in] + 2 or 3 + texture_coordinate - [in] + Texture coordinates are mutable and are often modified on an otherwise const fragment. + Returns: + True if input was valid and the texture coordinate was set. + */ + bool SetVertexTextureCoordinate( + unsigned grid2dex_i, + unsigned grid2dex_j, + ON_3dPoint texture_coordinate + ) const; + bool SetVertexTextureCoordinate( + ON_2udex grid2dex, + ON_3dPoint texture_coordinate + ) const; + bool SetVertexTextureCoordinate( + unsigned grid_point_index, + ON_3dPoint texture_coordinate + ) const; + + bool TextureCoordinatesExist() const; + void SetTextureCoordinatesExist(bool TextureCoordinatesExist) const; + /* Parameters: grid_side_index - [in] @@ -4213,10 +6094,10 @@ public: Returns: The subd edge that is on the identified side of the grid. */ - const class ON_SubDEdgePtr EdgePtr( + const class ON_SubDEdgePtr SubDEdgePtr( unsigned int grid_side_index ) const; - const class ON_SubDEdge* Edge( + const class ON_SubDEdge* SubDEdge( unsigned int grid_side_index ) const; @@ -4225,12 +6106,12 @@ public: grid_corner_index - [in] grid side N is between corner index N and corner index (N+1)%4. Remarks: - For partial fragments (IsPartialFragment() = true), grid_corner_index = 2 is the only + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. - For partial fragments (IsPartialFragment() = true), grid_side_index = 1 and grid_side_index = 2 + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 correspond to half of original SuD edges. */ - const class ON_SubDVertexPtr VertexPtr( + const class ON_SubDVertexPtr SubDVertexPtr( unsigned int grid_corner_index ) const; @@ -4239,12 +6120,12 @@ public: grid_corner_index - [in] grid side N is between corner index N and corner index (N+1)%4. Remarks: - For partial fragments (IsPartialFragment() = true), grid_corner_index = 2 is the only + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. - For partial fragments (IsPartialFragment() = true), grid_side_index = 1 and grid_side_index = 2 + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 correspond to half of original SuD edges. */ - const class ON_SubDVertex* Vertex( + const class ON_SubDVertex* SubDVertex( unsigned int grid_corner_index ) const; @@ -4256,9 +6137,9 @@ public: Returns: Limit surface location at the grid corner or ON_3dPoint::NanPoint if the fragment is empty. Remarks: - For partial fragments (IsPartialFragment() = true), grid_corner_index = 2 is the only + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. - For partial fragments (IsPartialFragment() = true), grid_side_index = 1 and grid_side_index = 2 + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 correspond to half of original SuD edges. */ const ON_3dPoint CornerPoint( @@ -4272,9 +6153,9 @@ public: Returns: Limit surface normal at the grid corner or ON_3dPoint::NanPoint if the fragment is empty. Remarks: - For partial fragments (IsPartialFragment() = true), grid_corner_index = 2 is the only + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. - For partial fragments (IsPartialFragment() = true), grid_side_index = 1 and grid_side_index = 2 + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 correspond to half of original SuD edges. */ const ON_3dVector CornerNormal( @@ -4288,14 +6169,13 @@ public: Returns: Limit surface frame at the grid corner or ON_3dPoint::NanPoint if the fragment is empty. Remarks: - For partial fragments (IsPartialFragment() = true), grid_corner_index = 2 is the only + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. - For partial fragments (IsPartialFragment() = true), grid_side_index = 1 and grid_side_index = 2 + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 correspond to half of original SuD edges. */ - bool GetCornerFrame( - unsigned int grid_corner_index, - ON_Plane& frame + const ON_Plane CornerFrame( + unsigned int grid_corner_index ) const; /* @@ -4305,9 +6185,9 @@ public: Returns: Limit surface location at the midde of the grid side or ON_3dPoint::NanPoint if the fragment is empty. Remarks: - For partial fragments (IsPartialFragment() = true), grid_corner_index = 2 is the only + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. - For partial fragments (IsPartialFragment() = true), grid_side_index = 1 and grid_side_index = 2 + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 correspond to half of original SuD edges. */ const ON_3dPoint SidePoint( @@ -4321,9 +6201,9 @@ public: Returns: Limit surface normal at the grid corner or ON_3dPoint::NanPoint if the fragment is empty. Remarks: - For partial fragments (IsPartialFragment() = true), grid_corner_index = 2 is the only + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. - For partial fragments (IsPartialFragment() = true), grid_side_index = 1 and grid_side_index = 2 + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 correspond to half of original SuD edges. */ const ON_3dVector SideNormal( @@ -4337,14 +6217,13 @@ public: Returns: Limit surface frame at the grid corner or ON_3dPoint::NanPoint if the fragment is empty. Remarks: - For partial fragments (IsPartialFragment() = true), grid_corner_index = 2 is the only + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. - For partial fragments (IsPartialFragment() = true), grid_side_index = 1 and grid_side_index = 2 + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 correspond to half of original SuD edges. */ - bool GetSideFrame( - unsigned int grid_side_index, - ON_Plane& frame + const ON_Plane SideFrame( + unsigned int grid_side_index ) const; /* @@ -4354,20 +6233,17 @@ public: Returns: Limit surface location at the center of the grid side or ON_3dPoint::NanPoint if the fragment is empty. Remarks: - For partial fragments (IsPartialFragment() = true), grid_corner_index = 2 is the only + For partial fragments (IsFaceCornerFragment() = true), grid_corner_index = 2 is the only corner that corresponds to a SubD vertex. - For partial fragments (IsPartialFragment() = true), grid_side_index = 1 and grid_side_index = 2 + For partial fragments (IsFaceCornerFragment() = true), grid_side_index = 1 and grid_side_index = 2 correspond to half of original SuD edges. */ const ON_3dPoint CenterPoint( ) const; - const ON_3dVector CenterNormal( - ) const; + const ON_3dVector CenterNormal() const; - bool GetCenterFrame( - ON_Plane& frame - ) const; + const ON_Plane CenterFrame() const; private: bool Internal_GetFrameHelper( @@ -4384,85 +6260,346 @@ public: */ ON_ComponentStatus Status() const; - /* - Returns: - True if this fragment covers a portion of the original SubD face. - Remarks: - For partial fragments (IsPartialFragment() = true), grid_corner_index = 2 is the only - corner that corresponds to a SubD vertex. - For partial fragments (IsPartialFragment() = true), grid_side_index = 1 and grid_side_index = 2 - correspond to half of original SuD edges. - */ - bool IsPartialFragment() const; - - /* - Returns: - True if this fragment covers an entire subd face. - */ - bool IsCompleteFragment() const; - bool Transform( const ON_Xform& xform ); + ON_SubDMeshFragment* m_next_fragment; + ON_SubDMeshFragment* m_prev_fragment; + unsigned short m_face_fragment_count; // Number of fragments that will be delivered for this face. unsigned short m_face_fragment_index; // First fragment has index = 0. Last fragment has index = m_face_fragment_count-1. - // For quad based subdivision algorithms, the mesh fragment - // is a tesselation of a rectangular shaped surface, - // there are m_side_count quad edges along each side of the tesselation, - // there are a total of m_side_count X m_side_count quads, and + // The mesh fragment is a grid of quads. + // There are m_side_count quad edges along each side of the tesselation, + // There are a total of m_side_count X m_side_count quads, and // m_P_count = (m_side_count+1)*(m_side_count+1). - // - // For trangle based subdivision algorithms, the mesh fragment - // is a tesselation of a triangular shaped surface, - // there are m_side_count triangle edges along each side of the tesselation, - // there are a total of m_side_count X m_side_count triangles, and - // m_P_count = (m_side_count+1)*(m_side_count+2)/2. - // - // Number of points - unsigned short m_P_count; - unsigned short m_P_capacity; +public: + enum : unsigned + { + MaximumVertexCount = 0x3FFF + }; - // points + /* + Returns: + Number of vertices in the mesh fragment grid. + VertexCount() should be identical to m_grid.GridPointCount(). + VertexCapacity() should be >= VertexCount(). + */ + unsigned VertexCount() const; + + /* + Description: + Sets number of fragment vertices being used (number of elements being used in the m_P[], m_N[], and m_T[] arrays). + Parameters: + vertex_count - [in] + A value no larger than ON_SubDMeshFragment::MaximumVertexCount. + */ + bool SetVertexCount(size_t vertex_count); + + /* + Returns: + Capactity for vertices in the mesh fragment grid. + VertexCapacity() should be >= VertexCount(). + VertexCount() should be identical to m_grid.PointCount(). + */ + unsigned VertexCapacity() const; + + /* + Description: + Sets number of fragment vertices available (number of elements available in the m_P[], m_N[], and m_T[] arrays). + The memory for the arrays is managed by something besides this ON_SubDManagedMeshFragment instance. + Parameters: + vertex_capacity - [in] + A value no larger than ON_SubDMeshFragment::MaximumVertexCount. + Returns: + True if successful + */ + bool SetUnmanagedVertexCapacity(size_t vertex_capacity); + + + /* + Description: + Sets number of fragment vertices available (number of elements available in the m_P[], m_N[], and m_T[] arrays). + The memory for the arrays is managed by something besides this ON_SubDManagedMeshFragment instance. + Parameters: + vertex_capacity - [in] + A value no larger than ON_SubDMeshFragment::MaximumVertexCount. + Returns: + True if successful + */ + bool ReserveManagedVertexCapacity(size_t vertex_capacity); + + /* + Returns: + True if the memory in the m_P[], m_N[], and m_T[] is managed by this ON_SubDManagedMeshFragment instance. + */ + bool ManagedArrays() const; + + bool DeleteManagedArrays(); + + /* + Returns: + True if the memory in the m_P[], m_N[], and m_T[] is managed by something besides this ON_SubDManagedMeshFragment instance. + */ + bool UnmanagedArrays() const; + +private: + friend class ON_SubDManagedMeshFragment; + friend class ON_SubDMeshImpl; + // Number of grid vertices and capacity of arrays in certain conditions. + enum : unsigned short + { + ValueMask = 0x3FFF, // maximum vertex count and capacity + EtcMask = 0xC000, + EtcControlNetQuadBit = 0x8000, // bit is on m_vertex_count_etc. Set means 4 m_ctrlnetP[] points are set. + EtcTextureCoordinatesExistBit = 0x4000, // bit is on m_vertex_count_etc + EtcManagedArraysBit = 0x8000, // bit is on m_vertex_capacity_etc. Set means this class manages the m_P[], m_N[], and m_T[] arrays. + }; + mutable unsigned short m_vertex_count_etc; + mutable unsigned short m_vertex_capacity_etc; + static void Internal_Set3dPointArrayToNan(double* a, size_t a_count, size_t a_stride); + +private: + // corners for control net display in grid order (counter-clockwise quad order must swap [2] and[3]) + double m_ctrlnetP[4][3]; + + // Normal used for shading the control net display. + double m_ctrlnetN[3]; + + // The fragment's default corner texture coordinates in grid order. + // m_ctrlnetT[0] (bbox min = lower left) + // m_ctrlnetT[1] (lower right) + // m_ctrlnetT[2] (upper left) + // m_ctrlnetT[3] (bbox max = upper right) + mutable double m_ctrlnetT[4][3]; + +public: + /////////////////////////////////////////////////////////////////////////////////// + // + // 3-dimensional grid vertex points + // + // Depending on the strides, m_P[], m_N[], and m_T[] can be separate or interlaced. + // + // The stride m_P_stride and memory m_P references is managed by some other class or function. + // Never modify m_P_stride, m_P, or the values in m_P. + // Use m_grid functions to get point indices and quad face indices. + double* m_P; // surface points size_t m_P_stride; - // The memory m_P references is managed by some other class or function. - // Never modify the values in m_P. - double* m_P; + const double* PointArray(ON_SubDComponentLocation subd_appearance)const; + size_t PointArrayStride(ON_SubDComponentLocation subd_appearance)const; + unsigned PointArrayCount(ON_SubDComponentLocation subd_appearance) const; - // surface normals parallel to m_P[] array +public: + + /* + Description: + Create a one quad fragment with + m_P = this->m_ctrlnetP[], m_P_stride = 3 + m_N = this->m_ctrlnetN, m_N_stride = 0 + m_T = this->m_ctrlnetT, m_T_stride = 3 + NOTE WELL: + 1) "this" must remain in scope and unchanged while the returned value + is used because the returned value references memory in thism_ctrlnetX[] arrays. + 2) The next/prev pointers on the returned fragment are copied from this, + but the returned value is not reciprocally referenced by next/prev in the linked list. + Returns: + A control net quad fragment that can be used locally when the SubDAppearance + is ON_SubDComponentLocation::ControlNet. + The points, normals, and texture_coordinates of the returned fragment + are the control net quad points, normals, and texture coordinates + of this fragment. m_grid is a single quad grid. + */ + const ON_SubDMeshFragment ControlNetQuadFragmentForExperts() const; + + /* + Parameters: + bGridOrder - [in] + If true, then points are returned in fragment grid order. + Otherwise, points are returned in counter-clockwise quad order. + quad_points - [out] + quad_normal - [out] + */ + bool GetControlNetQuad( + bool bGridOrder, + ON_3dPoint quad_points[4], + ON_3dVector& quad_normal + ) const; + + const ON_3dPoint ControlNetQuadPoint( + bool bGridOrder, + unsigned point_index + ) const; + + /* + Parameters: + bGridOrder - [in] + If true, then points are returned in fragment grid order. + Otherwise, points are returned in counter-clockwise quad order. + quad_points - [in] + */ + void SetControlNetQuad( + bool bGridOrder, + const ON_3dPoint quad_points[4], + ON_3dVector quad_normal + ); + + void UnsetControlNetQuad(); + + const ON_BoundingBox SurfaceBoundingBox() const; + const ON_BoundingBox ControlNetQuadBoundingBox() const; + const ON_BoundingBox BoundingBox(ON_SubDComponentLocation subd_appearance) const; + +public: + /* + Returns: + If grid vertex points are available, then VertexCount() is returned. + Otherwise 0 is returned. + */ + unsigned int PointCount() const; + unsigned int PointCapacity() const; + + /////////////////////////////////////////////////////////////////////////////////// + // + // 3-dimensional grid vertex surface normals + // + // The m_N[] and m_P[] arrays are parallel. + // Depending on the strides, m_P[], m_N[], and m_T[] can be separate or interlaced. + // + // If m_N is not nullptr and m_N_stride>0, then m_N[] can accomodate up to m_P_capacity normals. + // If m_N is not nullptr and m_N_stride=0, then m_N[] can accomodate a single normal (flat shaded polygon face). + // The stride m_N_stride and memory m_N references is managed by some other class or function. + // Never modify m_N_stride, m_N, or the values in m_N. + // Use m_grid functions to get normal indices and quad face indices. + // Note well: m_N_stride can be 0 when the normal is constant (control net face normal for example). + double* m_N; // surface normals size_t m_N_stride; - // If m_N is not nullptr, then it can accomodate up to m_P_capacity normals. - // The memory m_N references is managed by some other class or function. - // Never modify the values in m_N. - double* m_N; + const double* NormalArray(ON_SubDComponentLocation subd_appearance)const; + size_t NormalArrayStride(ON_SubDComponentLocation subd_appearance)const; + unsigned NormalArrayCount(ON_SubDComponentLocation subd_appearance) const; + +public: + /* + Returns: + If grid vertex surface normals are available, then VertexCount() is returned. + Otherwise 0 is returned. + */ + unsigned int NormalCount() const; + unsigned int NormalCapacity() const; + + /////////////////////////////////////////////////////////////////////////////////// + // + // 3d vertex texture coordinates. + // + // Depending on the strides, m_P[], m_N[], and m_T[] can be separate or interlaced. + // + // If m_T is not nullptr and m_T_stride>0, then m_T[] can accomodate up to m_P_capacity textures coordinates. + // Otherwise there are no texture coordinates. + // Never modify m_T_stride, m_T. + // Use m_grid functions to get texture indices and quad face indices. + // Note well: m_T_stride can be 0 when the texture coordinate is constant (one color per face for example) + mutable double* m_T; + mutable size_t m_T_stride; + + const double* TextureCoordinateArray(ON_SubDComponentLocation subd_appearance)const; + size_t TextureCoordinateArrayStride(ON_SubDComponentLocation subd_appearance)const; + unsigned TextureCoordinateArrayCount(ON_SubDComponentLocation subd_appearance) const; +public: + /* + Returns: + If grid texture coordinates are available, then VertexCount() is returned. + Otherwise 0 is returned. + */ + unsigned int TextureCoordinateCount() const; + unsigned int TextureCoordinateCapacity() const; + + + /* + Description: + Sets the values of each fragment's m_ctrlnetT[] member. + Parameters: + bSetTextureCoordinates + If true, the texture coordinate corners are used ti set the fragment's m_T[] + values after m_ctrlnetT[] is set. + */ + void SetTextureCoordinateCorners( + bool bGridOrder, + const ON_2dPoint texture_coordinate_corners[4], + bool bSetTextureCoordinates + ) const; + + void SetTextureCoordinateCorners( + bool bGridOrder, + const ON_2dPoint texture_coordinate_corners[4], + double s0, + double s1, + double t0, + double t1, + bool bSetTextureCoordinates + ) const; + + void SetTextureCoordinateCorners( + bool bGridOrder, + const ON_3dPoint texture_coordinate_corners[4], + bool bSetTextureCoordinates + ) const; + + /* + Description: + Get the texture coordinate corners. + Parameters: + bGridOrder - [in] + If true, then points are returned in fragment grid order. + Otherwise, points are returned in counter-clockwise quad order. + texture_coordinate_corners - [out] + */ + + bool GetTextureCoordinteCorners( + bool bGridOrder, + ON_3dPoint texture_coordinate_corners[4] + ) const; + + /* + Description: + Set the texture coordinates in m_T[] from the values in m_ctrlnetT[]. + */ + void SetTextureCoordinatesFromCorners() const; + + // Normalized grid parameters useful for appling a texture to the grid are available + // from m_grid functions. - // quads or tris - ON_SubDLimitMeshFragmentGrid m_grid; // + // Information to resolve m_P[], m_N[], and m_T[] into a grid of NxN quads. + ON_SubDMeshFragmentGrid m_grid; - ON_BoundingBox m_bbox; + const ON_SubDMeshFragmentGrid& Grid(ON_SubDComponentLocation subd_appearance) const; - ON_SubDLimitMeshFragment* m_next_fragment; - ON_SubDLimitMeshFragment* m_prev_fragment; + // 3d bounding box of grid vertex points. + ON_BoundingBox m_surface_bbox; +public: + /* + Returns: + Amount of memory needed for the fragment, the m_P[], and the m_N[] arrays. + */ + static size_t SizeofFragment(unsigned int display_density); }; -class ON_SUBD_CLASS ON_SubDManagedLimitMeshFragment : public ON_SubDLimitMeshFragment +class ON_CLASS ON_SubDManagedMeshFragment : public ON_SubDMeshFragment { public: - ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT; - ~ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT; - ON_SubDManagedLimitMeshFragment(const ON_SubDManagedLimitMeshFragment&) ON_NOEXCEPT; - ON_SubDManagedLimitMeshFragment& operator=(const ON_SubDManagedLimitMeshFragment&) ON_NOEXCEPT; + ON_SubDManagedMeshFragment() ON_NOEXCEPT; + ~ON_SubDManagedMeshFragment() ON_NOEXCEPT; + ON_SubDManagedMeshFragment(const ON_SubDManagedMeshFragment&) ON_NOEXCEPT; + ON_SubDManagedMeshFragment& operator=(const ON_SubDManagedMeshFragment&) ON_NOEXCEPT; - static ON_SubDManagedLimitMeshFragment Create(const ON_SubDLimitMeshFragment& src) ON_NOEXCEPT; + static ON_SubDManagedMeshFragment Create(const ON_SubDMeshFragment& src) ON_NOEXCEPT; #if defined(ON_HAS_RVALUEREF) // rvalue copy constructor - ON_SubDManagedLimitMeshFragment( ON_SubDManagedLimitMeshFragment&& ) ON_NOEXCEPT; + ON_SubDManagedMeshFragment( ON_SubDManagedMeshFragment&& ) ON_NOEXCEPT; // rvalue assignment operator - ON_SubDManagedLimitMeshFragment& operator=( ON_SubDManagedLimitMeshFragment&& ) ON_NOEXCEPT; + ON_SubDManagedMeshFragment& operator=( ON_SubDManagedMeshFragment&& ) ON_NOEXCEPT; #endif void Clear() ON_NOEXCEPT; @@ -4470,74 +6607,35 @@ public: void Destroy() ON_NOEXCEPT; bool ReserveCapacity( - ON_SubD::FacetType facet_type, unsigned int mesh_density ) ON_NOEXCEPT; private: - void CopyHelper(const ON_SubDLimitMeshFragment& src); + void CopyHelper(const ON_SubDMeshFragment& src); size_t m_storage_capacity = 0; double* m_storage = nullptr; }; ////////////////////////////////////////////////////////////////////////// // -// ON_SubDDisplayParameters +// ON_SubDMesh // -// A collection of parameters that are passed to functions that -// calculate a various representations of the limit surface. -// -class ON_SUBD_CLASS ON_SubDDisplayParameters -{ -public: - static const ON_SubDDisplayParameters Empty; - // Parameters for the default limit surface display mesh. - static const ON_SubDDisplayParameters DefaultDisplayMeshParameters; - - /* - Description: - In most applications, the caller sets the mesh_density - and leaves the other parameters set to the default - values. - */ - static ON_SubDDisplayParameters CreateFromDisplayDensity( - unsigned int display_density - ); - - ON_SubDDisplayParameters() = default; - ~ON_SubDDisplayParameters() = default; - ON_SubDDisplayParameters(const ON_SubDDisplayParameters&) = default; - ON_SubDDisplayParameters& operator=(const ON_SubDDisplayParameters&) = default; - - unsigned int m_display_density = 0; - - bool m_bUseMultipleThreads = false; - - // Edge sealing takes extra time and can be skipped if - // adjacent faces can have mesh fragments whose shared vertex - // locations are nearly (but generally not exactly) equal. - bool m_bSkipEdgeSealing = false; - - ON_Terminator* m_terminator = nullptr; - // optional progress reporting - ON_ProgressReporter* m_progress_reporter = nullptr; - ON_Interval m_progress_reporter_interval = ON_Interval::ZeroToOne; -}; - -////////////////////////////////////////////////////////////////////////// -// -// ON_SubDLimitMesh -// -class ON_SUBD_CLASS ON_SubDLimitMesh +/// +/// ON_SubDMesh is used to store a traditional quad mesh of either +/// a SubgD surface or SubD control net. It is typically used for +/// rendering and user interface selection cacluations. +/// It is a list of ON_SubDMeshFragment classes. +/// +class ON_CLASS ON_SubDMesh { #if defined(ON_SUBD_CENSUS) private: - ON_SubDLimitMeshCensusCounter m_census_counter; + ON_SubDMeshCensusCounter m_census_counter; #endif public: - static const ON_SubDLimitMesh Empty; + static const ON_SubDMesh Empty; /* Returns: @@ -4547,15 +6645,7 @@ public: This is a runtime number. It is not saved in archives and begins at 1 with each new runtime instance of the opennurbs library. */ - unsigned int ContentSerialNumber() const; - - enum : unsigned int - { - DefaultDisplayDensity = 4, // default limit mesh density 16x16 quads per SubD quad 16 = 2^4 - MaximumDisplayDensity = 8 // 8 (256x256 quads per SubD quad 256 = 2^8) - //MaximumLevelOfDetail = 0, // 0 = most facets per fragment - //MinimumLevelOfDetail = 8 // 8 = fewest facets per fragment - }; + ON__UINT64 ContentSerialNumber() const; @@ -4574,28 +6664,69 @@ public: ON_Mesh* destination_mesh ) const; - ON_SubDLimitMesh() = default; - ~ON_SubDLimitMesh() = default; - ON_SubDLimitMesh(const ON_SubDLimitMesh&) = default; - ON_SubDLimitMesh& operator=(const ON_SubDLimitMesh&) = default; + + /* + Description: + Parameters: + mesh_density - [in] + Larger numbers return denser meshes. + destination_mesh - [in] + If destination_mesh is not nullptr, then the returned mesh + will be store here. Otherwise the returned mesh will be + allocated with a call to new ON_Mesh(). + Returns: + If this limit mesh is not empty, an ON_Mesh representation of this limit mesh. + Othewise, nullptr. + */ + ON_Mesh* ToMesh( + unsigned int mesh_density, + ON_Mesh* destination_mesh + ) const; + + /* + Description: + Parameters: + frit - [in] + A fragment iterator from an ON_SubDMesh or ON_SubD. + mesh_density - [in] + Larger numbers return denser meshes. + MinimumMeshDensity() <= mesh_density <= MaximumMeshDensity() + destination_mesh - [in] + If destination_mesh is not nullptr, then the returned mesh + will be store here. Otherwise the returned mesh will be + allocated with a call to new ON_Mesh(). + Returns: + If this limit mesh is not empty, an ON_Mesh representation of this limit mesh. + Othewise, nullptr. + */ + static ON_Mesh* ToMesh( + class ON_SubDMeshFragmentIterator& frit, + unsigned int mesh_density, + ON_Mesh* destination_mesh + ); + + ON_SubDMesh() = default; + ~ON_SubDMesh() = default; + ON_SubDMesh(const ON_SubDMesh&) = default; + ON_SubDMesh& operator=(const ON_SubDMesh&) = default; #if defined(ON_HAS_RVALUEREF) // rvalue copy constructor - ON_SubDLimitMesh( ON_SubDLimitMesh&& ) ON_NOEXCEPT; + ON_SubDMesh( ON_SubDMesh&& ) ON_NOEXCEPT; // rvalue assignment operator - ON_SubDLimitMesh& operator=( ON_SubDLimitMesh&& ); + ON_SubDMesh& operator=( ON_SubDMesh&& ); #endif - ON_SubDLimitMesh Copy() const; + ON_SubDMesh Copy() const; - ON_SubDLimitMesh& CopyFrom( - const ON_SubDLimitMesh& src + ON_SubDMesh& CopyFrom( + const ON_SubDMesh& src ); static void Swap( - ON_SubDLimitMesh& a, - ON_SubDLimitMesh& b + ON_SubDMesh& a, + ON_SubDMesh& b ); bool Transform( @@ -4605,7 +6736,7 @@ public: unsigned int DisplayDensity() const; ON_SubDDisplayParameters DisplayParameters() const; unsigned int FragmentCount() const; - const ON_SubDLimitMeshFragment* FirstFragment() const; // linked list of mesh fragments + const ON_SubDMeshFragment* FirstFragment() const; // linked list of mesh fragments /* Parameters: @@ -4616,7 +6747,7 @@ public: fragments for the face and this is the first on. The others follow using m_next_fragment. */ - const ON_SubDLimitMeshFragment* FaceFragment( + const ON_SubDMeshFragment* FaceFragment( const class ON_SubDFace* face ) const; @@ -4675,7 +6806,7 @@ public: /* Description: - The ON__INT_PTRs in the tree are const ON_SubDLimitMeshFragment* pointers. + The ON__INT_PTRs in the tree are const ON_SubDMeshFragment* pointers. */ const ON_RTree& FragmentTree() const; @@ -4694,8 +6825,6 @@ public: bool IsEmpty() const; - ON_SubD::FacetType GridType() const; - ON_BoundingBox BoundingBox() const; bool GetTightBoundingBox( @@ -4710,53 +6839,52 @@ public: public: /* Description: - Pretend this function and ON_SubDLimitMeshImpl do not exist. + Pretend this function and ON_SubDMeshImpl do not exist. Returns: Something that you are pretending does not exist. Remarks: - It is intentional that the definition of ON_SubDLimitMeshImpl class is not + It is intentional that the definition of ON_SubDMeshImpl class is not available in the opennurbs library interface (not in a header file). - The size and design of ON_SubDLimitMeshImpl will change constantly. + The size and design of ON_SubDMeshImpl will change constantly. If you choose to hack and whack so you can dereference an - ON_SubDLimitMeshImpl* pointer, then your code will crash unpredictably. + ON_SubDMeshImpl* pointer, then your code will crash unpredictably. */ - class ON_SubDLimitMeshImpl* SubLimple() const; + class ON_SubDMeshImpl* SubLimple() const; unsigned int SubLimpleUseCount() const; private: #pragma ON_PRAGMA_WARNING_PUSH #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 ) - friend class ON_SubDLimitMeshImpl; + friend class ON_SubDMeshImpl; // C4251: ... needs to have dll-interface to be used by clients of class ... // m_impl_sp is private and all code that manages m_impl_sp is explicitly implemented in the DLL. private: - std::shared_ptr< class ON_SubDLimitMeshImpl > m_impl_sp; + std::shared_ptr< class ON_SubDMeshImpl > m_impl_sp; #pragma ON_PRAGMA_WARNING_POP }; - -class ON_SUBD_CLASS ON_SubDSectorLimitPoint +class ON_CLASS ON_SubDSectorSurfacePoint { public: // For performance reasons, the default the data members are // not initialized by the default constructor - // Use = ON_SubDSectorLimitPoint::Unset when initialization is required - static const ON_SubDSectorLimitPoint Unset; // all doubles are ON_UNSET_VALUE, all pointer are nullptr - static const ON_SubDSectorLimitPoint Nan; // all doubles are ON_DBL_QNAN, all pointer are nullptr - static const ON_SubDSectorLimitPoint Zero; // all doubles are 0.0, all pointer are nullptr + // Use = ON_SubDSectorSurfacePoint::Unset when initialization is required + static const ON_SubDSectorSurfacePoint Unset; // all doubles are ON_UNSET_VALUE, all pointer are nullptr + static const ON_SubDSectorSurfacePoint Nan; // all doubles are ON_DBL_QNAN, all pointer are nullptr + static const ON_SubDSectorSurfacePoint Zero; // all doubles are 0.0, all pointer are nullptr /* Returns: - true if m_limitP[0] is a nan (like ON_DBL_QNAN). + true if m_limitP[0] is ON_UNSET_VALUE. false otherwise. */ bool IsUnset() const; /* Returns: - true if m_limitP[0] is ON_UNSET_VALUE. + true if m_limitP[0] is a nan (like ON_DBL_QNAN). false otherwise. */ bool IsNan() const; @@ -4774,7 +6902,9 @@ public: one nonzero coordinate. false otherwise. */ - bool IsSet() const; + bool IsSet( + bool bUndefinedNormalIsPossible + ) const; bool Transform( const ON_Xform& xform @@ -4786,40 +6916,48 @@ public: double m_limitT2[3]; // second unit tangent double m_limitN[3]; // unit normal (same direction as m_limitT1 x m_limitT2) - // When an ON_SubDVertex has a single sector, these pointers are both null. + // When an ON_SubDVertex has a single sector, (ON_SubDVertex.IsSingleSectorVertex() is true), + // these pointers are both null. // When an ON_SubDVertex has a multiple sectors, // m_sector_face is the "first" face in the sector and // m_next_sector_limit_point is used to create a linked list. // (The "first" face in a sector is the one ON_SubDSectorIterator.IncrementToCrease(-1) returns.) - const class ON_SubDSectorLimitPoint* m_next_sector_limit_point; // nullptr for vertices with one sector + const class ON_SubDSectorSurfacePoint* m_next_sector_limit_point; // nullptr for vertices with one sector const class ON_SubDFace* m_sector_face; // nullptr for vertices with one sector }; - - //////////////////////////////////////////////////////////////////////////// // // ON_SubDComponentBase // -class ON_SUBD_CLASS ON_SubDComponentBase +class ON_CLASS ON_SubDComponentBase { public: static const ON_SubDComponentBase Unset; - - ///* - //Returns: - // True if component is not nullptr, component->m_id > 0 and component->m_archive_id != ON_UNSET_UINT_INDEX. - //*/ - //static bool IsActive( - // const ON_SubDComponentBase* component - // ); - + + /* + Returns: + True if this component is active in its parent subd or other + relevent context. + Remarks: + When a component is in use, IsActive() = true. + If was used and then deleted, IsActive() is false. + */ + bool IsActive() const; + public: ON_SubDComponentBase() = default; ~ON_SubDComponentBase() = default; ON_SubDComponentBase(const ON_SubDComponentBase&) = default; ON_SubDComponentBase& operator=(const ON_SubDComponentBase&) = default; +public: + static int CompareId( + const ON_SubDComponentBase* lhs, + const ON_SubDComponentBase* rhs + ); + + public: // The audience for this comment is anybody who wants to change the data // fields in ON_SubDComponentBase. Everyone else should ignore this comment. @@ -4851,14 +6989,82 @@ private: mutable unsigned int m_archive_id = 0; public: - unsigned short m_level = 0; + + /* + Returns: + Subdivision level (0 to 255) + */ + unsigned const SubdivisionLevel() const; + + /* + Parameters: + level - [in] + Subdivision level (0 to 255) + */ + void SetSubdivisionLevel(unsigned level); + public: const ON_ComponentStatus Status() const; + public: mutable ON_ComponentStatus m_status = ON_ComponentStatus::NoneSet; + /* + Returns: + The current value of the component mark ( m_status->RuntimeMark() ). + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + */ + bool Mark() const; + + /* + Description: + Clears (sets to false) the value of the component mark. + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool ClearMark() const; + + /* + Description: + Sets (sets to true) the value of the component mark. + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool SetMark() const; + + /* + Description: + Sets the value of the component mark to bMark. + Parameter: + bMark - [in] + Remarks: + SubD components have a mutable runtime mark that can be used + in any context where a single thread cares about the marks. + It is widely used in many calculations to keep track of sets of + components that are in a certain context specfic state. + Returns: + Input value of Mark(). + */ + bool SetMark( + bool bMark + ) const; + public: ////////////////////////////////////////////////////////////// @@ -4869,81 +7075,139 @@ public: Description: Set the saved subdivision point. Parameters: - subdivision_point_type - [in] - Specifies subdivision algorithm. - Use ON_SubD::SubDType::Unset to clear the cache. subdivision_point - [in] includes displacement if it exists Returns: true if successful */ bool SetSavedSubdivisionPoint( - ON_SubD::SubDType subd_type, const double subdivision_point[3] ) const; bool GetSavedSubdivisionPoint( - ON_SubD::SubDType subd_type, double subdivision_point[3] ) const; - ON_SubD::SubDType SavedSubdivisionPointType() const; + void ClearSavedSubdivisionPoint() const; /* - Description: - Clears saved subdivision information for this component. + Returns: + Saved subdivision point. If a point has not been saved, + ON_3dPoint::NanPoint is returned. */ - void ClearSavedSubdivisionPoint() const; + const ON_3dPoint SavedSubdivisionPoint() const; + + /* + Returns: + True if the subdivision point is cached. + */ + bool SavedSubdivisionPointIsSet() const; ////////////////////////////////////////////////////////////// // // displacement applied to subdivision point // - bool SetDisplacement( - ON_SubD::SubDType subd_type, + bool SetSubdivisionDisplacement( const double displacement[3] ); - bool GetDisplacement( - ON_SubD::SubDType subd_type, + bool GetSubdivisionDisplacement( double displacement[3] ) const; - ON_SubD::SubDType DisplacementType() const; + /* + Returns: + subdivision displacement. If a displacement has not been set, + ON_3dPoint::ZeroVector is returned. + */ + const ON_3dVector SubdivisionDisplacement() const; - void ClearDisplacement() const; + /* + Returns: + True if the subdivision displacment vector is valid and not zero. + */ + bool SubdivisionDisplacementIsNonzero() const; + + + void ClearSubdivisionDisplacement() const; protected: + friend class ON_Internal_SubDFaceMeshFragmentAccumulator; + friend class ON_SubDHeap; enum SavedPointsFlags : unsigned char { - // (m_saved_points_flags & SubDTypeMask) is an ON_SubD::SubDType value for the cached points. - SubDTypeMask = 0x0F, + // if ( 0 != (m_saved_points_flags & ControlNetFragmentBit), then ON_subDFace.m_control_net_mesh_fragments are set. + // Otherwise means any information in ON_subDFace.m_control_net_mesh_fragments is invalid and must be recalculated. + ControlNetFragmentBit = 0x10, // if ( 0 != (m_saved_points_flags & SubDDisplacementVIsSet), then m_cache_subd_P is set. - SubdivisionPointIsSet = 0x20, + SubdivisionPointBit = 0x20, // if ( 0 != (m_saved_points_flags & SubDDisplacementVIsSet), then m_displacementV is set. - DisplacementVectorIsSet = 0x40, + SubdivisionDisplacementBit = 0x40, - // if ( 0 != (m_saved_points_flags & LimitPointIsSet), then ON_subDVertex.m_limit* values are set. - LimitPointIsSet = 0x80, + // if ( 0 != (m_saved_points_flags & SurfacePointBit), then ON_subDVertex.m_limit* values are set. + // ON_SubDVertex: Set means one or more sector limit surface points are saved in ON_SubDVertex.m_limit_point. + // ON_SubDEdge: Set means the limit surface NURBS curve control points are cached. + // ON_SubDFace: Set means limit surface mesh fragments are saved in ON_SubDFace.m_surface_mesh_fragments. + // Unset means any information in the fields identified above is invalid and must be recalculated. + SurfacePointBit = 0x80, - // SubdivisionPointIsSet | DisplacementVectorIsSet | LimitPointIsSet - CachedPointMask = 0xE0 + // ControlNetFragmentBit | SubdivisionPointBit | SubdivisionDisplacementBit | SurfacePointBit + CachedPointMask = 0xF0 }; // m_saved_points_flags is a bit field based on ON_SubDComponentBase::SavePointsFlags values. - // GetLimitPoint( bUseSavedLimitPoint=true ) can change the value of m_saved_points_flags + // GetSurfacePoint( bUseSavedSurfacePoint=true ) can change the value of m_saved_points_flags + void Internal_SetSavedSurfacePointFlag(bool bSavedSurfacePointFlag) const; + void Internal_SetSavedControlNetFragmentFlag(bool bSavedControlNetFragmentFlag) const; mutable unsigned char m_saved_points_flags = 0U; + unsigned char m_level = 0U; + +private: + unsigned char m_reserved_byte = 0U; + public: // All the faces with the same nonzero value of m_group_id are in the same "group". // SDK interface on ON_SubD will be added after we get a better idea of how this // feature will be used. - unsigned int m_group_id = 0U; + mutable unsigned int m_group_id = 0U; protected: + /* + Description: + Clears the flags indicating the saved subdivision point and surface point information + is current. + */ + void Internal_ClearSubdivisionPointAndSurfacePointFlags() const; + + /* + Description: + Clears the flag indicating the saved subdivision point is current. + */ + void Internal_ClearSubdivisionPointFlag() const; + + /* + Description: + Clears the flag indicating the saved surface point information is current. + */ + void Internal_ClearSurfacePointFlag() const; + + /* + Description: + Clears the flag indicating that ON_SubDFace.m_control_net_fragment is current. + */ + void Internal_ClearControlNetFragmentFlag() const; + + bool Internal_SubdivisionPointFlag() const; + + bool Internal_SurfacePointFlag() const; + + bool Internal_ControlNetFragmentFlag() const; + + void Internal_TransformComponentBase(bool bTransformationSavedSubdivisionPoint, const class ON_Xform& xform); // GetSubdivisionPoint( bUseSavedSubdivisionPoint=true ) can change the value of m_cache_subd_P mutable double m_saved_subd_point1[3]; // saved subdivision point @@ -4980,14 +7244,78 @@ protected: void CopyBaseFrom( const ON_SubDComponentBase* src ); + +private: + ON_SubDComponentPtr m_reserved; // reserved for the symmetric component when symmetry is implemnted. +}; + +//////////////////////////////////////////////////////////////////////////// +// +// ON_SubDVertexEdgeProperties +// +class ON_CLASS ON_SubDVertexEdgeProperties +{ +public: + ON_SubDVertexEdgeProperties() = default; + ~ON_SubDVertexEdgeProperties() = default; + ON_SubDVertexEdgeProperties(const ON_SubDVertexEdgeProperties&) = default; + ON_SubDVertexEdgeProperties& operator=(const ON_SubDVertexEdgeProperties&) = default; + +public: + static const ON_SubDVertexEdgeProperties Zero; // all member values are zero. + +public: + // Number of null edges + unsigned short m_null_edge_count = 0; + + // Number of edges tags ON_SubD::EdgeTag::Unset + unsigned short m_unset_edge_count = 0; + + // Number of edges tags ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX + unsigned short m_smooth_edge_count = 0; + + // Number of edges tags ON_SubD::EdgeTag::Crease + unsigned short m_crease_edge_count = 0; + + // Number of wire edges (0 attached faces) + unsigned short m_wire_edge_count = 0; + + // Number of boundary edges (1 attached face) + unsigned short m_boundary_edge_count = 0; + + // Number of interior edges (2 attached faces) + unsigned short m_interior_edge_count = 0; + + // Number of nonmanifold edges (3 or more attached faces) + unsigned short m_nonmanifold_edge_count = 0; + + // Minimum value of attached edges's m_face_count. + unsigned short m_min_edge_face_count = 0; + + // Maximum value of attached edges's m_face_count. + unsigned short m_max_edge_face_count = 0; }; //////////////////////////////////////////////////////////////////////////// // // ON_SubDVertex // -class ON_SUBD_CLASS ON_SubDVertex : public ON_SubDComponentBase +class ON_CLASS ON_SubDVertex : public ON_SubDComponentBase { +public: + ON_SubDVertex() = default; + ~ON_SubDVertex() = default; + ON_SubDVertex(const ON_SubDVertex&) = default; + ON_SubDVertex& operator=(const ON_SubDVertex&) = default; + +public: + /* + Description: + Clears saved subdivision and limit surface information + for this component. + */ + void ClearSavedSubdivisionPoints() const; + public: static const ON_SubDVertex Empty; @@ -5040,8 +7368,8 @@ public: const class ON_Xform& xform ); - bool SetLocation( - ON_3dPoint location, + bool SetControlNetPoint( + ON_3dPoint control_net_point, bool bClearNeighborhoodCache ); @@ -5050,10 +7378,11 @@ public: public: - ON_COMPONENT_INDEX ComponentIndex() const; - ON_SubDComponentPtr ComponentPtr() const; + const ON_COMPONENT_INDEX ComponentIndex() const; + const ON_SubDComponentPtr ComponentPtr() const; public: + // m_prev_vertex, m_next_vertex must be the first data members of ON_SubDVertex const class ON_SubDVertex* m_prev_vertex = nullptr; // linked list of vertices on this level const class ON_SubDVertex* m_next_vertex = nullptr; // linked list of vertices on this level @@ -5086,25 +7415,54 @@ public: // may be used in m_faces[]. const class ON_SubDFace** m_faces = nullptr; + /* + Description: + Expert user tool to remove an edge from the vertex's m_edges[] array. + Remarks: + Does not modify the edge. If the vertex is referenced by the edge, + then the vertex must be removed from edge's m_vertex[] array. + */ + bool RemoveEdgeFromArray( + const class ON_SubDEdge* f + ); + + /* + Description: + Expert user tool to remove a face from the vertex's m_faces[] array. + Remarks: + Does not modify the face or any edges. The faces edges must be updated accordingly. + */ + bool RemoveFaceFromArray( + const class ON_SubDFace* f + ); + public: - double m_P[3]; // vertex location + double m_P[3]; // vertex control net location + + /// + /// The SubD vertex control net point. + /// + const ON_3dPoint ControlNetPoint() const; private: // Cached limit point and limit normal - // GetLimitPoint( bUseSavedLimitPoint=true ) can change the value of m_limit_point. + // GetSurfacePoint( bUseSavedSurfacePoint=true ) can change the value of m_limit_point. // If the limit point is set and vertex has a single sector, then // m_limit_point.m_sector_face = nullptr and m_limit_point.m_next_sector_limit_point = nullptr. // If the limit point is set and vertex has a multiple sectors, then // m_limit_point.m_sector_face = first face in the sector. // If multiple limit points are set, then they are in a linked list - // traversed using the ON_SubDSectorLimitPoint.m_next_sector_limit_point. + // traversed using the ON_SubDSectorSurfacePoint.m_next_sector_limit_point. // The second and any additional limit points are managed by a fixed size pool. - // Calling ClearLimitPoint() will return these to the pool. - mutable ON_SubDSectorLimitPoint m_limit_point = ON_SubDSectorLimitPoint::Unset; + // Calling ClearSurfacePoint() will return these to the pool. + mutable ON_SubDSectorSurfacePoint m_limit_point = ON_SubDSectorSurfacePoint::Unset; public: - static const unsigned int MaximumEdgeCount; - static const unsigned int MaximumFaceCount; + enum : unsigned int + { + MaximumEdgeCount = 0xFFF0U, + MaximumFaceCount = 0xFFF0U + }; static int CompareUnorderedEdges( const ON_SubDVertex* a, @@ -5160,49 +7518,26 @@ public: unsigned int FaceArrayIndex( const ON_SubDFace* face - ) const; - - ON_SubD::FacetType FirstFaceFacetType() const; + ) const; /* - Returns - true if m_vertex_tag is ON_SubD::VertexTag::Crease, ON_SubD::VertexTag::Corner or ON_SubD::VertexTag::Dart. + Description: + Expert user tool to replace reference to old_face with a reference to new_face. + Parameters: + old_face = [in] + Cannot be nullptr. + new_face = [in] + If new_face is nullptr, old_face is simply removed. + Returns: + If the replacement was successful, then the m_faces[] array index where old_face/new_face replacement occured is returned. + Otherwise ON_UNSET_UINT_INDEX is returned. + Remarks: + No modifications are made to old_face or new_face. */ - bool IsTagged() const; - - ///* - //Parameters: - // subd_type - [in] - // Specifies subdivision algorithm - // vertex_tag_filter - [in] - // If vertex_tag is not ON_SubD::VertexTag::Unset and vertex_tag != m_vertex_tag, - // then false is returned. This parameter can be used when a smooth or crease - // vertex is explicity required. - // bTestFaces - [in] - // If true, and the edge and face count tests succeed, then the faces in the - // vertex m_faces[] array are tested to insure they are - // quads (ccquad subdivisiontype) or tris (lwtri subdivisiontype). - //Returns: - // If m_vertex_tag is ON_SubD::Vertex::Tag::smooth, - // and the number of edges = number of faces, - // and there are 4 (ccquad subdivisiontype) or 6 (lwtri subdivisiontype) edges, - // and bTestFaces is false or the faces pass the face test, - // then true is returned. - // - // If m_vertex_tag is ON_SubD::Vertex::Tag::crease, - // and the number of edges = 1 + number of faces, - // and there are 3 (ccquad subdivisiontype) or 4 (lwtri subdivisiontype) edges, - // and bTestFaces is false or the faces pass the face test, - // then true is returned. - - // In all other cases, false is returned. - //*/ - //bool IsOrdinary( - // ON_SubD::SubDType subd_type, - // ON_SubD::VertexTag vertex_tag_filter, - // bool bTestFaces - // ) const; - + unsigned int ReplaceFaceInArray( + const ON_SubDFace* old_face, + const ON_SubDFace* new_face + ); /* Returns: @@ -5244,7 +7579,7 @@ public: Returns: True if m_vertex_tag is ON_SubD::VertexTag::Crease or ON_SubD::VertexTag::Corner or ON_SubD::VertexTag::Dart. */ - bool IsCreaseOrCornerOrDart() const; + bool IsDartOrCreaseOrCorner() const; /* Returns: @@ -5252,6 +7587,8 @@ public: */ bool IsSmoothOrDart() const; + const ON_SubDVertexEdgeProperties EdgeProperties() const; + /* Description: A "standard" vertex is one where the standard subdivsion matrix for that vertex @@ -5270,93 +7607,169 @@ public: In this situation, it is possible that the vertex, as the center of a sector, is standard. */ - bool IsStandard( - ON_SubD::SubDType subd_type - ) const; + bool IsStandard() const; + + /* + Description: + A single sector vertex is a smooth vertex, a dart vertex, or a manifold boundary + vertex. + Returns: + True if the vertex has a single sector and, consequently, a single + limit surface normal. + + */ + bool IsSingleSectorVertex() const; + + /* + Description: + A manifold boundary vertex is a crease or corner vertex with 2 creased edges, + each attached to a single face, and all other edges are smooth edges attached + to two faces. There is a single sector at a manifold boundary vertex. + Returns: + True if the vertex has a single sector and, consequently, a single + limit surface normal. + + */ + bool IsManifoldBoundaryVertex() const; + + /* + Description: + A vertex has interior vertex topology if + EdgeCount() >= 2, + EdgeCount() == FaceCount(), + and every attached edge has two attached faces. + Returns: + True if the vertex has interior vertex toplology. + Remarks: + Tags are ignored. This property is often used during construction + and modification when tags are not set. + */ + bool HasInteriorVertexTopology() const; + + /* + Description: + A vertex has boundary vertex topology if + EdgeCount() >= 2, + EdgeCount() == 1+FaceCount(), + two attached edges are attached to one face, + the remaining edges are attached to two faces. + Returns: + True if the vertex has interior vertex toplology. + Remarks: + Tags are ignored. This property is often used during construction + and modification when tags are not set. + */ + bool HasBoundaryVertexTopology() const; /* Parameters: - subdivision_point_type - [in] - Selects subdivision algorithm. Must be either - ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark. bUseSavedSubdivisionPoint - [in] If there is a saved subdivision point and bUseSavedSubdivisionPoint is true, then the saved value is returned. subdivision_point - [out] + The SubD vertex Catmull-Clark subdivision point is returned here. Returns: true if successful */ bool GetSubdivisionPoint( - ON_SubD::SubDType subdivision_point_type, - bool bUseSavedSubdivisionPoint, double subdivision_point[3] ) const; - /* - Parameters: - facet_type - [in] - Selects subdivision algorithm. - bUseSavedLimitPoint - [in] - If there is a saved limit point and normal and bUseSavedLimitPoint - is true, then the saved value is used. - limit_point - [out] - Returns: - true if successful - */ - bool GetLimitPoint( - ON_SubD::SubDType subd_type, - const ON_SubDFace* sector_face, - bool bUseSavedLimitPoint, - class ON_SubDSectorLimitPoint& limit_point - ) const; + /// + /// The SubD vertex Catmull-Clark subdivision point. + /// + const ON_3dPoint SubdivisionPoint() const; /* Description: - If there is a saved limit point, then its location is returned in limit_point[]. + Evaluates the Catmull-Clark subdivision point ignoring all cached information. + This function is typically used in testing and debugging code and + in ordinary cases, it is faster and better to call SubdivisionPoint() + or GetSubdivisionPoint(). Parameters: - limit_point - [out] - Returns: - ON_SubD::SubDType::Unset: No saved limit point. The input value of limit_point[] is not changed. - Otherwise: The type of saved SubD limit point and its location is returned in limit_point[]. + subdivision_point - [out] + The vertex Catmull-Clark subdivision point is returned here. */ - ON_SubD::SubDType GetSavedLimitPointLocation( - double limit_point[3] + bool EvaluateCatmullClarkSubdivisionPoint( + double subdivision_point[3] ) const; /* - Description: - Save limit point and limit normal for future use. Parameters: - subd_type - [in] - limit_point - [in] - limit_normal - [in] + sector_face - [in] + A face in the sector of interest + limit_point - [out] Returns: true if successful */ - bool SetSavedLimitPoint( - ON_SubD::SubDType subd_type, - const ON_SubDSectorLimitPoint limit_point + bool GetSurfacePoint( + const ON_SubDFace* sector_face, + class ON_SubDSectorSurfacePoint& limit_point ) const; - void ClearSavedLimitPoints() const; - - /* - Returns: - ON_SubD::SubDType::TriLoopWarren - The vertex limit point and normal are saved for Loop trianglular subdivision. - ON_SubD::SubDType::QuadCatmullClark - The vertex limit point and normal are saved for Catmull-Clark quad subdivision. - ON_SubD::SubDType::Unset - The vertex limit point and normal are not saved. - */ - ON_SubD::SubDType SavedLimitPointType() const; + bool GetSurfacePoint( + const ON_SubDFace* sector_face, + bool bUndefinedNormalIsPossible, + class ON_SubDSectorSurfacePoint& limit_point + ) const; /* Description: - Report what type of facet or facets use this vertex. + If there is a saved limit surface point, then its location is returned in surface_point[]. + Parameters: + surface_point - [out] + Returns: + True if a saved limit surface point is returned. + False if there is no saved limit surface point. The input value of surface_point[] is not changed. */ - ON_SubD::VertexFacetType VertexFacetTypes() const; + bool GetSavedSurfacePoint( + double surface_point[3] + ) const; + bool GetSurfacePoint( + double surface_point[3] + ) const; + + /// + /// The SubD vertex Catmull-Clark limit surface point. + /// + const ON_3dPoint SurfacePoint() const; + + /* + Parameters: + subd_appearance - [in] + If ON_SubDComponentLocation::ControlNet, then this->ControlNetPoint() is returned. + If ON_SubDComponentLocation::Surface, then this->SurfacePoint() is returned. + Otherwise ON_3dPoint::NanPoint is returned. + */ + const ON_3dPoint Point(ON_SubDComponentLocation subd_appearance) const; + + + /* + Description: + Save limit surface point and limit normal for future use. + Parameters: + bUndefinedNormalIsPossible - [in] + true if an undefined normal might occur + surface_point - [in] + Returns: + true if successful + Remarks: + ClearSavedSubdivisionPoints() clears any saved limit points. + */ + bool SetSavedSurfacePoint( + bool bUndefinedNormalIsPossible, + const ON_SubDSectorSurfacePoint surface_point + ) const; + + void ClearSavedSurfacePoints() const; + + /* + Returns: + true + The vertex limit point and at least one sector normal are saved for Catmull-Clark quad subdivision. + */ + bool SurfacePointIsSet() const; /* Description: @@ -5364,21 +7777,42 @@ public: cached subdivision information that needs to be recalculated. */ void VertexModifiedNofification() const; + + /* + Returns: + Number of edges attached to this vertex with Edge().m_status.RuntimeMark() = true; + */ + unsigned int MarkedEdgeCount() const; + + /* + Returns: + Number of faces attached to this vertex with Face().m_status.RuntimeMark() = true; + */ + unsigned int MarkedFaceCount() const; + + /* + Description: + Expert user tool to unset ON_SubEdge.m_sector_coefficent[] values for + edges attached to this vertex. + Parameters: + relative_edge_end_dex - [in] + 0: unset vertex edge sector coefficient at the end where the edges + attaches to this vertex. + 1: unset vertex edge sector coefficient at the end where the edges + attaches to the other vertex. + 2: unset vertex edge sector coefficients at both ends of the edge. + */ + void UnsetSectorCoefficientsForExperts( + unsigned int relative_edge_end_dex + ) const; private: - static bool GetQuadPoint( + static bool Internal_GetCatmullClarkSubdivisionPoint( const class ON_SubDVertex* vertex, // smooth or dart - bool bUseSavedSubdivisionPoint, double vertex_point[3] ); - static bool GetTriPoint( - const class ON_SubDVertex* vertex, // smooth or dart - bool bUseSavedSubdivisionPoint, - double vertex_point[3] - ); - - static unsigned int GetFacePointSum( + static unsigned int Internal_GetFacePointSum( const ON_SubDFace* face, const ON_SubDVertex* vertex, double* facePsum // sum of points that are not immediately adjacent to vertex @@ -5397,9 +7831,8 @@ private: Returns: true if successful */ - static bool GetGeneralQuadSubdivisionPoint( + static bool Internal_GetGeneralQuadSubdivisionPoint( const class ON_SubDVertex* vertex, - bool bUseSavedSubdivisionPoint, double vertex_point[3] ); @@ -5409,7 +7842,7 @@ private: const ON_SubDVertex* src, bool bCopyEdgeArray, bool bCopyFaceArray, - bool bCopyLimitPointList + bool bCopySurfacePointList ); }; @@ -5417,8 +7850,22 @@ private: // // ON_SubDEdge // -class ON_SUBD_CLASS ON_SubDEdge : public ON_SubDComponentBase +class ON_CLASS ON_SubDEdge : public ON_SubDComponentBase { +public: + ON_SubDEdge() = default; + ~ON_SubDEdge() = default; + ON_SubDEdge(const ON_SubDEdge&) = default; + ON_SubDEdge& operator=(const ON_SubDEdge&) = default; + +public: + /* + Description: + Clears saved subdivision and limit surface information + for this component. + */ + void ClearSavedSubdivisionPoints() const; + public: static const ON_SubDEdge Empty; @@ -5472,7 +7919,7 @@ public: const class ON_Xform& xform ); - ON_BoundingBox ControlNetBoundingBox() const; + const ON_BoundingBox ControlNetBoundingBox() const; /* @@ -5486,19 +7933,30 @@ public: Description: Expert user tool to unset sector coefficients. */ - void UnsetSectorCoefficients() const; + void UnsetSectorCoefficientsForExperts() const; + + /* + Description: + Expert user tool to set sector coefficients. + Returns: + True if values were modified. + */ + bool UpdateEdgeSectorCoefficientsForExperts( + bool bUnsetEdgeSectorCoefficientsOnly + ); public: - ON_COMPONENT_INDEX ComponentIndex() const; - ON_SubDComponentPtr ComponentPtr() const; + const ON_COMPONENT_INDEX ComponentIndex() const; + const ON_SubDComponentPtr ComponentPtr() const; public: + // m_prev_edge, m_next_edge must be the first data members of ON_SubDEdge const class ON_SubDEdge* m_prev_edge = nullptr; // linked list of edges on this level const class ON_SubDEdge* m_next_edge = nullptr; // linked list of edges on this level public: // When checking the edge tag, it is important to consider what - // should happen in the ON_SubD::EdgeTag::X case. It is strongly + // should happen in the ON_SubD::EdgeTag::SmoothX case. It is strongly // suggested that you use the member functions ON_SubDEdge::IsSmooth() // and ON_SubDEdge::IsCrease() instead of comparing m_edge_tag to // ON_SubD::EdgeTag values. @@ -5522,7 +7980,10 @@ public: // The value of ON_SubDFacePtr.FaceDirection() is 1 if the // edge's natural orientation from m_vertex[0] to m_vertex[1] // is opposited the face's boundary orientation. - static const unsigned int MaximumFaceCount; + enum : unsigned int + { + MaximumFaceCount = 0xFFF0U + }; unsigned short m_face_count = 0; unsigned short m_facex_capacity = 0; ON_SubDFacePtr m_face2[2]; @@ -5557,7 +8018,7 @@ public: // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth // and both subdivision edges will be tagged as ON_SubD::EdgeTag::Smooth. // - // If the value of m_edge_tag is ON_SubD::EdgeTag::X, then the edge + // If the value of m_edge_tag is ON_SubD::EdgeTag::SmoothX, then the edge // must have exactly two neighboring faces, // both vertices must be tagged and the m_sector_coefficient[] // values are calculated by ON_SubDSectorType::SectorWeight(). @@ -5570,9 +8031,13 @@ public: // and both end vertices are tagged, that is a severe error // condition and the edge is subdivided at its midpoint. // - // If the value of m_edge_tag is ON_SubD::EdgeTag::X + // If the value of m_edge_tag is ON_SubD::EdgeTag::SmoothX // and both end vertices are not tagged, that is a severe error // condition and the edge is subdivided at its midpoint. + // + // m_sector_coefficient[tagged_end] = 1/2 + 1/3*cos(theta) + // where "theta" = tagged end "theta" (which depends on vertex tag (dart/crease/corner), + // the number of faces in the sector, and the crease angle when the tagged end is a corner). double m_sector_coefficient[2]; // If m_edge_tag is not ON_SubD::EdgeTag::Sharp, then m_sharpness is ignored. @@ -5583,14 +8048,36 @@ public: // For this reason, m_sharpness must be > 0 and < 1. double m_sharpness = 0.0; +private: + // Cached limit curve + // GetEdgeSurfaceCurveControlPoints( bUseSavedSurfacePoint=true ) can change the value of m_limit_curve. + // If 0 != ON_SUBD_CACHE_LIMIT_FLAG(m_saved_points_flags), then + // m_limit_curve is the edge's limit surface curve. + // The memory is managed by the parent ON_SubD. + // If 0 == ON_SUBD_CACHE_LIMIT_FLAG(m_saved_points_flags), + // then any information in m_limit_mesh_fragments is dirty + // and should not be used. + // ClearSavedSubdivisionPoints() zeros + // the appropriate bit of m_saved_points_flags. + + friend class ON_Internal_SubDFaceMeshFragmentAccumulator; + friend class ON_SubDHeap; + mutable class ON_SubDEdgeSurfaceCurve* m_limit_curve = nullptr; + public: unsigned int FaceCount() const; - ON_SubDFacePtr FacePtr( + /* + Returns: + True if the edge has two faces and with opposite face directions. + */ + bool IsInteriorEdge() const; + + const ON_SubDFacePtr FacePtr( unsigned int i ) const; - ON_SubDFacePtr FacePtr( + const ON_SubDFacePtr FacePtrFromFace( const class ON_SubDFace* f ) const; @@ -5642,10 +8129,36 @@ public: ON_SubDFacePtr face_ptr ); + /* + Description: + Expert user tool to replace reference to old_face with a reference to new_face. + Existing orientation is copied. No changes are made to old_face and new_face and + their edge references must be updated accordingly. + Parameters: + old_face = [in] + Cannot be nullptr. + new_face = [in] + If new_face is nullptr, old_face is simply removed. + Returns: + If the replacement was successful, then the m_faces[] array index where old_face/new_face replacement occured is returned. + Otherwise ON_UNSET_UINT_INDEX is returned. + Remarks: + No modifications are made to old_face or new_face. + */ + unsigned int ReplaceFaceInArray( + const ON_SubDFace* old_face, + const ON_SubDFace* new_face + ); + const class ON_SubDVertex* Vertex( unsigned int i ) const; + unsigned int VertexArrayIndex( + const class ON_SubDVertex* v + ) const; + + /* Description: Return the vertex at the other end of the edge. @@ -5656,24 +8169,61 @@ public: If vertex is not nullptr and exactly one of m_vertex[] is equal to vertex, then the other m_vertex[] pointer is returned. In any other case, nullptr is returned. + See Also: + ON_SubDEdge.NeighborFace() */ const ON_SubDVertex* OtherEndVertex( const ON_SubDVertex* vertex ) const; /* + Parameters: + vertex0 - [in] + vertex1 - [in] + bIgnoreOrientation - [in] + If false, then the returned edge must have vertices (vertex0, vertex1). + If true, then the returned edge may have vertices (vertex0, vertex1) or (vertex1, vertex0). Returns: - If vertex is set, then the location is returned. + If an edge connects the input vertices, it is returned. Otherwise nullptr is returned. + */ + static const ON_SubDEdge* FromVertices( + const ON_SubDVertex* vertex0, + const ON_SubDVertex* vertex1, + bool bIgnoreOrientation + ); + + static const ON_SubDEdgePtr FromVertices( + const ON_SubDVertex* vertex0, + const ON_SubDVertex* vertex1 + ); + + /* + Parameters: + i - [in] + 0 or 1. + Returns: + If i is 0 or 1 and vertex[i] is set, then vertex[i]->ControlNetPoint() is returned. Otherwise ON_3dPoint::NanPoint is returned. */ - const ON_3dPoint EndPoint( unsigned int i ) const; + const ON_3dPoint ControlNetPoint( unsigned int i ) const; /* Returns: - If vertices are set, then the vector from m_vertex[0] to m_vertex[1] is returned. + If vertices are set, then the vector from m_vertex[0]->ControlNetPoint() + to m_vertex[1]->ControlNetPoint() is returned. Otherwise ON_3dVector::NanVector is returned. */ - const ON_3dVector Direction() const; + const ON_3dVector ControlNetDirection() const; + + /* + Returns: + If vertices are set and v is an end of the edge, + then the vector from v to OtherEndVertex(v) is returned. + Otherwise ON_3dVector::NanVector is returned. + */ + const ON_3dVector ControlNetDirectionFrom( + const ON_SubDVertex* v + ) const; /* Description: @@ -5718,8 +8268,27 @@ public: ) const; /* + Parameters: + edge_vertex_index - [in] + 0 or 1 identifying which end of this edge to check. + i - [in] + Index of the face in this edge's face array. Returns: - True if m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::X. + The edge adjacent to this edge in this->Face(i). + The orientation is with respect to this->Face(i). + */ + const ON_SubDEdgePtr AdjacentEdgePtr( + unsigned int edge_vertex_index, + unsigned int i + ) const; + + const ON_SubDEdge* AdjacentEdge( + unsigned int edge_vertex_index, + unsigned int i + ) const; + /* + Returns: + True if m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX. False in all other cases. */ bool IsSmooth() const; @@ -5729,7 +8298,7 @@ public: True if m_edge_tag is ON_SubD::EdgeTag::Smooth. Remarks: Expert user function. - This is used in rare cases when level 0 edges tagged as ON_SubD::EdgeTag::X + This is used in rare cases when level 0 edges tagged as ON_SubD::EdgeTag::SmoothX need special handling in low level evaluation code. Typical SDK level functions and anything related to runtime user interface should call IsSmooth(). */ @@ -5737,10 +8306,10 @@ public: /* Returns: - True if m_edge_tag is ON_SubD::EdgeTag::X. + True if m_edge_tag is ON_SubD::EdgeTag::SmoothX. Remarks: Expert user function. - This is used in rare cases when level 0 edges tagged as ON_SubD::EdgeTag::X + This is used in rare cases when level 0 edges tagged as ON_SubD::EdgeTag::SmoothX need special handling in low level evaluation code. Typical SDK level functions and anything related to runtime user interface should call IsSmooth(). An edge tagged as "X" can occur at level 0. It is subdivided as a smooth @@ -5780,15 +8349,21 @@ public: /* Returns: - bitwise or of applicable ON_ComponentAttributes::EdgeFlags values. + bitwise or of applicable ON_ComponentAttributes::EdgeAttributes values. + Remarks: + ON_ComponentAttributes::EdgeAttributes has subsets of mutually exclusive + edge attributes. If the edge is valid, then exactly one bit from each + mutually exclusive set of properties will be set. + If an edge is not valie, then all bits for a set may be clear. + For example, if the edge has nullptr values in m_vertex[] + or the vertex control point locations are unset or nan, + then neither the ON_ComponentAttributes::EdgeAttributes::Open bit + nor ON_ComponentAttributes::EdgeAttributes::Closed bit will be set. */ - unsigned int EdgeFlags() const; + unsigned int EdgeAttributes() const; /* Parameters: - subdivision_point_type - [in] - Selects subdivision algorithm. Must be either - ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark. bUseSavedSubdivisionPoint - [in] If there is a saved subdivision point and bUseSavedSubdivisionPoint is true, then the saved value is returned. @@ -5797,11 +8372,36 @@ public: true if successful */ bool GetSubdivisionPoint( - ON_SubD::SubDType subdivision_point_type, - bool bUseSavedSubdivisionPoint, double subdivision_point[3] ) const; + /// + /// The SubD edge Catmull-Clark subdivision point. + /// + const ON_3dPoint SubdivisionPoint() const; + + /* + Description: + Evaluates the Catmull-Clark subdivision point ignoring all cached information. + This function is typically used in testing and debugging code and + in ordinary cases, it is faster and better to call SubdivisionPoint() + or GetSubdivisionPoint(). + Parameters: + subdivision_point - [out] + The edge Catmull-Clark subdivision point is returned here. + */ + bool EvaluateCatmullClarkSubdivisionPoint( + double subdivision_point[3] + ) const; + + const ON_3dPoint ControlNetCenterPoint() const; + const ON_3dVector ControlNetCenterNormal( + unsigned int edge_face_index + ) const; + + + + /* Parameters: edge_vertex_index - [in] @@ -5822,13 +8422,26 @@ public: /* Returns: - 0: m_vertex[0] is tagged and m_vertex[1] is not tagged. - 1: m_vertex[0] is tagged and m_vertex[1] is not tagged. - 2: m_vertex[0] and m_vertex[1] are both tagged. - 3: neither m_vertex[0] nor m_vertex[1] is tagged. + Set bTagged[i] = m_vertex[0]->IsDartOrCreaseOrCorner(). + 0: bTagged[0] is true and bTagged[1] is false. + 1: bTagged[0] is false and bTagged[1] is true. + 2: bTagged[0] and Tagged[1] are both true. + 3: bTagged[0] and Tagged[1] are both false. */ unsigned int TaggedEndIndex() const; + /* + Returns: + Number of end vertices with Vertex().m_status.RuntimeMark() = true; + */ + unsigned int MarkedVertexCount() const; + + /* + Returns: + Number of faces attached to this edge with Face().m_status.RuntimeMark() = true; + */ + unsigned int MarkedFaceCount() const; + private: static unsigned int GetFacePointSum( const ON_SubDFace* face, @@ -5850,22 +8463,37 @@ private: // // ON_SubDFace // -class ON_SUBD_CLASS ON_SubDFace : public ON_SubDComponentBase +class ON_CLASS ON_SubDFace : public ON_SubDComponentBase { +public: + ON_SubDFace() = default; + ~ON_SubDFace() = default; + ON_SubDFace(const ON_SubDFace&) = default; + ON_SubDFace& operator=(const ON_SubDFace&) = default; + +public: + /* + Description: + Clears saved subdivision and limit surface information + for this component. + */ + void ClearSavedSubdivisionPoints() const; + + public: static const ON_SubDFace Empty; - bool Write ( + bool Write( class ON_BinaryArchive& archive - ) const; + ) const; - static bool Read ( + static bool Read( class ON_BinaryArchive& archive, class ON_SubD& subd, class ON_SubDFace*& face - ); + ); + - /* Parameters: bIncludeVertices - [in] @@ -5873,7 +8501,7 @@ public: bIncludeEdges - [in] If true, then attached edges are included. Returns: - A ON_ComponentStatusLogicalOr() of this vertex's status and the + A ON_ComponentStatusLogicalOr() of this vertex's status and the specified attached components. See Also: ON_SubDComponentBase::Status() @@ -5889,10 +8517,10 @@ public: Parameters: bTransformationSavedSubdivisionPoint - [in] - If the transformation is being applied to every vertex, edge and + If the transformation is being applied to every vertex, edge and face in every level of a subdivision object, and the transformation - is an isometry (rotation, translation, ...), a uniform scale, or a - composition of these types, then set + is an isometry (rotation, translation, ...), a uniform scale, or a + composition of these types, then set bTransformationSavedSubdivisionPoint = true to apply the transformation to saved subdivision and saved limit point information. In all other cases, set bTransformationSavedSubdivisionPoint = false @@ -5904,13 +8532,13 @@ public: bool Transform( bool bTransformationSavedSubdivisionPoint, const class ON_Xform& xform - ); + ); - ON_BoundingBox ControlNetBoundingBox() const; + const ON_BoundingBox ControlNetBoundingBox() const; - ON_COMPONENT_INDEX ComponentIndex() const; - ON_SubDComponentPtr ComponentPtr() const; + const ON_COMPONENT_INDEX ComponentIndex() const; + const ON_SubDComponentPtr ComponentPtr() const; /* Description: @@ -5920,16 +8548,102 @@ public: void FaceModifiedNofification() const; public: + // m_prev_face, m_next_face must be the first data members of ON_SubDFace const class ON_SubDFace* m_prev_face = nullptr; // linked list of faces on this level const class ON_SubDFace* m_next_face = nullptr; // linked list of faces on this level +private: + // Location of this face's default 2d texture coordinates ("surface parameter") in normaized 2d texture coordinates + mutable double m_texture_coordinate_origin[2] = {}; + mutable double m_texture_coordinate_delta[2] = {}; + + enum TextureCoordinateBits : unsigned char + { + None = 0, + + // 4 ways the texture domain can be rotated for a face when packing is applied. + // These enum values must be 0,1,2,3 + PackingRotate0 = 0, + PackingRotate90 = 1, + PackingRotate180 = 2, + PackingRotate270 = 3, + + // bits mask for packing rotation setting + PackingRotateMask = 3, + + UnusedBit1 = 0x04U, + UnusedBit2 = 0x08U, + UnusedBitsMask = 0x0CU, + + // bits for TextureCoordinateDomainType() + DomainTypeMask = 0xF0U, + }; + mutable unsigned char m_texture_coordinate_bits = 0; + +private: + unsigned char m_reserved1 = 0; + unsigned short m_reserved2 = 0; + +public: + const bool TextureDomainIsSet() const; + + /* + Description: + Set the rectangle in 2d texture space that will be used by this face + for generating default "surface mapping" texture coordinates. + The lower left corner will be origin. + The upper right corner will be delta. + Parameters: + origin - [in] + delta - [in] + >= 0.0; + packing_rotation_degrees - [in] + must be a mulitple of 90. + */ + void SetTextureDomain( + ON_SubDTextureDomainType texture_domain_type, + ON_2dPoint origin, + ON_2dVector delta, + int packing_rotation_degrees + ) const; + + const ON_2dPoint TextureDomainOrigin() const; + + const ON_2dVector TextureDomainDelta() const; + + ON_SubDTextureDomainType TextureDomainType() const; + + /* + Returns: + 0, 90, 180, or 270 + */ + unsigned TextureDomainRotationDegrees() const; + + double TextureDomainRotationRadians() const; + /* + Parameters: + bGridOrder - [in] + false: counter clockwise quad order. + true: fragment grid order + bNormalized - [in] + false: corners of the (x0,x0+dx) x (y0,y0+dy) square are returned where + (x0,y0) = TextureCoordinateDomainOrigin() and + (dx,dy) = TextureCoordinateDomainDelta(). + true: corners of the (0,1) x (0,1) square are returned. + corner_index - [in] + Returns: + Specified texture coordinate corner value. + */ + const ON_2dPoint TextureDomainCorner( + bool bGridOrder, + bool bNormalized, + int corner_index + ) const; + public: unsigned int m_zero_face_id = 0; // id of level zero face unsigned int m_parent_face_id = 0; // id of previous level face -private: - unsigned int m_reserved = 0; // id of previous level face - public: // Array of m_edge_count edges that form the boundary of the face. // The edges are in ordered to form a continuous loop. @@ -5945,21 +8659,80 @@ public: // The value of ON_SubDEdgePtr.EdgeDirection() is 1 if the // edge's natural orientation from m_vertex[0] to m_vertex[1] // is opposited the face's boundary orientation. - static const unsigned int MaximumEdgeCount; + enum : unsigned int + { + MaximumEdgeCount = 0xFFF0U + }; unsigned short m_edge_count = 0; unsigned short m_edgex_capacity = 0; ON_SubDEdgePtr m_edge4[4]; ON_SubDEdgePtr* m_edgex = nullptr; + /* + Returns: + A linked list of limit mesh fragments that cover this face. + Nullptr if none are currently available. + + If the face is a quad, there is a single fragment. If the + face is an n-gon with 4 != n, then the list has n fragments. + The i-th fragment covers the subdivision quad with its + corner at Vertex(i). + + Remarks: + Use the returned fragments immediately or make a + copies you manage for later use. + + Use ON_SubD.UpdateSurfaceMeshCache(false) or ON_SubD.UpdateSurfaceMeshCache(true) + to create the face fragments. + + ON_SubDFace.ClearSavedSubdivisionPoints() removes any saved + fragments and is called when the face or nearby components + are modified. + */ + const class ON_SubDMeshFragment* MeshFragments() const; + + + const ON_3dPoint ControlNetCenterPoint() const; + + const ON_3dVector ControlNetCenterNormal() const; + + const ON_Plane ControlNetCenterFrame() const; + + bool IsConvex() const; + + bool IsNotConvex() const; + + bool IsPlanar(double planar_tolerance = ON_ZERO_TOLERANCE) const; + + bool IsNotPlanar(double planar_tolerance = ON_ZERO_TOLERANCE) const; + +private: + // If 0 != ON_SUBD_CACHE_LIMIT_FLAG(m_saved_points_flags), then + // m_limit_mesh_fragments is a linked list of m_edge_count + // fragments available from MeshFragments() and managed + // by the parent ON_SubD. + // If 0 == ON_SUBD_CACHE_LIMIT_FLAG(m_saved_points_flags), + // then any information in m_limit_mesh_fragments is dirty + // and should not be used. + // ClearSavedSubdivisionPoints() zeros + // the appropriate bit of m_saved_points_flags. + friend class ON_Internal_SubDFaceMeshFragmentAccumulator; + friend class ON_SubDHeap; + + // Mesh fragment(s) for this face + mutable class ON_SubDMeshFragment* m_mesh_fragments = nullptr; + +private: + public: unsigned int EdgeCount() const; - ON_SubDEdgePtr EdgePtr( + const ON_SubDEdgePtr EdgePtr( unsigned int i ) const; - ON_SubDEdgePtr EdgePtr( + const ON_SubDEdgePtr EdgePtrFromEdge( const class ON_SubDEdge* e ) const; @@ -5967,6 +8740,10 @@ public: unsigned int i ) const; + const ON_3dPoint ControlNetPoint( + unsigned int i + ) const; + unsigned int VertexIndex( const ON_SubDVertex* vertex ) const; @@ -6024,22 +8801,30 @@ public: ON_SubDEdge* edge_to_insert ); + /////* + ////Description: + //// Expert user tool to replace one edge with another in the face's edge array. + ////Parameters: + //// edge_to_remove - [in] + //// edge_to_insert - [in] + //// The inserted edge is assigned the same boundary orientation specified + //// in edgeptr_to_insert. + ////Remarks: + //// Does not modify the edge. The corresponding reference to this face must + //// be removed from the first edge and added to the second edge. + ////*/ + ////bool ReplaceEdgeInArray( + //// unsigned int fei0, + //// ON_SubDEdge* edge_to_remove, + //// ON_SubDEdgePtr edgeptr_to_insert + ////); + /* Description: - Expert user tool to replace one edge with another in the face's edge array. - Parameters: - edge_to_remove - [in] - edge_to_insert - [in] - The inserted edge is assigned the same boundary orientation specified - in edgeptr_to_insert. - Remarks: - Does not modify the edge. The corresponding reference to this face must - be removed from the first edge and added to the second edge. + Rotates the edge array so that Edge(fei0) becomes the first edge in the edge array. */ - bool ReplaceEdgeInArray( - unsigned int fei0, - ON_SubDEdge* edge_to_remove, - ON_SubDEdgePtr edgeptr_to_insert + bool RotateEdgeArray( + unsigned int fei0 ); const ON_SubDEdge* PrevEdge( @@ -6088,37 +8873,37 @@ public: const ON_SubDEdge* edge ) const; - ///* - //Parameters: - // subd_type - [in] - // bTestFaces - [in] - // If true, then false is returned if any neighboring face is not - // a quad (ccquad subdivision type) or tri (lwtri subdivsion type). - //*/ - //bool IsOrdinary( - // ON_SubD::SubDType subd_type, - // bool bTestFaces - // ) const; - /* Parameters: - subdivision_point_type - [in] - Selects subdivision algorithm. Must be either - ON_SubD::SubDType::TriLoopWarren or ON_SubD::SubDType::QuadCatmullClark. - bUseSavedSubdivisionPoint - [in] - If there is a saved subdivision point and bUseSavedSubdivisionPoint - is true, then the saved value is returned. subdivision_point - [out] The average of the face vertex locations. Returns: true if successful */ bool GetSubdivisionPoint( - ON_SubD::SubDType subdivision_point_type, - bool bUseSavedSubdivisionPoint, double subdivision_point[3] ) const; + /// + /// The SubD face Catmull-Clark subdivision point. + /// + const ON_3dPoint SubdivisionPoint() const; + + /* + Description: + Evaluates the Catmull-Clark subdivision point ignoring all cached information. + This function is typically used in testing and debugging code and + in ordinary cases, it is faster and better to call SubdivisionPoint() + or GetSubdivisionPoint(). + Parameters: + subdivision_point - [out] + The Catmull-Clark face subdivision point is returned here. + */ + bool EvaluateCatmullClarkSubdivisionPoint( + double subdivision_point[3] + ) const; + + /* Description: Reverse the order and orientation of the edges that form @@ -6144,20 +8929,56 @@ public: Remarks: The knots for the bicubic b-spline surface are uniform. */ - bool GetQuadLimitSurface( + bool GetQuadSurface( + double* limit_surface_cv, size_t limit_surface_cv_stride0, - size_t limit_surface_cv_stride1, - double* limit_surface_cv + size_t limit_surface_cv_stride1 ) const; - bool GetQuadLimitSurface( + bool GetQuadSurface( class ON_NurbsSurface& limit_surface ) const; - bool GetQuadLimitSurface( + bool GetQuadSurface( class ON_BezierSurface& limit_surface ) const; + /* + Returns: + Number of edges in the face's boundary with Edge().m_status.RuntimeMark() = true; + */ + unsigned int MarkedEdgeCount() const; + + /* + Returns: + Number of vertices in the face's boundary with Vertex().m_status.RuntimeMark() = true; + */ + unsigned int MarkedVertexCount() const; + + /* + Description: + Set the mark of every vertex attached to this face. + Returns: + Number of marks changed. + */ + unsigned int SetVertexMarks( + bool bMark + ) const; + + /* + Description: + Set the mark of every boundary edge attached to this face. + Returns: + Number of marks changed. + */ + unsigned int SetEdgeMarks( + bool bMark + ) const; + + unsigned int GetEdgeArray( + ON_SimpleArray< ON_SubDEdgePtr >& face_edge_array + ) const; + private: friend class ON_SubDArchiveIdMap; void CopyFrom( @@ -6170,7 +8991,12 @@ private: // // ON_SubDVertexArray // -class ON_SUBD_CLASS ON_SubDVertexArray + +/* +Description: + Avoid using ON_SubDVertexArray. ON_SubDVertexIterator is more efficient and a better choice. +*/ +class ON_CLASS ON_SubDVertexArray { public: ON_SubDVertexArray( @@ -6221,7 +9047,12 @@ private: // // ON_SubDEdgeArray // -class ON_SUBD_CLASS ON_SubDEdgeArray + +/* +Description: + Avoid using ON_SubDEdgeArray. ON_SubDEdgeIterator is more efficient and a better choice. +*/ +class ON_CLASS ON_SubDEdgeArray { public: ON_SubDEdgeArray( @@ -6272,7 +9103,12 @@ private: // // ON_SubDFaceArray // -class ON_SUBD_CLASS ON_SubDFaceArray + +/* +Description: + Avoid using ON_SubDFaceArray. ON_SubDFaceIterator is more efficient and a better choice. +*/ +class ON_CLASS ON_SubDFaceArray { public: ON_SubDFaceArray( @@ -6323,15 +9159,17 @@ private: // // ON_SubDVertexIterator // -class ON_SUBD_CLASS ON_SubDVertexIterator +class ON_CLASS ON_SubDVertexIterator { public: - // The ON_SubD member function - // ON_SubDVertexIterator ON_SubD::VertexIterator(subd_level_index) - // is the best way to get a vertex iterator. + // The best way to get an ON_SubDVertexIterator is so use the ON_SubD member function + // ON_SubDVertexIterator vit = subd.VertexIterator(); ON_SubDVertexIterator( const class ON_SubD& subd ); + + // The best way to get an ON_SubDVertexIterator is so use the ON_SubD member function + // ON_SubDVertexIterator vit = subd.VertexIterator(); ON_SubDVertexIterator( const class ON_SubDRef& subd_ref ); @@ -6525,21 +9363,71 @@ private: ////////////////////////////////////////////////////////////////////////// // -// ON_SubDEdgeIterator +// ON_SubDVertexIdIterator // -class ON_SUBD_CLASS ON_SubDEdgeIterator + +class ON_SubDVertexIdIterator : private ON_FixedSizePoolIterator { public: - // The ON_SubD member function - // ON_SubDEdgeIterator ON_SubD::EdgeIterator() - // is the best way to get an edge iterator from an ON_SubD. + ON_SubDVertexIdIterator() = default; + ~ON_SubDVertexIdIterator() = default; + ON_SubDVertexIdIterator(const ON_SubDVertexIdIterator&) = default; + ON_SubDVertexIdIterator& operator=(const ON_SubDVertexIdIterator&) = default; + +public: + ON_SubDVertexIdIterator(const ON_SubDRef& subd_ref); + ON_SubDVertexIdIterator(const ON_SubD& subd); + +public: + /* + Description: + In general, you want to use a ON_SubDVertexIterator to loop through SubD vertices. + This is a special tool for unusual sitiations wheh it is necessary to + iteratate through every vertex on every level of a SubD in order + of increasing m_id value. + Returns: + The vertex with the smallest id. + */ + const ON_SubDVertex* FirstVertex(); + + /* + Description: + In general, you want to use a ON_SubDVertexIterator to loop through SubD vertices. + This is a special tool for unusual sitiations wheh it is necessary to + iteratate through every vertex on every level of a SubD in order + of increasing m_id value. + Returns: + The vertex in order of increasing id. + */ + const ON_SubDVertex* NextVertex(); + + /* + Returns: + The most recently returned vertex from a call to FirstVertex() or NextVertex(). + */ + const ON_SubDVertex* CurrentVertex() const; + +private: + friend class ON_SubDHeap; + ON_SubDRef m_subd_ref; + void Internal_Init(); +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdgeIterator +// +class ON_CLASS ON_SubDEdgeIterator +{ +public: + // The best way to get an ON_SubDEdgeIterator is so use the ON_SubD member function + // ON_SubDEdgeIterator eit = subd.EdgeIterator(); ON_SubDEdgeIterator( const class ON_SubD& subd ); - // The ON_SubDRef member function - // ON_SubDEdgeIterator ON_SubDRef::EdgeIterator() - // is the best way to get an edge iterator from an ON_SubDRef. + // The best way to get an ON_SubDEdgeIterator is so use the ON_SubD member function + // ON_SubDEdgeIterator eit = subd.EdgeIterator(); ON_SubDEdgeIterator( const class ON_SubDRef& subd_ref ); @@ -6733,21 +9621,71 @@ private: ////////////////////////////////////////////////////////////////////////// // -// ON_SubDFaceIterator +// ON_SubDEdgeIdIterator // -class ON_SUBD_CLASS ON_SubDFaceIterator + +class ON_SubDEdgeIdIterator : private ON_FixedSizePoolIterator { public: - // The ON_SubD member function - // ON_SubDFaceIterator ON_SubD::FaceIterator() - // is the best way to get a face iterator from an ON_SubD. + ON_SubDEdgeIdIterator() = default; + ~ON_SubDEdgeIdIterator() = default; + ON_SubDEdgeIdIterator(const ON_SubDEdgeIdIterator&) = default; + ON_SubDEdgeIdIterator& operator=(const ON_SubDEdgeIdIterator&) = default; + +public: + ON_SubDEdgeIdIterator(const ON_SubDRef& subd_ref); + ON_SubDEdgeIdIterator(const ON_SubD& subd); + +public: + /* + Description: + In general, you want to use a ON_SubDEdgeIterator to loop through SubD edges. + This is a special tool for unusual sitiations wheh it is necessary to + iteratate through every edge on every level of a SubD in order + of increasing m_id value. + Returns: + The edge with the smallest id. + */ + const ON_SubDEdge* FirstEdge(); + + /* + Description: + In general, you want to use a ON_SubDEdgeIterator to loop through SubD edges. + This is a special tool for unusual sitiations wheh it is necessary to + iteratate through every edge on every level of a SubD in order + of increasing m_id value. + Returns: + The edge in order of increasing id. + */ + const ON_SubDEdge* NextEdge(); + + /* + Returns: + The most recently returned edge from a call to FirstEdge() or NextEdge(). + */ + const ON_SubDEdge* CurrentEdge() const; + +private: + friend class ON_SubDHeap; + ON_SubDRef m_subd_ref; + void Internal_Init(); +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFaceIterator +// +class ON_CLASS ON_SubDFaceIterator +{ +public: + // The best way to get an ON_SubDFaceIterator is so use the ON_SubD member function + // ON_SubDFaceIterator fit = subd.FaceIterator(); ON_SubDFaceIterator( const class ON_SubD& subd ); - // The ON_SubDRef member function - // ON_SubDFaceIterator ON_SubDRef::FaceIterator() - // is the best way to get a face iterator from an ON_SubDRef. + // The best way to get an ON_SubDFaceIterator is so use the ON_SubD member function + // ON_SubDFaceIterator fit = subd.FaceIterator(); ON_SubDFaceIterator( const class ON_SubDRef& subd_ref ); @@ -6941,11 +9879,63 @@ private: ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null; }; +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFaceIdIterator +// + +class ON_SubDFaceIdIterator : private ON_FixedSizePoolIterator +{ +public: + ON_SubDFaceIdIterator() = default; + ~ON_SubDFaceIdIterator() = default; + ON_SubDFaceIdIterator(const ON_SubDFaceIdIterator&) = default; + ON_SubDFaceIdIterator& operator=(const ON_SubDFaceIdIterator&) = default; + +public: + ON_SubDFaceIdIterator(const ON_SubDRef& subd_ref); + ON_SubDFaceIdIterator(const ON_SubD& subd); + +public: + /* + Description: + In general, you want to use a ON_SubDFaceIterator to loop through SubD faces. + This is a special tool for unusual sitiations wheh it is necessary to + iteratate through every face on every level of a SubD in order + of increasing m_id value. + Returns: + The face with the smallest id. + */ + const ON_SubDFace* FirstFace(); + + /* + Description: + In general, you want to use a ON_SubDFaceIterator to loop through SubD faces. + This is a special tool for unusual sitiations wheh it is necessary to + iteratate through every face on every level of a SubD in order + of increasing m_id value. + Returns: + The face in order of increasing id. + */ + const ON_SubDFace* NextFace(); + + /* + Returns: + The most recently returned face from a call to FirstFace() or NextFace(). + */ + const ON_SubDFace* CurrentFace() const; + +private: + friend class ON_SubDHeap; + ON_SubDRef m_subd_ref; + void Internal_Init(); +}; + ////////////////////////////////////////////////////////////////////////// // // ON_SubDComponentIterator // -class ON_SUBD_CLASS ON_SubDComponentIterator +class ON_CLASS ON_SubDComponentIterator { public: static const ON_SubDComponentIterator Empty; @@ -7056,12 +10046,194 @@ private: ON_SubDComponentPtr m_cptr_current = ON_SubDComponentPtr::Null; }; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDMeshFragmentIterator +// + +class ON_CLASS ON_SubDMeshFragmentIterator +{ +public: + ON_SubDMeshFragmentIterator() = default; + ~ON_SubDMeshFragmentIterator() = default; + ON_SubDMeshFragmentIterator(const ON_SubDMeshFragmentIterator&); + ON_SubDMeshFragmentIterator& operator=(const ON_SubDMeshFragmentIterator&); + +public: + ON_SubDMeshFragmentIterator(const class ON_SubDMesh limit_mesh); + ON_SubDMeshFragmentIterator(ON_SubDRef& subd_ref); + ON_SubDMeshFragmentIterator(const ON_SubD& subd); + ON_SubDMeshFragmentIterator(const ON_SubDFaceIterator& fit); + +public: + const ON_SubDMeshFragment* FirstFragment(); + const ON_SubDMeshFragment* NextFragment(); + const ON_SubDMeshFragment* CurrentFragment(); + + bool IsEmpty() const; + + const ON_SubD& SubD() const; + + /* + Returns + ON_SubDComponentLocation::Surface or ON_SubDComponentLocation::ControlNet + */ + ON_SubDComponentLocation SubDAppearance() const; + + /* + Parameters: + subd_appearance_override - [in] + ON_SubDComponentLocation::Unset - appearance controlled by SubD().SubDAppearance(). + ON_SubDComponentLocation::Surface + ON_SubDComponentLocation::ControlNet + Returns + ON_SubDComponentLocation::Surface or ON_SubDComponentLocation::ControlNet + */ + void SetSubDAppearanceOverride(ON_SubDComponentLocation subd_appearance_override); + + + /* + Returns: + Density setting used to create the fragments and the maximum mesh density + these fragments can deliver. + Remarks: + 0: a single mesh quad per SubD quad + 1: 4 mesh quads per SubD quad (n mesh quads per SubD n-gon when n != 4) + d: 4^d mesh quads per SubD quad (n*(4^(d-1)) mesh quads per SubD n-gon when n != 4) + */ + unsigned int MaximumMeshDensity() const; + + + /* + Returns: + Minimum mesh density that can be extracted from these fragments. + Remarks: + 0 = all fragments are full sized. + 1 = there are half sixed fragments because the SubD has n-gons with n != 4. + */ + unsigned int MinimumMeshDensity() const; + + /* + Parameters: + candidate_mesh_density - [in] + Returns: + If candidate_mesh_density > MeshDensity(), then MeshDensity() is returned. + If candidate_mesh_density < MinimumMeshDensity(), then MinimumMeshDensity() is returned. + Otherwise, candidate_mesh_density is returned. + */ + unsigned int ClampMeshDensity( + unsigned int candidate_mesh_density + ) const; + + /* + Returns: + Total number of fragments. + */ + unsigned int FragmentCount() const; + + /* + Returns: + Total number of full sized fragments. + Remarks: + A full sized fragment covers an entire quad face. + */ + unsigned int FullSizeFragmentCount() const; + + /* + Returns: + Total number of half sized fragments. + Remarks: + A half sized fragment covers the corner of an N-gon and are used when N != 4. + */ + unsigned int HalfSizeFragmentCount() const; + + + /* + Parameters: + full_size_fragment_count - [out] + Number of full sized fragments. These cover an entire quad face. + half_size_fragment_count - [out] + Number of half sized fragments. These cover a corner of an N-gon + and are used when N != 4. + Returns: + Total number of fragments. + */ + unsigned int GetFragmentCounts( + unsigned int& full_size_fragment_count, + unsigned int& half_size_fragment_count + ) const; + + /* + Parameters: + mesh_density - [in] + MinimumMeshDensity() <= mesh_density <= MeshDensity() + Returns: + Total number of mesh quads in at the specified mesh density. + */ + unsigned int TotalQuadCount(unsigned int mesh_density) const; + + /* + Parameters: + mesh_density - [in] + MinimumMeshDensity() <= mesh_density <= MeshDensity() + Returns: + Total number of mesh points delivered at the specified mesh density. + */ + unsigned int TotalPointCount(unsigned int mesh_density) const; + + /* + Returns: + Total number of mesh quads delivered at MaximumMeshDensity(). + */ + unsigned int MaximumDensityQuadCount() const; + + /* + Parameters: + mesh_density - [in] + MinimumMeshDensity() <= mesh_density <= MeshDensity() + Returns: + Total number of mesh points delivered at MaximumMeshDensity(). + */ + unsigned int MaximumDensityPointCount() const; + + const ON_BoundingBox SurfaceBoundingBox() const; + const ON_BoundingBox ControlNetQuadBoundingBox() const; + /* + Returns: + If this->SubDAppearance() is ON_SubDComponentLocation::ControlNet, then this->ControlNetQuadBoundingBox() is returned. + Otherwise this->SurfaceBoundingBox() returned. + */ + const ON_BoundingBox BoundingBox() const; + +private: + void Internal_CopyFrom(const ON_SubDMeshFragmentIterator& src); + + ON_SubDMesh m_limit_mesh; + ON_SubD m_subd; + ON_SubDFaceIterator m_fit; + const ON_SubDMeshFragment* m_current_fragment = nullptr; + bool m_bFromFaceFragments = false; + mutable bool m_bHaveCounts = false; + + // used to override the appearance of SubD().SubDApperance(). + ON_SubDComponentLocation m_subd_appearance_override = ON_SubDComponentLocation::Unset; + + mutable unsigned int m_maximum_mesh_density = 0; // See MaximumMeshDensity() comment + + // full sized fragment count (for quads) + mutable unsigned int m_full_size_fragment_count = 0; + + // half sized fragment count (for n-gons with n != 4) + mutable unsigned int m_half_size_fragment_count = 0; +}; + ////////////////////////////////////////////////////////////////////////// // // ON_SubDSectorIterator // -class ON_SUBD_CLASS ON_SubDSectorIterator +class ON_CLASS ON_SubDSectorIterator { public: static const ON_SubDSectorIterator Empty; @@ -7371,7 +10543,7 @@ private: // // ON_SubDFaceEdgeIterator // -class ON_SUBD_CLASS ON_SubDFaceEdgeIterator +class ON_CLASS ON_SubDFaceEdgeIterator { public: ON_SubDFaceEdgeIterator(); @@ -7431,6 +10603,31 @@ public: */ const ON_SubDEdge* PrevEdge(); + + /* + Parameters: + bReturnNullAtFirstEdge = [in] + If true and the next edge would be FirstEdge(), the nullptr is returned + and CurrentEdge() is not changed. + Description: + Increments the iterator and returns the edge. + */ + const ON_SubDEdge* NextEdge( + bool bReturnNullAtFirstEdge + ); + + /* + Parameters: + bReturnNullAtLastEdge = [in] + If true and the previous edge would be FirstEdge(), the nullptr is returned + and CurrentEdge() is not changed. + Description: + Decrements the iterator and returns the edge. + */ + const ON_SubDEdge* PrevEdge( + bool bReturnNullAtFirstEdge + ); + /* Returns: Current edge. @@ -7450,17 +10647,17 @@ private: ////////////////////////////////////////////////////////////////////////// // -// ON_SubDFromMeshOptions +// ON_ToSubDParameters // -class ON_SUBD_CLASS ON_SubDFromMeshOptions +class ON_CLASS ON_ToSubDParameters { public: - // Default construction is identical to ON_SubDFromMeshOptions::Smooth. - ON_SubDFromMeshOptions() = default; - ~ON_SubDFromMeshOptions() = default; - ON_SubDFromMeshOptions(const ON_SubDFromMeshOptions&) = default; - ON_SubDFromMeshOptions& operator=(const ON_SubDFromMeshOptions&) = default; + // Default construction is identical to ON_ToSubDParameters::Smooth. + ON_ToSubDParameters() = default; + ~ON_ToSubDParameters() = default; + ON_ToSubDParameters(const ON_ToSubDParameters&) = default; + ON_ToSubDParameters& operator=(const ON_ToSubDParameters&) = default; /////////////////////////////////////////////////////////////////////////////////////// // @@ -7468,21 +10665,21 @@ public: // // No interior creases and no corners. - static const ON_SubDFromMeshOptions Smooth; + static const ON_ToSubDParameters Smooth; // Create an interior sub-D crease along coincident input mesh edges // where the vertex normal directions at one end differ by at // least 30 degrees. - static const ON_SubDFromMeshOptions InteriorCreaseAtMeshCrease; + static const ON_ToSubDParameters InteriorCreaseAtMeshCrease; // Create an interior sub-D crease along all coincident input mesh edges. - static const ON_SubDFromMeshOptions InteriorCreaseAtMeshEdge; + static const ON_ToSubDParameters InteriorCreaseAtMeshEdge; /////////////////////////////////////////////////////////////////////////////////////// // // Custom interior crease options // -#pragma region RH_C_SHARED_ENUM [SubD::InteriorCreaseOption] [Rhino.Geometry.SubD.InteriorCreaseOption] [nested:byte] +#pragma region RH_C_SHARED_ENUM [SubD::InteriorCreaseOption] [Rhino.Geometry.SubDCreationOptions.InteriorCreaseOption] [nested:byte] /// ///Defines how interior creases are treated. /// @@ -7505,7 +10702,7 @@ public: }; #pragma endregion - static ON_SubDFromMeshOptions::InteriorCreaseOption InteriorCreaseOptionFromUnsigned( + static ON_ToSubDParameters::InteriorCreaseOption InteriorCreaseOptionFromUnsigned( unsigned int interior_crease_option_as_unsigned ); @@ -7514,20 +10711,20 @@ public: interior_crease_option - [in] */ void SetInteriorCreaseOption( - ON_SubDFromMeshOptions::InteriorCreaseOption interior_crease_option + ON_ToSubDParameters::InteriorCreaseOption interior_crease_option ); /* Returns: The interior crease option. */ - ON_SubDFromMeshOptions::InteriorCreaseOption InteriorCreaseTest() const; + ON_ToSubDParameters::InteriorCreaseOption InteriorCreaseTest() const; /* Description: When the interior crease option is - ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCreases, + ON_ToSubDParameters::InteriorCreaseOption::AtMeshCreases, the value of MinimumCreaseAngleRadians() determines which coincident input mesh edges generate sub-D creases. @@ -7547,7 +10744,7 @@ public: /* Description: When the interior crease option is - ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCreases, + ON_ToSubDParameters::InteriorCreaseOption::AtMeshCreases, the value of MinimumCreaseAngleRadians() determines which coincident input mesh edges generate sub-D creases. @@ -7568,8 +10765,8 @@ public: Returns: The currently selected interior crease option. */ - ON_SubDFromMeshOptions::InteriorCreaseOption CopyInteriorCreaseTest( - ON_SubDFromMeshOptions source_options + ON_ToSubDParameters::InteriorCreaseOption CopyInteriorCreaseTest( + ON_ToSubDParameters source_options ); @@ -7580,13 +10777,13 @@ public: // Look for convex corners at sub-D vertices with 2 edges // that have an included angle <= 90 degrees. - static const ON_SubDFromMeshOptions ConvexCornerAtMeshCorner; + static const ON_ToSubDParameters ConvexCornerAtMeshCorner; /////////////////////////////////////////////////////////////////////////////////////// // // Custom convex corner options // -#pragma region RH_C_SHARED_ENUM [SubD::ConvexCornerOption] [Rhino.Geometry.SubD.ConvexCornerOption] [nested:byte] +#pragma region RH_C_SHARED_ENUM [SubD::ConvexCornerOption] [Rhino.Geometry.SubDCreationOptions.ConvexCornerOption] [nested:byte] /// ///Defines how convex corners are treated. /// @@ -7606,7 +10803,7 @@ public: }; #pragma endregion - static ON_SubDFromMeshOptions::ConvexCornerOption ConvexCornerOptionFromUnsigned( + static ON_ToSubDParameters::ConvexCornerOption ConvexCornerOptionFromUnsigned( unsigned int convex_corner_option_as_unsigned ); @@ -7615,14 +10812,14 @@ public: convex_corner_option - [in] */ void SetConvexCornerOption( - ON_SubDFromMeshOptions::ConvexCornerOption convex_corner_option + ON_ToSubDParameters::ConvexCornerOption convex_corner_option ); /* Returns: The currently selected convex corner option. */ - ON_SubDFromMeshOptions::ConvexCornerOption ConvexCornerTest() const; + ON_ToSubDParameters::ConvexCornerOption ConvexCornerTest() const; /* Description: @@ -7681,30 +10878,96 @@ public: Returns: The currently selected convex corner option. */ - ON_SubDFromMeshOptions::ConvexCornerOption CopyConvexCornerTest( - ON_SubDFromMeshOptions source_parameters + ON_ToSubDParameters::ConvexCornerOption CopyConvexCornerTest( + ON_ToSubDParameters source_parameters ); + /* + Returns: + false - In ON_SubD::CreateFromMesh(), input mesh vertex locations will be used to set subd vertex control net locations. + true - In ON_SubD::CreateFromMesh(), input mesh vertex locations will be used to set subd vertex limit surface locations. + Remark: + Interpolation computation is available in Rhino, Rhino compute, Rhino Inside, + Grasshopper. Interpolation computation is not available in the opennurbs IO toolkit. + */ + bool InterpolateMeshVertices() const; + + /* + Parameters: + false + In ON_SubD::CreateFromMesh(), input mesh vertex locations will be used to set subd vertex control net locations. + This is the fastest and most robust way to create a subd from a mesh. + true + In ON_SubD::CreateFromMesh(), input mesh vertex locations will be used to set subd vertex limit surface locations. + If there are a large number of vertices, this option can require lots of computation. + Remark: + Interpolation computation is available in Rhino, Rhino compute, Rhino Inside, + Grasshopper. Interpolation computation is not available in the opennurbs IO toolkit. + */ + void SetInterpolateMeshVertices( + bool bInterpolateMeshVertices + ); + + /* + Returns: + true - In ON_SubD::CreateFromMesh(), colinear boundary edges belonging to the same face are merged into a single edge. + false - In ON_SubD::CreateFromMesh(), each mesh boundary edge becomes a SubD boundary edge. + */ + bool MergeColinearBoundaryEdges() const; + + /* + Parameters: + bMergeColinearBoundaryEdges - [in] + true + In ON_SubD::CreateFromMesh(), colinear boundary edges belonging to the same face are merged into a single edge. + In general, this is the best choice and leads to the highest quality of SubD object. + false + In ON_SubD::CreateFromMesh(), each mesh boundary edge becomes a SubD boundary edge. + Use this when the boundary topology of the mesh and SubD should be identical. + */ + void SetMergeColinearBoundaryEdges( + bool bMergeColinearBoundaryEdges + ); + + /* + Returns: + true - In ON_SubD::CreateFromMesh(), colinear interior edges between two faces are merged into a single edge. + false - In ON_SubD::CreateFromMesh(), each mesh interior edge becomes a SubD interior edge. + */ + bool MergeColinearInteriorEdges() const; + + /* + Parameters: + bMergeColinearInteriorEdges - [in] + true + In ON_SubD::CreateFromMesh(), colinear interior edges between two faces are merged into a single edge. + In general, this is the best choice and leads to the highest quality of SubD object. + false + In ON_SubD::CreateFromMesh(), each mesh interior edge becomes a SubD interior edge. + Use this when the interior topology of the mesh and SubD should be identical. + */ + void SetMergeColinearInteriorEdges( + bool bMergeColinearInteriorEdges + ); + - /////////////////////////////////////////////////////////////////////////////////////// - // - // Sub-D type option - // - ON_SubD::SubDType SubDType() const; - - void SetSubDType( - ON_SubD::SubDType subd_type - ); - private: - ON_SubD::SubDType m_subd_type = ON_SubD::SubDType::Unset; - unsigned char m_reserved1 = 0; - unsigned short m_reserved2 = 0; + enum : unsigned char + { + MergeColinearBoundaryEdgesMask = 1, + MergeColinearInteriorEdgesMask = 2 + }; + unsigned char m_merge_edges_bits = 0; // clear bit (0) = "true, set bit (1) = "false" + + bool m_bInterpolateMeshVertices = false; + + ON_ToSubDParameters::InteriorCreaseOption m_interior_crease_option = ON_ToSubDParameters::InteriorCreaseOption::None; + ON_ToSubDParameters::ConvexCornerOption m_convex_corner_option = ON_ToSubDParameters::ConvexCornerOption::None; - ON_SubDFromMeshOptions::InteriorCreaseOption m_interior_crease_option = ON_SubDFromMeshOptions::InteriorCreaseOption::None; - ON_SubDFromMeshOptions::ConvexCornerOption m_convex_corner_option = ON_SubDFromMeshOptions::ConvexCornerOption::None; unsigned short m_maximum_convex_corner_edge_count = 2U; + unsigned short m_reserved3 = 0; + double m_minimum_crease_angle_radians = ON_PI/6.0; // 30 degrees in radians double m_maximum_convex_corner_angle_radians = 0.501*ON_PI; // 90 degrees (+ a smidge) in radians }; @@ -7716,7 +10979,7 @@ private: // Used when an ON_SubD vertex, edge or face needs to be sent around as // a piece of ON_Geometry. // -class ON_SUBD_CLASS ON_SubDComponentRef : public ON_Geometry +class ON_CLASS ON_SubDComponentRef : public ON_Geometry { ON_OBJECT_DECLARE(ON_SubDComponentRef); public: @@ -7741,38 +11004,41 @@ public: Parameters: subd_ref - [in] component_index - [in] - bLimitSurface - [in] - true - the reference is to the limit surface location - false - the reference is to the control net location + component_location - [in] */ - static ON_SubDComponentRef Create( + static const ON_SubDComponentRef Create( const ON_SubDRef& subd_ref, ON_COMPONENT_INDEX component_index, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ); - static ON_SubDComponentRef Create( + static const ON_SubDComponentRef Create( const ON_SubDRef& subd_ref, ON_SubDComponentPtr component_ptr, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ); - static ON_SubDComponentRef Create( + static const ON_SubDComponentRef Create( const ON_SubDRef& subd_ref, const class ON_SubDVertex* vertex, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ); - static ON_SubDComponentRef Create( + static const ON_SubDComponentRef Create( const ON_SubDRef& subd_ref, const class ON_SubDEdge* edge, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ); - static ON_SubDComponentRef Create( + static const ON_SubDComponentRef Create( const ON_SubDRef& subd_ref, const class ON_SubDFace* face, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ); void Clear(); @@ -7787,6 +11053,15 @@ public: ON_SubDComponentLocation ComponentLocation() const; + /* + The interpretation of this value depends on the context. + */ + ON__UINT_PTR ReferenceId() const; + + void SetReferenceId( + ON__UINT_PTR reference_id + ); + const class ON_SubDVertex* Vertex() const; const class ON_SubDEdge* Edge() const; @@ -7801,6 +11076,7 @@ private: ON_SubDComponentPtr m_component_ptr = ON_SubDComponentPtr::Null; ON_COMPONENT_INDEX m_component_index = ON_COMPONENT_INDEX::UnsetComponentIndex; ON_SubDComponentLocation m_component_location = ON_SubDComponentLocation::Unset; + ON__UINT_PTR m_reference_id = 0; public: // overrides of virtual ON_Object functions @@ -7834,13 +11110,15 @@ public: const ON_SubDComponentRef& Append( const ON_SubDRef& subd_ref, ON_COMPONENT_INDEX ci, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ); const ON_SubDComponentRef& Append( const ON_SubDRef& subd_ref, ON_SubDComponentPtr component_ptr, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ); /* @@ -7848,6 +11126,7 @@ public: Appends a copy of src_ref and returns a pointer to the ON_SubDComponentRef managed by this class. */ + const ON_SubDComponentRef& Append(const ON_SubDComponentRef& src_ref); const ON_SubDComponentRef& Append(const ON_SubDComponentRef* src_ref); /* @@ -7866,13 +11145,15 @@ public: const ON_SubDComponentRef& AppendForExperts( const ON_SubD& subd, ON_COMPONENT_INDEX ci, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ); const ON_SubDComponentRef& AppendForExperts( const ON_SubD& subd, ON_SubDComponentPtr component_ptr, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ); /* @@ -7982,7 +11263,7 @@ private: // // Used in selection tests to return a point and parameters on a component. // -class ON_SUBD_CLASS ON_SubDComponentPoint +class ON_CLASS ON_SubDComponentPoint { public: static const ON_SubDComponentPoint Unset; @@ -7994,10 +11275,10 @@ public: /* Description: - Compares the m_component_ptr values. This function is useful for sorting - arrays of ON_SubDComponentPoint values remove duplicates. + Dictionary compare of component type, component pointer, and component direction (partial). + This function is useful for sorting arrays of ON_SubDComponentPoint values remove duplicates. */ - static int CompareComponentPtr( + static int CompareComponentAndDirection( const ON_SubDComponentPoint* a, const ON_SubDComponentPoint* b ); @@ -8021,7 +11302,7 @@ public: // // ON_SubDMatrix // -class ON_SUBD_CLASS ON_SubDMatrix +class ON_CLASS ON_SubDMatrix { public: ON_SubDMatrix() = default; @@ -8050,34 +11331,36 @@ public: bool IsValid() const; bool IsValidPointRing( + const double* point_ring, size_t point_ring_count, - size_t point_ring_stride, - const double* point_ring + size_t point_ring_stride ) const; bool EvaluateSubdivisionPoint( unsigned int component_index, + const double* point_ring, size_t point_ring_count, size_t point_ring_stride, - const double* point_ring, double subd_point[3] ) const; - bool EvaluateLimitPoint( + bool EvaluateSurfacePoint( + const double* point_ring, size_t point_ring_count, size_t point_ring_stride, - const double* point_ring, + bool bUndefinedNormalIsPossible, double limit_point[3], double limit_tangent1[3], double limit_tangent2[3], double limit_normal[3] ) const; - bool EvaluateLimitPoint( + bool EvaluateSurfacePoint( + const double* point_ring, size_t point_ring_count, size_t point_ring_stride, - const double* point_ring, - class ON_SubDSectorLimitPoint& limit_point + bool bUndefinedNormalIsPossible, + class ON_SubDSectorSurfacePoint& limit_point ) const; /* @@ -8233,8 +11516,8 @@ public: ); double TestComponentRing( - size_t component_ring_count, - const ON_SubDComponentPtr* component_ring + const ON_SubDComponentPtr* component_ring, + size_t component_ring_count ) const; /* @@ -8254,12 +11537,11 @@ public: limit_normal - [out] */ static double TestEvaluation( - ON_SubD::SubDType subd_type, const unsigned int subd_recursion_count, ON_SubDSectorIterator sit, ON_SimpleArray& component_ring, ON_SimpleArray< ON_3dPoint >& subd_points, - class ON_SubDSectorLimitPoint& limit_point + class ON_SubDSectorSurfacePoint& limit_point ); private: @@ -8273,7 +11555,11 @@ private: // ON_SubD_FixedSizeHeap // -class ON_SUBD_CLASS ON_SubD_FixedSizeHeap +/* +Description: + A ON_SubD_FixedSizeHeap is used to manage heap used for a local subdivision. +*/ +class ON_CLASS ON_SubD_FixedSizeHeap { private: static unsigned int m__sn_factory; @@ -8288,24 +11574,37 @@ public: /* Description: - Reserve enough room to accomodate a face - with one extraordinary vertex. + Reserve enough room to for a subdivision of a vertex sector. Parameters: - subd_type - [in] - extraordinary_valence - [in] + sector_edge_count - [in] + Number of edges in the sector. */ bool ReserveSubDWorkspace( - ON_SubD::SubDType subd_type, - unsigned int extraordinary_valence + unsigned int sector_edge_count ); + /* + Description: + Reserve enough room for a local subdivide the neighborhood of center_face. + Parameters: + center_face0 - [in] + */ bool ReserveSubDWorkspace( - size_t vertex_capacity, - size_t edge_capacity, - size_t face_capacity, - size_t array_capacity + const ON_SubDFace* center_face0 ); +private: + bool Internal_ReserveSubDWorkspace( + size_t vertex_capacity, + size_t face_capacity, + size_t array_capacity, + bool bEnableHashTable + ); + + bool Internal_ReserveSubDWorkspace_HashTable(); + +public: + /* Description: Reset this ON_SubD_FixedSizeHeap so it can be used again. @@ -8322,34 +11621,84 @@ public: ON_SubDVertex* AllocateVertex( const double vertexP[3], - unsigned int edge_capacity, - unsigned int face_capacity + unsigned int edge_capacity ); + /* + Description: + Allocate a vertex located at the vertex0 subdivision point. + + Parameters: + vertex0 - [in] + edge_capacity - [in] + If the returned vertex will be interior in a local subdivision, + then pass vertex0->EdgeCount(). + If the returned vertex will be an outer ring vertex in a local subdivision, + then pass 3. + */ ON_SubDVertex* AllocateVertex( const ON_SubDVertex* vertex0, - ON_SubD::SubDType subd_type, - bool bUseSavedSubdivisionPoint, - unsigned int edge_capacity, - unsigned int face_capacity + unsigned int edge_capacity ); + /* + Description: + Allocate a vertex located at the edge0 subdivision point. + The vertex will have an edge and face capacity of 4. + Parameters: + edge0 - [in] + */ ON_SubDVertex* AllocateVertex( - const ON_SubDEdge* edge0, - ON_SubD::SubDType subd_type, - bool bUseSavedSubdivisionPoint, - unsigned int edge_capacity, - unsigned int face_capacity + const ON_SubDEdge* edge0 ); - ON_SubDVertex* AllocateVertex( - const ON_SubDFace* face0, - ON_SubD::SubDType subd_type, - bool bUseSavedSubdivisionPoint, - unsigned int edge_capacity, - unsigned int face_capacity + /* + Description: + Find or allocate a vertex located at the edge0 subdivision point. + The vertex will have an edge and face capacity of 4. + Parameters: + edge0 - [in] + Remarks: + In order for FindOrAllocateVertex() to find a vertex, that vertex must + have been created by an earlier call to FindOrAllocateVertex(). + Typically, AllocateVertex(edge0) is used for center face boundary edges + and FindOrAllocateVertex(edge0) is used for ring edges. + */ + ON_SubDVertex* FindOrAllocateVertex( + const ON_SubDEdge* edge0 ); + /* + Description: + Find or allocate a vertex and the face subdivision point. The vertex will have an + edge and face capacity of face0->EdgeCount(). + Parameters: + face0 - [in] + outer face in a local subdivision situation + Remarks: + In order for FindOrAllocateVertex() to find a vertex, that vertex must + have been created by an earlier call to FindOrAllocateVertex(). + Typically, AllocateVertex(edge0) is used for the center face and + and FindOrAllocateVertex(edge0) is used for ring faces. + */ + ON_SubDVertex* FindOrAllocateVertex( + const ON_SubDFace* face0 + ); + + + /* + Description: + Allocate a vertex located at the sector_face0 subdivision point. + The vertex will have an edge and face capacity of 3. + Parameters: + sector_face0 - [in] + A face in a vertex sector. + */ + ON_SubDVertex* AllocateSectorFaceVertex( + const ON_SubDFace* sector_face0 + ); + + /* Parameters: v0 - [in] @@ -8374,21 +11723,36 @@ public: If v0 or v1 is not null, then ON_SubDEdge.m_level is set to the maximum of v0->m_level or v1->m_level. */ - ON_SubDEdge* AllocateEdge( + const ON_SubDEdgePtr AllocateEdge( ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, double v1_sector_weight ); + /* + Description: + NOTE WELL: + In order for FindOrAllocateEdge() to find an edge, that edge must have been created + by an earlier call to FindOrAllocateEdge(). + */ + const ON_SubDEdgePtr FindOrAllocateEdge( + ON_SubDVertex* v0, + double v0_sector_weight, + ON_SubDVertex* v1, + double v1_sector_weight + ); + + private: /* Returns: A face with all field values zero (same values as ON_SubDEdge::Face), except ON_SubDFace.m_id. */ - ON_SubDFace* AllocateFace( + ON_SubDFace* Internal_AllocateFace( unsigned int zero_face_id, unsigned int parent_face_id ); + public: ON_SubDFace* AllocateQuad( unsigned int zero_face_id, @@ -8396,10 +11760,13 @@ public: const ON_SubDEdgePtr eptrs[4] ); - ON_SubDFace* AllocateTri( + ON_SubDFace* AllocateQuad( unsigned int zero_face_id, unsigned int parent_face_id, - const ON_SubDEdgePtr eptrs[3] + ON_SubDEdgePtr e0, + ON_SubDEdgePtr e1, + ON_SubDEdgePtr e2, + ON_SubDEdgePtr e3 ); /* @@ -8429,13 +11796,11 @@ public: from AllocatePtrArray(). */ bool ReturnPtrArray( - unsigned int capacity, - void* p + void* p, + unsigned int capacity ); private: - //unsigned int m_max_valence = 0; - ON_SubDVertex* m_v = nullptr; unsigned int m_v_capacity = 0; unsigned int m_v_index = 0; @@ -8452,13 +11817,29 @@ private: unsigned int m_p_capacity = 0; unsigned int m_p_index = 0; +private: + // Used to find level 1 subdivision vertex from level 0 component + class ON_SubD_FixedSizeHeap_ComponentPairHashElement** m_hash_table = nullptr; + class ON_SubD_FixedSizeHeap_ComponentPairHashElement* m_hash_elements = nullptr; + unsigned int m_h_capacity = 0; // m_hash_table[] capacity. + unsigned int m_h_count = 0; // 0xFFFFFFFFU means hash is disabled + enum : unsigned int + { + DisabledHashCount = 0xFFFFFFFFU + }; + + bool Internal_HashEnabled() const; + unsigned int Internal_Hash(ON_SubDComponentPtr component0); + class ON_SubDVertex* Internal_HashFindVertex1(unsigned int hash, ON_SubDComponentPtr component0); + void Internal_HashAddPair(unsigned int hash, ON_SubDComponentPtr component0, class ON_SubDVertex* vertex1); + private: // copies not supported ON_SubD_FixedSizeHeap(const ON_SubD_FixedSizeHeap&) = delete; ON_SubD_FixedSizeHeap& operator=(const ON_SubD_FixedSizeHeap&) = delete; }; -class ON_SUBD_CLASS ON_SubDEdgeChain +class ON_CLASS ON_SubDEdgeChain { public: ON_SubDEdgeChain() = default; @@ -8471,6 +11852,52 @@ public: public: + ///////////////////////////////////////////////////////// + // + // Edge chain tools + // + /* + Description: + Sort edges into a chains + + Parameters: + unsorted_edges - [in] + To sort an array in place, pass the same array as both parameters. + If unsorted_edges[] contains three or more edges that share a common vertex, + then all of the edges that share that vertex are ignored. + The edges can be from one or more SubDs. + + unsigned int minimum_chain_length - [in] + minimum number of edges to consider for a chain. + + sorted_edges - [out] + The sorted_edges[] has the edges grouped into edge chains. + + In an edge chain subsequent edges share a common vertex; i.e. + sorted_edges[i].RelativeVertex(1) == sorted_edges[i+1].RelativeVertex(0). + + When sorted_edges[i].RelativeVertex(1) != sorted_edges[i+1].RelativeVertex(0), + a chain ends at sorted_edges[i] and another begins at sorted_edges[i+1]. + + The first edge in every chain has the same orientation as the input edge + from unsorted_edges[]. + + Returns: + Number of chains in sorted_edges[]. + */ + static unsigned int SortEdgesIntoEdgeChains( + const ON_SimpleArray< ON_SubDEdgePtr >& unsorted_edges, + unsigned int minimum_chain_length, + ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges + ); + + static unsigned int SortEdgesIntoEdgeChains( + const ON_SimpleArray< const ON_SubDEdge* >& unsorted_edges, + unsigned int minimum_chain_length, + ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges + ); + + ///////////////////////////////////////////////////////// // // Edge chain tools @@ -8484,14 +11911,37 @@ public: search_direction - [in] ON_ChainDirection::Previous or ON_ChainDirection::Next. The search direction is relative to this->EdgeDirection(). - bStopAtTagChange - [in] - If true and the edge is smooth, the chain will stop if it encounters a vertex that is not smooth. - If true and the edge is a crease, the chain will stop if it encounters a vertex is smooth. + chain_type - [in] + Determines what edge/vertex tag conditions must be satisified by the neighbor. + Returns: + The next or previous edge in the chain if it exists. + Otherwise, nullptr is returned. + Remarks: + When multiple edges are candidates, there is a bias to preserve smooth/crease and a bias to + preserve face count. If the biases don't reduce the list of candidates to one or bStopAtBreak is true + and a creaase/smooth break is encountered, then ON_SubDEdgePtr::Null is returned. + */ + static const ON_SubDEdgePtr EdgeChainNeighbor( + ON_SubDEdgePtr starting_edge, + ON_ChainDirection search_direction, + ON_SubD::ChainType chain_type + ); + + /* + Description: + Get the neighboring link in an edge chain. + Parameters: + starting_edge - [in] + search_direction - [in] + ON_ChainDirection::Previous or ON_ChainDirection::Next. + The search direction is relative to this->EdgeDirection(). + chain_type - [in] + Determines what edge/vertex tag conditions must be satisified by the neighbor. bEnableStatusCheck - [in] status_pass - [in] status_fail - [in] If bEnableStatusFilter is false, then no status checks are performed. - If bEnableStatusFilter is true, ON_ComponentStatus(candidate_edge->m_status,status_pass,status_fail) + If bEnableStatusFilter is true, ON_ComponentStatus::StatusCheck(candidate_edge->m_status,status_pass,status_fail) must be true for candidate_edge to be returned. Returns: The next or previous edge in the chain if it exists. @@ -8504,7 +11954,7 @@ public: static const ON_SubDEdgePtr EdgeChainNeighbor( ON_SubDEdgePtr starting_edge, ON_ChainDirection search_direction, - bool bStopAtTagChange, + ON_SubD::ChainType chain_type, bool bEnableStatusCheck, ON_ComponentStatus status_pass, ON_ComponentStatus status_fail @@ -8531,9 +11981,9 @@ public: edge_chain - [in/out] */ static void ReverseEdgeChain( - size_t edge_count, - ON_SubDEdgePtr* edge_chain - ); + ON_SubDEdgePtr* edge_chain, + size_t edge_count + ); /* Description: @@ -8569,8 +12019,8 @@ public: True if edge_chain[] is valid. */ static bool IsValidEdgeChain( - size_t edge_count, const ON_SubDEdgePtr* edge_chain, + size_t edge_count, bool bCheckForDuplicateEdges ); @@ -8597,8 +12047,24 @@ public: const ON_SubDVertex* vertex ) const; + /* + Returns: + True if the edge chain has 3 or more edges that form a closed loop. + */ bool IsClosedLoop() const; + /* + Parameters: + bStrictlyConvex - [in] + If true, then a colinear pair of edges is not considered convex. + Returns: + True if the edge chain has 3 or more edges that forma a convex loop. + */ + bool IsConvexLoop( + bool bStrictlyConvex + ) const; + + unsigned int BeginEdgeChain( ON_SubDRef subd_ref, const ON_SubDEdge* initial_edge @@ -8611,9 +12077,9 @@ public: unsigned int BeginEdgeChain( ON_SubDRef subd_ref, - size_t edge_count, - const ON_SubDEdge*const* initial_edge_chain - ); + const ON_SubDEdge*const* initial_edge_chain, + size_t edge_count + ); unsigned int BeginEdgeChain( ON_SubDRef subd_ref, @@ -8627,9 +12093,9 @@ public: unsigned int BeginEdgeChain( ON_SubDRef subd_ref, - size_t edge_count, - const ON_SubDEdgePtr* initial_edge_chain - ); + const ON_SubDEdgePtr* initial_edge_chain, + size_t edge_count + ); unsigned int EdgeCount() const; @@ -8665,12 +12131,12 @@ public: unsigned int AddOneNeighbor( ON_ChainDirection direction, - bool bStopAtTagChange + ON_SubD::ChainType chain_type ); unsigned int AddAllNeighbors( ON_ChainDirection direction, - bool bStopAtTagChange + ON_SubD::ChainType chain_type ); unsigned int AddEdge( @@ -8693,6 +12159,7 @@ private: ON_ComponentStatus m_status_check_fail = ON_ComponentStatus::Selected; }; + #if defined(ON_COMPILING_OPENNURBS) /* The ON_SubDAsUserData class is used to attach a subd to it proxy mesh @@ -8773,5 +12240,3 @@ private: #endif #endif - -#endif diff --git a/opennurbs_subd_archive.cpp b/opennurbs_subd_archive.cpp index c64b4297..b0e615ca 100644 --- a/opennurbs_subd_archive.cpp +++ b/opennurbs_subd_archive.cpp @@ -26,9 +26,7 @@ //////////////////////////////////////////////////////////////// */ -#if defined(OPENNURBS_SUBD_WIP) - -static bool WriteDouble3( +static bool Internal_WriteDouble3( const double x[3], ON_BinaryArchive& archive ) @@ -42,7 +40,7 @@ static bool WriteDouble3( return ON_SUBD_RETURN_ERROR(false); } -static bool ReadDouble3( +static bool Internal_ReadDouble3( ON_BinaryArchive& archive, double x[3] ) @@ -56,6 +54,90 @@ static bool ReadDouble3( return ON_SUBD_RETURN_ERROR(false); } +enum : unsigned char +{ + ON_SubDComponentArchiveAnonymousChunkMark = 254U, + ON_SubDComponentArchiveEndMark = 255U +}; + +static bool Internal_ReadComponentAdditionSize(ON_BinaryArchive& archive, unsigned char valid_sz, unsigned char* sz) +{ + if (archive.Archive3dmVersion() < 70) + { + return ON_SUBD_RETURN_ERROR(false); + } + + if (0 == valid_sz) + return ON_SUBD_RETURN_ERROR(false); + *sz = (1 == valid_sz) ? 2 : 1; + if (false == archive.ReadChar(sz)) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 != *sz && valid_sz != *sz ) + return ON_SUBD_RETURN_ERROR(false); + return true; +} + +static bool Internal_WriteComponentAdditionSize(bool bHaveAddition, ON_BinaryArchive& archive, unsigned char sz) +{ + if (archive.Archive3dmVersion() < 70) + { + return ON_SUBD_RETURN_ERROR(false); + } + + if (0 == sz) + return ON_SUBD_RETURN_ERROR(false); + if (false == bHaveAddition) + sz = 0; + if (false == archive.WriteChar(sz)) + return ON_SUBD_RETURN_ERROR(false); + return true; +} + +static bool Internal_FinishReadingComponentAdditions(ON_BinaryArchive& archive) +{ + if (archive.Archive3dmVersion() < 70) + { + return ON_SUBD_RETURN_ERROR(false); + } + + unsigned char sz = 1; + if ( false == archive.ReadChar(&sz)) + return ON_SUBD_RETURN_ERROR(false); + + for (;;) + { + if (ON_SubDComponentArchiveEndMark == sz) + return true; + if (ON_SubDComponentArchiveAnonymousChunkMark == sz) + { + // skip an addition a future version added as an anonymous chunk + int v = 0; + if (false == archive.BeginRead3dmAnonymousChunk(&v)) + break; + if (false == archive.EndRead3dmChunk()) + break; + } + else if ( sz > 0 ) + { + // skip an addition a future version added as a fixed number of bytes + if (false == archive.SeekForward(sz)) + break; + } + sz = 0; + if (false == archive.ReadChar(&sz)) + break; + } + return ON_SUBD_RETURN_ERROR(false); +} + +static bool Internal_FinishWritingComponentAdditions(ON_BinaryArchive& archive) +{ + if (archive.Archive3dmVersion() < 70) + return ON_SUBD_RETURN_ERROR(false); + const unsigned char sz = ON_SubDComponentArchiveEndMark; + return archive.WriteChar(sz); +} + static bool WriteBase( const ON_SubDComponentBase* base, ON_BinaryArchive& archive @@ -65,7 +147,7 @@ static bool WriteBase( { unsigned int archive_id = base->ArchiveId(); unsigned int id = base->m_id; - unsigned short level = base->m_level; + unsigned short level = (unsigned short)base->SubdivisionLevel(); if (!archive.WriteInt(archive_id)) break; if (!archive.WriteInt(id)) @@ -73,36 +155,57 @@ static bool WriteBase( if (!archive.WriteShort(level)) break; - double P[3], V[3]; - bool bHaveP = false; - bool bHaveV = false; - ON_SubD::SubDType subd_P_type = base->SavedSubdivisionPointType(); - ON_SubD::SubDType subd_V_type = base->DisplacementType(); - if (ON_SubD::SubDType::Unset != subd_P_type) - bHaveP = base->GetSavedSubdivisionPoint(subd_P_type, P); - - if (ON_SubD::SubDType::Unset != subd_V_type) - bHaveV = base->GetSavedSubdivisionPoint(subd_V_type, V); - - unsigned char cP = bHaveP ? ((unsigned char)subd_P_type) : 0U; - if (!archive.WriteChar(cP)) - break; - if (0 != cP) + if (archive.Archive3dmVersion() < 70) { - if (!WriteDouble3(P,archive)) + // version 6 3dm files + double P[3], V[3]; + const bool bHaveP = base->GetSavedSubdivisionPoint(P); + const bool bHaveV = base->GetSubdivisionDisplacement(V); + + unsigned char cP = bHaveP ? 4U : 0U; + if (!archive.WriteChar(cP)) + break; + if (0 != cP) + { + if (!Internal_WriteDouble3(P, archive)) + break; + } + + unsigned char cV = bHaveV ? 4U : 0U; + if (!archive.WriteChar(cV)) + break; + if (0 != cV) + { + if (!Internal_WriteDouble3(V, archive)) + break; + } + return true; + } + + // version 7 3dm files and later + + // 24 byte displacement addition + double V[3]; + const bool bWriteDisplacement = base->GetSubdivisionDisplacement(V); + if ( false == Internal_WriteComponentAdditionSize(bWriteDisplacement,archive,24) ) + break; + if (bWriteDisplacement) + { + if (!archive.WriteDouble(3,V)) break; } - unsigned char cV = bHaveV ? ((unsigned char)subd_V_type) : 0U; - if (!archive.WriteChar(cV)) + // 4 byte group id addition + const bool bWriteGroupId = base->m_group_id > 0; + if (false == Internal_WriteComponentAdditionSize(bWriteGroupId, archive, 4)) break; - if (0 != cV) + if (bWriteGroupId) { - if (!WriteDouble3(V,archive)) + if (!archive.WriteInt(base->m_group_id)) break; } - return true; + return Internal_FinishWritingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); } @@ -125,37 +228,73 @@ static bool ReadBase( if (!archive.ReadShort(&level)) break; - unsigned char cP = 0U; - unsigned char cV = 0U; - double P[3], V[3]; - - if (!archive.ReadChar(&cP)) - break; - if (0 != cP) - { - if (!ReadDouble3(archive,P)) - break; - } - - if (!archive.ReadChar(&cV)) - break; - if (0 != cV) - { - if (!ReadDouble3(archive,V)) - break; - } - base.m_id = id; base.SetArchiveId(archive_id); - base.m_level = level; - if ( 0 != cP ) - base.SetSavedSubdivisionPoint(ON_SubD::SubDTypeFromUnsigned(cP),P); - if ( 0 != cV ) - base.SetDisplacement(ON_SubD::SubDTypeFromUnsigned(cV),V); + base.SetSubdivisionLevel(level); + if (archive.Archive3dmVersion() < 70) + { + unsigned char cP = 0U; + unsigned char cV = 0U; + double P[3], V[3]; - return true; + if (!archive.ReadChar(&cP)) + break; + if (0 != cP) + { + if (!Internal_ReadDouble3(archive, P)) + break; + } + + if (!archive.ReadChar(&cV)) + break; + if (0 != cV) + { + if (!Internal_ReadDouble3(archive, V)) + break; + const unsigned int no_displacement_version = ON_VersionNumberConstruct(7, 0, 2019, 6, 12, 0); + const unsigned int archive_opennurbs_version = archive.ArchiveOpenNURBSVersion(); + if (archive_opennurbs_version <= no_displacement_version) + { + cV = 0; + V[0] = 0.0; + V[1] = 0.0; + V[2] = 0.0; + } + } + if (4 == cP) + base.SetSavedSubdivisionPoint(P); + if (4 == cV) + base.SetSubdivisionDisplacement(V); + return true; + } + + // read additions + unsigned char sz; + + // 24 byte displacement addition + if (false == Internal_ReadComponentAdditionSize(archive, 24, &sz)) + break; + if (0 != sz) + { + double V[3] = {}; + if (!archive.ReadDouble(V)) + break; + base.SetSubdivisionDisplacement(V); + } + + // 4 byte group id addition + if (false == Internal_ReadComponentAdditionSize(archive, 4, &sz)) + break; + if (0 != sz) + { + if (!archive.ReadInt(&base.m_group_id)) + break; + } + + return Internal_FinishReadingComponentAdditions(archive); } + return ON_SUBD_RETURN_ERROR(false); } @@ -168,7 +307,7 @@ static bool WriteArchiveIdAndFlags( { if (!archive.WriteInt(archive_id)) return ON_SUBD_RETURN_ERROR(false); - unsigned char flags = (unsigned char)ON_SUBD_ELEMENT_FLAGS(ptr_flags); + unsigned char flags = (unsigned char)ON_SUBD_COMPONENT_FLAGS(ptr_flags); if (!archive.WriteChar(flags)) return ON_SUBD_RETURN_ERROR(false); return true; @@ -189,22 +328,22 @@ static bool ReadArchiveIdAndFlagsIntoComponentPtr( if (!archive.ReadChar(&flags)) return ON_SUBD_RETURN_ERROR(false); element_ptr = archive_id; - element_ptr *= (ON_SUBD_ELEMENT_FLAGS_MASK+1); - element_ptr += (flags & ON_SUBD_ELEMENT_FLAGS_MASK); + element_ptr *= (ON_SUBD_COMPONENT_FLAGS_MASK+1); + element_ptr += (flags & ON_SUBD_COMPONENT_FLAGS_MASK); return true; } static bool WriteSavedLimitPointList( unsigned int vertex_face_count, - ON_SubD::SubDType subd_type, - const ON_SubDSectorLimitPoint& limit_point, + bool bHaveLimitPoint, + const ON_SubDSectorSurfacePoint& limit_point, ON_BinaryArchive& archive ) { unsigned int limit_point_count = 0; - const ON_SubDSectorLimitPoint* p; + const ON_SubDSectorSurfacePoint* p; - if (ON_SubD::SubDType::Unset != subd_type) + if (bHaveLimitPoint) { for (p = &limit_point; nullptr != p && limit_point_count <= vertex_face_count; p = p->m_next_sector_limit_point) { @@ -220,12 +359,12 @@ static bool WriteSavedLimitPointList( if (limit_point_count > vertex_face_count) limit_point_count = 0; } - if ( 0 == limit_point_count ) - subd_type = ON_SubD::SubDType::Unset; + if (0 == limit_point_count) + bHaveLimitPoint = false; for (;;) { - unsigned char c = (unsigned char)subd_type; + unsigned char c = bHaveLimitPoint ? 4 : 0; if (!archive.WriteChar(c)) break; @@ -238,13 +377,13 @@ static bool WriteSavedLimitPointList( p = &limit_point; for (unsigned int i = 0; i < limit_point_count; i++, p = p->m_next_sector_limit_point ) { - if (!WriteDouble3(limit_point.m_limitP, archive)) + if (!Internal_WriteDouble3(limit_point.m_limitP, archive)) break; - if (!WriteDouble3(limit_point.m_limitT1, archive)) + if (!Internal_WriteDouble3(limit_point.m_limitT1, archive)) break; - if (!WriteDouble3(limit_point.m_limitT2, archive)) + if (!Internal_WriteDouble3(limit_point.m_limitT2, archive)) break; - if (!WriteDouble3(limit_point.m_limitN, archive)) + if (!Internal_WriteDouble3(limit_point.m_limitN, archive)) break; if (!WriteArchiveIdAndFlags(limit_point.m_sector_face ? limit_point.m_sector_face->ArchiveId() : 0, 0, archive)) break; @@ -257,8 +396,7 @@ static bool WriteSavedLimitPointList( static bool ReadSavedLimitPointList( ON_BinaryArchive& archive, unsigned int vertex_face_count, - ON_SubD::SubDType& limitP_type, - ON_SimpleArray< ON_SubDSectorLimitPoint > limit_points + ON_SimpleArray< ON_SubDSectorSurfacePoint > limit_points ) { limit_points.SetCount(0); @@ -286,14 +424,14 @@ static bool ReadSavedLimitPointList( for ( unsigned int i = 0; i < limit_point_count; i++ ) { - ON_SubDSectorLimitPoint limit_point = ON_SubDSectorLimitPoint::Unset; - if (!ReadDouble3(archive,limit_point.m_limitP)) + ON_SubDSectorSurfacePoint limit_point = ON_SubDSectorSurfacePoint::Unset; + if (!Internal_ReadDouble3(archive,limit_point.m_limitP)) break; - if (!ReadDouble3(archive,limit_point.m_limitT1)) + if (!Internal_ReadDouble3(archive,limit_point.m_limitT1)) break; - if (!ReadDouble3(archive,limit_point.m_limitT2)) + if (!Internal_ReadDouble3(archive,limit_point.m_limitT2)) break; - if (!ReadDouble3(archive,limit_point.m_limitN)) + if (!Internal_ReadDouble3(archive,limit_point.m_limitN)) break; ON_SubDFacePtr fptr = ON_SubDFacePtr::Null; if (!ReadArchiveIdAndFlagsIntoComponentPtr(archive,fptr.m_ptr)) @@ -304,7 +442,9 @@ static bool ReadSavedLimitPointList( if (limit_point_count != limit_points.UnsignedCount() ) break; - limitP_type = ON_SubD::SubDTypeFromUnsigned(c); + if (4 != c) + limit_points.SetCount(0); + return true; } return ON_SUBD_RETURN_ERROR(false); @@ -542,25 +682,6 @@ static bool ReadFacePtrList( return ON_SUBD_RETURN_ERROR(false); } -static bool SkipReadingLaterAdditions( - ON_BinaryArchive& archive, - unsigned char skip_mark - ) -{ - if ( 0 == skip_mark) - return true; - - if (1 == skip_mark) - { - // TODO ADD THIS // return archive.SkipReadingChunk(); // - return ON_SUBD_RETURN_ERROR(false); - } - - // TODO ADD THIS return archive.SkipReadingBytes( skip_mark ); - return ON_SUBD_RETURN_ERROR(false); -} - - bool ON_SubDVertex::Write( ON_BinaryArchive& archive @@ -576,25 +697,29 @@ bool ON_SubDVertex::Write( // break; //if (!archive.WriteChar((unsigned char)m_vertex_facet_type)) // break; - if (!WriteDouble3(m_P,archive)) + if (!Internal_WriteDouble3(m_P,archive)) break; if (!archive.WriteShort(m_edge_count)) break; if (!archive.WriteShort(m_face_count)) break; - if (!WriteSavedLimitPointList(m_face_count,SavedLimitPointType(),m_limit_point, archive)) + if (!WriteSavedLimitPointList(m_face_count, this->SurfacePointIsSet(), m_limit_point, archive)) break; if (!WriteEdgePtrList(m_edge_count,m_edge_capacity,m_edges,0,nullptr, archive)) break; if (!WriteFacePtrList(m_face_count,m_face_capacity,(const ON_SubDFacePtr*)m_faces,0,nullptr, archive)) break; - // mark end with a 0 byte - // If (when) new stuff is added, the value will be the number of bytes that are added - // or 1 if a chunk is added. - if (!archive.WriteChar((unsigned char)0U)) - break; - return true; + if (archive.Archive3dmVersion() < 70) + { + // mark end with a 0 byte + if (!archive.WriteChar((unsigned char)0U)) + break; + return true; + } + + + return Internal_FinishWritingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); } @@ -621,8 +746,7 @@ bool ON_SubDVertex::Read( unsigned short edge_count = 0; unsigned short face_count = 0; - ON_SubD::SubDType limitP_type = ON_SubD::SubDType::Unset; - ON_SimpleArray limit_points; + ON_SimpleArray limit_points; if (!ReadBase(archive,base)) break; @@ -632,19 +756,19 @@ bool ON_SubDVertex::Read( // break; //if (!archive.ReadChar(&vertex_facet_type)) // break; - if (!ReadDouble3(archive,P)) + if (!Internal_ReadDouble3(archive,P)) break; if (!archive.ReadShort(&edge_count)) break; if (!archive.ReadShort(&face_count)) break; - if (!ReadSavedLimitPointList(archive, face_count, limitP_type, limit_points)) + if (!ReadSavedLimitPointList(archive, face_count, limit_points)) break; ON_SubDVertex* v = subdimple->AllocateVertex( ON_SubD::VertexTagFromUnsigned(vertex_tag), - base.m_level, + base.SubdivisionLevel(), P, edge_count, face_count @@ -666,31 +790,29 @@ bool ON_SubDVertex::Read( break; v->m_face_count = face_count; - - unsigned char skip_mark = 0; - if (!archive.ReadChar(&skip_mark)) - break; - - if (!SkipReadingLaterAdditions(archive,skip_mark)) - break; - - if (ON_SubD::SubDType::Unset != limitP_type) + for (unsigned int i = 0; i < limit_points.UnsignedCount(); i++) { - for (unsigned int i = 0; i < limit_points.UnsignedCount(); i++) + ON_SubDSectorSurfacePoint limit_point = limit_points[i]; + limit_point.m_next_sector_limit_point = (const ON_SubDSectorSurfacePoint*)1U; // skips checks + if (false == v->SetSavedSurfacePoint( true, limit_point)) { - ON_SubDSectorLimitPoint limit_point = limit_points[i]; - limit_point.m_next_sector_limit_point = (const ON_SubDSectorLimitPoint*)1U; // skips checks - if (false == v->SetSavedLimitPoint(limitP_type, limit_point)) - { - v->ClearSavedLimitPoints(); - break; - } + v->ClearSavedSurfacePoints(); + break; } } vertex = v; - return true; + if (archive.Archive3dmVersion() < 70) + { + unsigned char sz = 1; + if (!archive.ReadChar(&sz) || 0 != sz) + break; + return true; + } + + // read additions + return Internal_FinishReadingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); } @@ -717,13 +839,15 @@ bool ON_SubDEdge::Write( if (!WriteFacePtrList(m_face_count,sizeof(m_face2)/sizeof(m_face2[0]),m_face2,m_facex_capacity,m_facex, archive)) break; - // mark end with a 0 byte - // If (when) new stuff is added, the value will be the number of bytes that are added - // or 1 if a chunk is added. - if (!archive.WriteChar((unsigned char)0U)) - break; + if (archive.Archive3dmVersion() < 70) + { + // mark end with a 0 byte + if (!archive.WriteChar((unsigned char)0U)) + break; + return true; + } - return true; + return Internal_FinishWritingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); } @@ -764,10 +888,9 @@ bool ON_SubDEdge::Read( if (!ReadVertexList(archive, vertex_count, 2, v)) break; - ON_SubDEdge* e = subdimple->AllocateEdge( ON_SubD::EdgeTagFromUnsigned(edge_tag), - base.m_level, + base.SubdivisionLevel(), face_count ); @@ -787,17 +910,17 @@ bool ON_SubDEdge::Read( break; e->m_face_count = face_count; - unsigned char skip_mark = 0; - if (!archive.ReadChar(&skip_mark)) - break; - - if (!SkipReadingLaterAdditions(archive,skip_mark)) - break; - edge = e; - return true; + if (archive.Archive3dmVersion() < 70) + { + unsigned char sz; + if (false == archive.ReadChar(&sz) || 0 != sz) + break; + return true; + } + return Internal_FinishReadingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); } @@ -819,13 +942,37 @@ bool ON_SubDFace::Write( if (!WriteEdgePtrList(m_edge_count,sizeof(m_edge4)/sizeof(m_edge4[0]),m_edge4,m_edgex_capacity,m_edgex, archive)) break; - // mark end with a 0 byte - // If (when) new stuff is added, the value will be the number of bytes that are added - // or 1 if a chunk is added. - if (!archive.WriteChar((unsigned char)0U)) - break; + if (archive.Archive3dmVersion() < 70) + { + unsigned char sz = 0; + if (!archive.WriteChar(sz)) + break; + return true; + } - return true; + // write 34 byte texture domain + const bool bWriteTextureDomain = TextureDomainIsSet(); + if (false == Internal_WriteComponentAdditionSize(bWriteTextureDomain, archive, 34)) + break; + if (bWriteTextureDomain) + { + const unsigned char domain_type = static_cast(TextureDomainType()); + if (!archive.WriteChar(domain_type)) + break; + + const unsigned packing_rot = TextureDomainRotationDegrees(); + const unsigned char packing_rot_dex = (unsigned char)(packing_rot/90U); + if (!archive.WriteChar(packing_rot_dex)) + break; + const ON_2dPoint texture_domain_origin = TextureDomainOrigin(); + if (!archive.WriteDouble(2, &texture_domain_origin.x)) + break; + const ON_2dVector texture_domain_delta = TextureDomainDelta(); + if (!archive.WriteDouble(2, &texture_domain_delta.x)) + break; + } + + return Internal_FinishWritingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); } @@ -859,7 +1006,7 @@ bool ON_SubDFace::Read( break; ON_SubDFace* f = subdimple->AllocateFace( - base.m_level, + base.SubdivisionLevel(), edge_count ); @@ -875,23 +1022,48 @@ bool ON_SubDFace::Read( break; f->m_edge_count = edge_count; - unsigned char skip_mark = 0; - if (!archive.ReadChar(&skip_mark)) - break; - - if (!SkipReadingLaterAdditions(archive,skip_mark)) - break; - face = f; - return true; + if (archive.Archive3dmVersion() < 70) + { + unsigned char sz; + if (false == archive.ReadChar(&sz) || 0 != sz) + break; + return true; + } + // read additions + unsigned char sz; + if (false == Internal_ReadComponentAdditionSize(archive, 34, &sz)) + break; + if ( 0 != sz) + { + // 34 bytes of texture domain information + unsigned char domain_type; + if (!archive.ReadChar(&domain_type)) + break; + unsigned char packing_rot_dex = 0; + if (!archive.ReadChar(&packing_rot_dex)) + break; + const unsigned packing_rot = ((unsigned int)packing_rot_dex) * 90U; + ON_2dPoint texture_domain_origin(ON_2dPoint::Origin); + if (!archive.ReadDouble(2, &texture_domain_origin.x)) + break; + ON_2dVector texture_domain_delta(ON_2dVector::ZeroVector); + if (!archive.ReadDouble(2, &texture_domain_delta.x)) + break; + f->SetTextureDomain(ON_SubD::TextureDomainTypeFromUnsigned(domain_type), texture_domain_origin, texture_domain_delta, packing_rot); + } + + return Internal_FinishReadingComponentAdditions(archive); } return ON_SUBD_RETURN_ERROR(false); } unsigned int ON_SubDLevel::SetArchiveId( - unsigned int archive_id_partition[4] + const ON_SubDimple& subdimple, + unsigned int archive_id_partition[4], + bool bLevelLinkedListIncreasingId[3] ) const { unsigned int archive_id = 1; @@ -900,21 +1072,81 @@ unsigned int ON_SubDLevel::SetArchiveId( //archive_id_partition[2] = 0; //archive_id_partition[3] = 0; - archive_id_partition[0] = archive_id; - for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) + const ON_SubDComponentPtr::Type component_type[3] = { + ON_SubDComponentPtr::Type::Vertex, + ON_SubDComponentPtr::Type::Edge, + ON_SubDComponentPtr::Type::Face + }; + + const ON_SubDComponentBaseLink* first_link[3] = { + (const ON_SubDComponentBaseLink*)m_vertex[0], + (const ON_SubDComponentBaseLink*)m_edge[0], + (const ON_SubDComponentBaseLink*)m_face[0] + }; + + + for (unsigned int listdex = 0; listdex < 3; listdex++) { - v->SetArchiveId(archive_id++); - } - archive_id_partition[1] = archive_id; - for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) - { - e->SetArchiveId(archive_id++); - } - archive_id_partition[2] = archive_id; - for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) - { - f->SetArchiveId(archive_id++); + bLevelLinkedListIncreasingId[listdex] = false; + unsigned int prev_id = 0; + archive_id_partition[listdex] = archive_id; + unsigned int linked_list_count = 0; + for (const ON_SubDComponentBaseLink* clink = first_link[listdex]; nullptr != clink; clink = clink->m_next) + { + ++linked_list_count; + if (prev_id < clink->m_id) + { + bLevelLinkedListIncreasingId[listdex] = true; + prev_id = clink->m_id; + clink->SetArchiveId(archive_id++); + continue; + } + + bLevelLinkedListIncreasingId[listdex] = false; + + // m_id values are not increasing in the linked list. + // This happens when the subd is edited and components are deleted + // and then added back later. + + // Finish counting components in the linked list. + for (clink = clink->m_next; nullptr != clink; clink = clink->m_next) + ++linked_list_count; + + // Now iteratate the fixed size pool (which always iterates in increasing id order), + // skip components not on this level, and set archive id of the ones on this level. + unsigned int cidit_level_count = 0; + archive_id = archive_id_partition[listdex]; + ON_SubDComponentIdIterator cidit; + subdimple.InitializeComponentIdIterator(component_type[listdex],cidit); + const unsigned level_index = this->m_level_index; + prev_id = 0; + for (const ON_SubDComponentBase* c = cidit.FirstComponent(); nullptr != c; c = cidit.NextComponent()) + { + if (prev_id >= c->m_id) + { + // This is a serious error! + // Contine because this allows us to save something do the disk in these bad cases. + ON_SUBD_ERROR("The m_id values of the active components in the fixed size pool are corrupt."); + } + else + { + prev_id = c->m_id; + } + if (level_index != c->SubdivisionLevel()) + continue; + ++cidit_level_count; + c->SetArchiveId(archive_id++); + } + if (cidit_level_count != linked_list_count) + { + // This is a serious error! + // Contine because this allows us to save something do the disk in these bad cases. + ON_SUBD_ERROR("The m_level values of the active components in the fixed size pool are corrupt."); + } + break; + } } + archive_id_partition[3] = archive_id; return archive_id-1; @@ -923,21 +1155,17 @@ unsigned int ON_SubDLevel::SetArchiveId( void ON_SubDLevel::ClearArchiveId() const { + // archive ids can be cleared in any order. for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) - { v->SetArchiveId(0); - } for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) - { e->SetArchiveId(0); - } for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) - { f->SetArchiveId(0); - } } bool ON_SubDLevel::Write( + const ON_SubDimple& subdimple, ON_BinaryArchive& archive ) const { @@ -949,21 +1177,26 @@ bool ON_SubDLevel::Write( { if (!archive.WriteShort((unsigned short)m_level_index)) break; - if (!archive.WriteChar((unsigned char)m_subdivision_type)) + + // from early days when there was a possibility of different types of subdivision algorithm + // 4,4,4 means catmull clark quad + if (!archive.WriteChar((unsigned char)4)) break; - if (!archive.WriteChar(m_ordinary_vertex_valence)) + if (!archive.WriteChar((unsigned char)4)) break; - if (!archive.WriteChar(m_ordinary_face_edge_count)) + if (!archive.WriteChar((unsigned char)4)) break; - ON_BoundingBox bbox = m_aggregates.m_bDirtyBoundingBox ? ON_BoundingBox::EmptyBoundingBox : m_aggregates.m_bbox; + + ON_BoundingBox bbox = m_aggregates.m_bDirtyBoundingBox ? ON_BoundingBox::EmptyBoundingBox : m_aggregates.m_controlnet_bbox; if (!archive.WriteDouble(3,bbox[0])) break; if (!archive.WriteDouble(3,bbox[1])) break; - unsigned int archive_id_partition[4] = { 0 }; - SetArchiveId(archive_id_partition); + unsigned int archive_id_partition[4] = {}; + bool bLevelLinkedListIncreasingId[3] = {}; + SetArchiveId(subdimple, archive_id_partition, bLevelLinkedListIncreasingId); if (!archive.WriteInt(4,archive_id_partition)) break; @@ -971,7 +1204,15 @@ bool ON_SubDLevel::Write( const ON_SubDVertex* v = nullptr; const ON_SubDEdge* e = nullptr; const ON_SubDFace* f = nullptr; - for (v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) + + // Have to use idit because subd editing (deleting and then adding) can leave the level's linked lists + // with components in an order that is not increasing in id and it is critical that the next three for + // loops iterate the level's components in order of increasing id. + ON_SubDLevelComponentIdIterator idit; + + // must iterate vertices in order of increasing id + idit.Initialize(bLevelLinkedListIncreasingId[0], ON_SubDComponentPtr::Type::Vertex, subdimple, *this); + for (v = idit.FirstVertex(); nullptr != v; v = idit.NextVertex()) { if( !v->Write(archive) ) break; @@ -979,7 +1220,9 @@ bool ON_SubDLevel::Write( if ( nullptr != v ) break; - for (e = m_edge[0]; nullptr != e; e = e->m_next_edge) + // must iterate edges in order of increasing id + idit.Initialize(bLevelLinkedListIncreasingId[1], ON_SubDComponentPtr::Type::Edge, subdimple, *this); + for (e = idit.FirstEdge(); nullptr != e; e = idit.NextEdge()) { if( !e->Write(archive) ) break; @@ -987,7 +1230,9 @@ bool ON_SubDLevel::Write( if ( nullptr != e ) break; - for (f = m_face[0]; nullptr != f; f = f->m_next_face) + // must iterate faces in order of increasing id + idit.Initialize(bLevelLinkedListIncreasingId[2], ON_SubDComponentPtr::Type::Face, subdimple, *this); + for (f = idit.FirstFace(); nullptr != f; f = idit.NextFace()) { if( !f->Write(archive) ) break; @@ -999,10 +1244,11 @@ bool ON_SubDLevel::Write( unsigned char c = 0; if (archive.Save3dmRenderMesh(ON::object_type::subd_object) || archive.Save3dmAnalysisMesh(ON::object_type::subd_object)) { - if (false == m_limit_mesh.IsEmpty()) + // no reason to save the m_control_net_mesh + if (false == m_surface_mesh.IsEmpty()) { c = 0; - // c = 1; TODO change to c = 1 when ON_SubDLimitMesh::Write()/Read() actually work + // c = 1; TODO change to c = 1 when ON_SubDMesh::Write()/Read() actually work } } @@ -1052,23 +1298,25 @@ bool ON_SubDLevel::Read( if (!archive.ReadShort(&level_index)) break; m_level_index = level_index; - unsigned char c = 0; - if (!archive.ReadChar(&c)) + + // from early days when there was a possibility of different types of subdivision algorithm + unsigned char ignored_c[3] = {}; + if (!archive.ReadChar(&ignored_c[0])) break; - m_subdivision_type = ON_SubD::SubDTypeFromUnsigned(c); - if (!archive.ReadChar(&m_ordinary_vertex_valence)) + if (!archive.ReadChar(&ignored_c[1])) break; - if (!archive.ReadChar(&m_ordinary_face_edge_count)) + if (!archive.ReadChar(&ignored_c[2])) break; - ON_BoundingBox bbox; - if (!archive.ReadDouble(3,bbox[0])) + + ON_BoundingBox controlnet_bbox; + if (!archive.ReadDouble(3, controlnet_bbox[0])) break; - if (!archive.ReadDouble(3,bbox[1])) + if (!archive.ReadDouble(3, controlnet_bbox[1])) break; - if (bbox.IsValid()) + if (controlnet_bbox.IsValid()) { m_aggregates.m_bDirtyBoundingBox = false; - m_aggregates.m_bbox = bbox; + m_aggregates.m_controlnet_bbox = controlnet_bbox; } else { @@ -1139,7 +1387,7 @@ bool ON_SubDLevel::Read( if (0 == minor_version ) break; - c = 0; + unsigned char c = 0; if (!archive.ReadChar(&c)) break; @@ -1169,7 +1417,8 @@ bool ON_SubDimple::Write( { const_cast< ON_SubDHeap* >(&m_heap)->ClearArchiveId(); - if ( !archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0) ) + const int minor_version = (archive.Archive3dmVersion() < 70) ? 0 : 1; + if ( !archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, minor_version) ) return ON_SUBD_RETURN_ERROR(false); bool rc = false; for (;;) @@ -1201,11 +1450,27 @@ bool ON_SubDimple::Write( for (level_index = 0; level_index < level_count; level_index++) { - if ( !m_levels[level_index]->Write(archive) ) + if ( !m_levels[level_index]->Write(*this,archive) ) break; } if (level_index < level_count) break; + + + if (minor_version <= 0) + { + rc = true; + break; + } + + // minor version = 1 addtions + const unsigned char texture_domain_type = static_cast(TextureDomainType()); + if (false == archive.WriteChar(texture_domain_type)) + break; + + if (false == m_texture_mapping_tag.Write(archive)) + break; + rc = true; break; } @@ -1281,6 +1546,17 @@ bool ON_SubDimple::Read( if ( level_index != level_count) break; + if (minor_version >= 1) + { + unsigned char texture_domain_type = static_cast(ON_SubDTextureDomainType::Unset); + if (false == archive.ReadChar(&texture_domain_type)) + break; + m_texture_domain_type = ON_SubD::TextureDomainTypeFromUnsigned(texture_domain_type); + + if (false == m_texture_mapping_tag.Read(archive)) + break; + } + rc = true; break; } @@ -1332,6 +1608,11 @@ bool ON_SubDimple::Read( } } + if (minor_version >= 1) + { + + } + if (rc) return true; return ON_SUBD_RETURN_ERROR(false); @@ -1761,5 +2042,3 @@ bool ON_SubDMeshProxyUserData::WriteToArchive( return false; } - -#endif diff --git a/opennurbs_subd_copy.cpp b/opennurbs_subd_copy.cpp index b86f9bd6..6e6936d1 100644 --- a/opennurbs_subd_copy.cpp +++ b/opennurbs_subd_copy.cpp @@ -26,11 +26,9 @@ //////////////////////////////////////////////////////////////// */ -#if defined(OPENNURBS_SUBD_WIP) - unsigned int ON_SubDArchiveIdMap::ArchiveIdFromComponentPtr(ON__UINT_PTR ptr) { - return (unsigned int)(ptr/(ON_SUBD_ELEMENT_FLAGS_MASK+1)); + return (unsigned int)(ptr/(ON_SUBD_COMPONENT_FLAGS_MASK+1)); } ON_SubDComponentPtr ON_SubDArchiveIdMap::FromVertex( @@ -38,7 +36,7 @@ ON_SubDComponentPtr ON_SubDArchiveIdMap::FromVertex( ) { ON__UINT_PTR archive_id = (nullptr == vertex) ? 0 : vertex->ArchiveId(); - return ON_SubDComponentPtr::Create((const ON_SubDVertex*)(archive_id*(ON_SUBD_ELEMENT_FLAGS_MASK+1))); + return ON_SubDComponentPtr::Create((const ON_SubDVertex*)(archive_id*(ON_SUBD_COMPONENT_FLAGS_MASK+1))); } ON_SubDComponentPtr ON_SubDArchiveIdMap::FromEdge( @@ -46,7 +44,7 @@ ON_SubDComponentPtr ON_SubDArchiveIdMap::FromEdge( ) { ON__UINT_PTR archive_id = (nullptr == edge) ? 0 : edge->ArchiveId(); - return ON_SubDComponentPtr::Create((const ON_SubDEdge*)(archive_id*(ON_SUBD_ELEMENT_FLAGS_MASK+1))); + return ON_SubDComponentPtr::Create((const ON_SubDEdge*)(archive_id*(ON_SUBD_COMPONENT_FLAGS_MASK+1))); } ON_SubDComponentPtr ON_SubDArchiveIdMap::FromFace( @@ -54,7 +52,7 @@ ON_SubDComponentPtr ON_SubDArchiveIdMap::FromFace( ) { ON__UINT_PTR archive_id = (nullptr == face) ? 0 : face->ArchiveId(); - return ON_SubDComponentPtr::Create((const ON_SubDFace*)(archive_id*(ON_SUBD_ELEMENT_FLAGS_MASK+1))); + return ON_SubDComponentPtr::Create((const ON_SubDFace*)(archive_id*(ON_SUBD_COMPONENT_FLAGS_MASK+1))); } ON_SubDComponentPtr ON_SubDArchiveIdMap::FromVertex( @@ -62,7 +60,7 @@ ON_SubDComponentPtr ON_SubDArchiveIdMap::FromVertex( ) { ON_SubDComponentPtr ptr = FromVertex(vertex_ptr.Vertex()); - ptr.m_ptr |= vertex_ptr.VertexPtrMark(); + ptr.m_ptr |= vertex_ptr.VertexDirection(); return ptr; } @@ -122,10 +120,10 @@ ON_SubDVertex* ON_SubDArchiveIdMap::CopyVertex( } // convert vertex->m_limit_point[].m_sector_face pointers to archive_id values - for (const ON_SubDSectorLimitPoint* p = &vertex->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point) + for (const ON_SubDSectorSurfacePoint* p = &vertex->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point) { ptr = ON_SubDArchiveIdMap::FromFace(p->m_sector_face); - const_cast(p)->m_sector_face = (const ON_SubDFace*)ptr.m_ptr; + const_cast(p)->m_sector_face = (const ON_SubDFace*)ptr.m_ptr; } return vertex; @@ -213,7 +211,7 @@ bool ON_SubDArchiveIdMap::ConvertArchiveIdToRuntimeVertexPtr( ON__UINT_PTR vptr = (ON__UINT_PTR)(vertex[i]); vertex[i] = nullptr; const unsigned int archive_id = ON_SubDArchiveIdMap::ArchiveIdFromComponentPtr(vptr); - // future use // ON__UINT_PTR flags = ON_SUBD_ELEMENT_FLAGS(vptr); + // future use // ON__UINT_PTR flags = ON_SUBD_COMPONENT_FLAGS(vptr); if (0 == archive_id || archive_id < m_archive_id_partition[0] || archive_id >= m_archive_id_partition[1]) { ON_ERROR("Invalid vertex archive id."); @@ -264,7 +262,7 @@ bool ON_SubDArchiveIdMap::ConvertArchiveIdToRuntimeEdgePtr( if ( i == edgeN_capacity ) eptr = edgeX; const unsigned int archive_id = ON_SubDArchiveIdMap::ArchiveIdFromComponentPtr(eptr->m_ptr); - ON__UINT_PTR flags = ON_SUBD_ELEMENT_FLAGS(eptr->m_ptr); + ON__UINT_PTR flags = ON_SUBD_COMPONENT_FLAGS(eptr->m_ptr); eptr->m_ptr = 0; if (0 == archive_id || archive_id < m_archive_id_partition[1] || archive_id >= m_archive_id_partition[2]) { @@ -288,7 +286,7 @@ bool ON_SubDArchiveIdMap::ConvertArchiveIdToRuntimeEdgePtr( ON_ERROR("archive_id != edge->ArchiveId()."); continue; } - *eptr = ON_SubDEdgePtr::Create(edge,ON_SUBD_ELEMENT_MARK(flags)); + *eptr = ON_SubDEdgePtr::Create(edge,ON_SUBD_COMPONENT_DIRECTION(flags)); } return true; } @@ -315,7 +313,7 @@ bool ON_SubDArchiveIdMap::ConvertArchiveIdToRuntimeFacePtr( if ( i == faceN_capacity ) fptr = faceX; const unsigned int archive_id = ON_SubDArchiveIdMap::ArchiveIdFromComponentPtr(fptr->m_ptr); - ON__UINT_PTR flags = ON_SUBD_ELEMENT_FLAGS(fptr->m_ptr); + ON__UINT_PTR flags = ON_SUBD_COMPONENT_FLAGS(fptr->m_ptr); fptr->m_ptr = 0; if (0 == archive_id || archive_id < m_archive_id_partition[2] || archive_id >= m_archive_id_partition[3]) { @@ -339,7 +337,7 @@ bool ON_SubDArchiveIdMap::ConvertArchiveIdToRuntimeFacePtr( ON_ERROR("archive_id != face->ArchiveId()."); continue; } - *fptr = ON_SubDFacePtr::Create(face,ON_SUBD_ELEMENT_MARK(flags)); + *fptr = ON_SubDFacePtr::Create(face,ON_SUBD_COMPONENT_DIRECTION(flags)); } return true; } @@ -541,7 +539,7 @@ unsigned int ON_SubDArchiveIdMap::ConvertArchiveIdsToRuntimePointers() // convert ON_SubDVertex.m_faces[] ConvertArchiveIdToRuntimeFacePtr(v->m_face_count,v->m_face_capacity,(ON_SubDFacePtr*)v->m_faces,0,nullptr); - for (const ON_SubDSectorLimitPoint* p = &v->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point) + for (const ON_SubDSectorSurfacePoint* p = &v->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point) { if ( 0 != p->m_sector_face ) ConvertArchiveIdToRuntimeFacePtr(1,1,(ON_SubDFacePtr*)&p->m_sector_face,0,nullptr); @@ -635,10 +633,20 @@ void ON_SubD::CopyHelper(const ON_SubD& src) } m_subdimple_sp = std::shared_ptr(subdimple); if (nullptr != subdimple) - subdimple->SetLimitMeshSubDWeakPointer(m_subdimple_sp); + subdimple->SetManagedMeshSubDWeakPointers(m_subdimple_sp); } -void ON_SubDimple::SetLimitMeshSubDWeakPointer( +ON__UINT64 ON_SubDimple::ContentSerialNumber() const +{ + return m_subd_content_serial_number; +} + +ON__UINT64 ON_SubDimple::ChangeContentSerialNumber() const +{ + return (m_subd_content_serial_number = ON_NextContentSerialNumber()); +} + +void ON_SubDimple::SetManagedMeshSubDWeakPointers( std::shared_ptr& subdimple_sp ) { @@ -649,14 +657,16 @@ void ON_SubDimple::SetLimitMeshSubDWeakPointer( ON_SubDLevel* level = m_levels[level_index]; if (nullptr == level) continue; - ON_SubDLimitMeshImpl* limple = level->m_limit_mesh.SubLimple(); - if (nullptr == limple) - continue; - limple->SetSubDWeakPointer(level->m_face[0],subdimple_sp); + ON_SubDMeshImpl* surface_mesh_imple = level->m_surface_mesh.SubLimple(); + if (nullptr != surface_mesh_imple) + surface_mesh_imple->SetSubDWeakPointer(level->m_face[0], subdimple_sp); + ON_SubDMeshImpl* control_net_mesh_imple = level->m_control_net_mesh.SubLimple(); + if (nullptr != control_net_mesh_imple) + control_net_mesh_imple->SetSubDWeakPointer(level->m_face[0], subdimple_sp); } } -void ON_SubDLimitMeshImpl::SetSubDWeakPointer( +void ON_SubDMeshImpl::SetSubDWeakPointer( const ON_SubDFace* subd_first_face, std::shared_ptr& subdimple_sp ) @@ -709,7 +719,7 @@ ON_SubDimple::ON_SubDimple(const ON_SubDimple& src) ON_SubDLevel* level = SubDLevel(level_index,true); if (nullptr == level) break; - if (false == level->CopyHelper(*src_level, eptrlist, *this, bCopyComponentStatus)) + if (false == level->CopyHelper(src, *src_level, eptrlist, *this, bCopyComponentStatus)) break; if ( src.m_active_level == src_level ) m_active_level = level; @@ -720,6 +730,9 @@ ON_SubDimple::ON_SubDimple(const ON_SubDimple& src) m_max_edge_id = src.m_max_edge_id; if (src.m_max_face_id > m_max_face_id) m_max_face_id = src.m_max_face_id; + + m_subd_appearance = src.m_subd_appearance; + ChangeContentSerialNumber(); } bool ON_SubDLevel::IsEmpty() const @@ -731,10 +744,169 @@ bool ON_SubDLevel::IsEmpty() const ); } +int ON_SubDComponentBaseLink::CompareId(ON_SubDComponentBaseLink const*const* lhs, ON_SubDComponentBaseLink const*const* rhs) +{ + unsigned int lhs_id = (*lhs)->m_id; + unsigned int rhs_id = (*rhs)->m_id; + if (lhs_id < rhs_id) + return -1; + if (lhs_id > rhs_id) + return 1; + return 0; +} + + + + +void ON_SubDLevelComponentIdIterator::Initialize( + bool bLevelLinkedListIncreasingId, + ON_SubDComponentPtr::Type ctype, + const ON_SubDimple& subdimple, + const ON_SubDLevel& level +) +{ + m_bLevelLinkedListIncreasingId = false; + m_ctype = ctype; + m_level_index = (unsigned short)level.m_level_index; + m_count = 0; + m_prev_id = 0; + m_first = nullptr; + m_current = nullptr; + + switch (ctype) + { + case ON_SubDComponentPtr::Type::Vertex: + m_first = (const ON_SubDComponentBaseLink*)level.m_vertex[0]; + break; + case ON_SubDComponentPtr::Type::Edge: + m_first = (const ON_SubDComponentBaseLink*)level.m_edge[0]; + break; + case ON_SubDComponentPtr::Type::Face: + m_first = (const ON_SubDComponentBaseLink*)level.m_face[0]; + break; + default: + m_first = nullptr; + break; + } + + if (nullptr == m_first) + return; + + m_bLevelLinkedListIncreasingId = bLevelLinkedListIncreasingId; + + if (false == m_bLevelLinkedListIncreasingId) + { + subdimple.InitializeComponentIdIterator(ctype, m_cidit); + } +} + +const ON_SubDVertex* ON_SubDLevelComponentIdIterator::FirstVertex() +{ + return (ON_SubDComponentPtr::Type::Vertex == m_ctype) ? (const ON_SubDVertex*)InternalFirst() : nullptr; +} + +const ON_SubDVertex* ON_SubDLevelComponentIdIterator::NextVertex() +{ + return (ON_SubDComponentPtr::Type::Vertex == m_ctype) ? (const ON_SubDVertex*)InternalNext() : nullptr; +} + +const ON_SubDEdge* ON_SubDLevelComponentIdIterator::FirstEdge() +{ + return (ON_SubDComponentPtr::Type::Edge == m_ctype) ? (const ON_SubDEdge*)InternalFirst() : nullptr; +} + +const ON_SubDEdge* ON_SubDLevelComponentIdIterator::NextEdge() +{ + return (ON_SubDComponentPtr::Type::Edge == m_ctype) ? (const ON_SubDEdge*)InternalNext() : nullptr; +} + +const ON_SubDFace* ON_SubDLevelComponentIdIterator::FirstFace() +{ + return (ON_SubDComponentPtr::Type::Face == m_ctype) ? (const ON_SubDFace*)InternalFirst() : nullptr; +} + +const ON_SubDFace* ON_SubDLevelComponentIdIterator::NextFace() +{ + return (ON_SubDComponentPtr::Type::Face == m_ctype) ? (const ON_SubDFace*)InternalNext() : nullptr; +} + +const ON_SubDComponentBase* ON_SubDLevelComponentIdIterator::InternalFirst() +{ + m_prev_id = 0; + if (nullptr == m_first) + return nullptr; + m_current = nullptr; + if (m_bLevelLinkedListIncreasingId) + { + m_current = m_first; + } + else + { + for (const ON_SubDComponentBase* c = m_cidit.FirstComponent(); nullptr != c; c = m_cidit.NextComponent()) + { + if (m_level_index != c->SubdivisionLevel()) + continue; + m_current = (const ON_SubDComponentBaseLink*)c; + break; + } + } + if (nullptr != m_current) + { + if (m_current->m_id <= m_prev_id) + { + // When this happens, searching by component id will be broken in the destination of the SubD copy. + // It is a very serious bug, but we continue so something will get saved to the archive or be created in the copy. + ON_SUBD_ERROR("Iterator is not in order of increasing id."); + } + else + m_prev_id = m_current->m_id; + m_count = 1; + } + return m_current; +} + + +const ON_SubDComponentBase* ON_SubDLevelComponentIdIterator::InternalNext() +{ + if (nullptr == m_first || nullptr == m_current) + return nullptr; + if (m_bLevelLinkedListIncreasingId) + { + m_current = m_current->m_next; + } + else + { + m_current = nullptr; + for (const ON_SubDComponentBase* c = m_cidit.NextComponent(); nullptr != c; c = m_cidit.NextComponent()) + { + if (m_level_index != c->SubdivisionLevel()) + continue; + m_current = (const ON_SubDComponentBaseLink*)c; + break; + } + } + if (nullptr != m_current) + { + if (m_current->m_id <= m_prev_id) + { + // When this happens, searching by component id will be broken in the destination of the SubD copy. + // It is a very serious bug, but we continue so something will get saved to the archive or be created in the copy. + ON_SUBD_ERROR("Iterator is not in order of increasing id."); + } + else + m_prev_id = m_current->m_id; + ++m_count; + } + return m_current; +} + + + bool ON_SubDLevel::CopyHelper( - const ON_SubDLevel& src, + const class ON_SubDimple& src_subdimple, + const ON_SubDLevel& src_level, class ON_SubDArchiveIdMap& eptrlist, - class ON_SubDimple& subdimple, + class ON_SubDimple& dest_subdimple, bool bCopyComponentStatus ) { @@ -742,22 +914,31 @@ bool ON_SubDLevel::CopyHelper( eptrlist.Reset(); - m_limit_mesh.Clear(); + m_surface_mesh.Clear(); + m_control_net_mesh.Clear(); for (;;) { - if ( 0 == src.SetArchiveId(eptrlist.m_archive_id_partition) ) + bool bLevelLinkedListIncreasingId[3] = {}; + if ( 0 == src_level.SetArchiveId(src_subdimple, eptrlist.m_archive_id_partition, bLevelLinkedListIncreasingId) ) break; unsigned int archive_id = 1; if ( archive_id != eptrlist.m_archive_id_partition[0]) break; - for (const ON_SubDVertex* source_vertex = src.m_vertex[0]; nullptr != source_vertex; source_vertex = source_vertex->m_next_vertex, archive_id++) + // Have to use idit because subd editing (deleting and then adding) can leave the level's linked lists + // with components in an order that is not increasing in id and it is critical that the next three for + // loops iterate the level's components in order of increasing id. + ON_SubDLevelComponentIdIterator src_idit; + + // must iterate source vertices in order of increasing id + src_idit.Initialize(bLevelLinkedListIncreasingId[0], ON_SubDComponentPtr::Type::Vertex, src_subdimple, src_level); + for (const ON_SubDVertex* source_vertex = src_idit.FirstVertex(); nullptr != source_vertex; source_vertex = src_idit.NextVertex(), archive_id++) { if (archive_id != source_vertex->ArchiveId()) break; - ON_SubDVertex* vertex = eptrlist.AddCopy(source_vertex,subdimple); + ON_SubDVertex* vertex = eptrlist.AddCopy(source_vertex,dest_subdimple); if (nullptr == vertex ) break; if (archive_id != vertex->ArchiveId()) @@ -769,11 +950,13 @@ bool ON_SubDLevel::CopyHelper( if ( archive_id != eptrlist.m_archive_id_partition[1]) break; - for (const ON_SubDEdge* source_edge = src.m_edge[0]; nullptr != source_edge; source_edge = source_edge->m_next_edge, archive_id++) + // must iterate source edges in order of increasing id + src_idit.Initialize(bLevelLinkedListIncreasingId[1], ON_SubDComponentPtr::Type::Edge, src_subdimple, src_level); + for (const ON_SubDEdge* source_edge = src_idit.FirstEdge(); nullptr != source_edge; source_edge = src_idit.NextEdge(), archive_id++) { if (archive_id != source_edge->ArchiveId()) break; - ON_SubDEdge* edge = eptrlist.AddCopy(source_edge,subdimple); + ON_SubDEdge* edge = eptrlist.AddCopy(source_edge,dest_subdimple); if (nullptr == edge ) break; if (archive_id != edge->ArchiveId()) @@ -785,11 +968,13 @@ bool ON_SubDLevel::CopyHelper( if ( archive_id != eptrlist.m_archive_id_partition[2]) break; - for (const ON_SubDFace* source_face = src.m_face[0]; nullptr != source_face; source_face = source_face->m_next_face, archive_id++) + // must iterate source faces in order of increasing id + src_idit.Initialize(bLevelLinkedListIncreasingId[2], ON_SubDComponentPtr::Type::Face, src_subdimple, src_level); + for (const ON_SubDFace* source_face = src_idit.FirstFace(); nullptr != source_face; source_face = src_idit.NextFace(), archive_id++) { if (archive_id != source_face->ArchiveId()) break; - ON_SubDFace* face = eptrlist.AddCopy(source_face,subdimple); + ON_SubDFace* face = eptrlist.AddCopy(source_face,dest_subdimple); if (nullptr == face ) break; if (archive_id != face->ArchiveId()) @@ -804,42 +989,44 @@ bool ON_SubDLevel::CopyHelper( if (0 == eptrlist.ConvertArchiveIdsToRuntimePointers()) break; - if (false == src.m_limit_mesh.IsEmpty()) + for (int meshdex = 0; meshdex < 2; meshdex++) { - ON_SubDLimitMesh local_limit_mesh; - local_limit_mesh.CopyFrom(src.m_limit_mesh); - for (const ON_SubDLimitMeshFragment* fragment = local_limit_mesh.FirstFragment(); nullptr != fragment; fragment = fragment->m_next_fragment) + ON_SubDMesh* src_mesh = (0==meshdex) ? (&src_level.m_surface_mesh) : (&src_level.m_control_net_mesh); + ON_SubDMesh* this_mesh = (0 == meshdex) ? (&m_surface_mesh) : (&m_control_net_mesh); + if (false == src_mesh->IsEmpty()) { - if (nullptr != fragment->m_face) + ON_SubDMesh local_subd_mesh; + local_subd_mesh.CopyFrom(*src_mesh); + for (const ON_SubDMeshFragment* fragment = local_subd_mesh.FirstFragment(); nullptr != fragment; fragment = fragment->m_next_fragment) { - archive_id = fragment->m_face->ArchiveId(); - ON_SubDComponentPtr cptr = ON_SubDArchiveIdMap::FromFace(fragment->m_face); - ON_SubDFacePtr fptr = cptr.FacePtr(); - const_cast< ON_SubDLimitMeshFragment* >(fragment)->m_face = nullptr; - if (0 != archive_id) + if (nullptr != fragment->m_face) { - if (eptrlist.ConvertArchiveIdToRuntimeFacePtr(1, 1, &fptr, 0, nullptr)) + archive_id = fragment->m_face->ArchiveId(); + ON_SubDComponentPtr cptr = ON_SubDArchiveIdMap::FromFace(fragment->m_face); + ON_SubDFacePtr fptr = cptr.FacePtr(); + const_cast(fragment)->m_face = nullptr; + if (0 != archive_id) { - const_cast< ON_SubDLimitMeshFragment* >(fragment)->m_face = fptr.Face(); - if ( nullptr != fragment->m_face) - continue; + if (eptrlist.ConvertArchiveIdToRuntimeFacePtr(1, 1, &fptr, 0, nullptr)) + { + const_cast(fragment)->m_face = fptr.Face(); + if (nullptr != fragment->m_face) + continue; + } } } + local_subd_mesh = ON_SubDMesh::Empty; + break; } - local_limit_mesh = ON_SubDLimitMesh::Empty; - break; + if (false == local_subd_mesh.IsEmpty()) + ON_SubDMesh::Swap(*this_mesh, local_subd_mesh); } - if (false == local_limit_mesh.IsEmpty()) - ON_SubDLimitMesh::Swap(m_limit_mesh,local_limit_mesh); } this->ClearArchiveId(); - this->m_level_index = src.m_level_index; - this->m_subdivision_type = src.m_subdivision_type; - this->m_ordinary_vertex_valence = src.m_ordinary_vertex_valence; - this->m_ordinary_face_edge_count = src.m_ordinary_face_edge_count; - this->m_aggregates = src.m_aggregates; + this->m_level_index = src_level.m_level_index; + this->m_aggregates = src_level.m_aggregates; this->m_aggregates.MarkAllAsNotCurrent(); rc = true; @@ -847,12 +1034,10 @@ bool ON_SubDLevel::CopyHelper( } eptrlist.Reset(); - src.ClearArchiveId(); + src_level.ClearArchiveId(); if ( false == rc ) return ON_SUBD_RETURN_ERROR(false); return rc; } - -#endif diff --git a/opennurbs_subd_data.cpp b/opennurbs_subd_data.cpp index a941243e..333483e6 100644 --- a/opennurbs_subd_data.cpp +++ b/opennurbs_subd_data.cpp @@ -55,6 +55,9 @@ void ON_SubDimple::ClearLevelContents( if (nullptr == level) return; + if (level == m_active_level) + ChangeContentSerialNumber(); + level->ResetFaceArray(); level->ResetEdgeArray(); level->ResetVertexArray(); @@ -101,7 +104,10 @@ void ON_SubDimple::ClearHigherSubdivisionLevels( if (nullptr != m_active_level && m_active_level->m_level_index > max_level_index) { if ( level_count > max_level_index ) - m_active_level = m_levels[max_level_index]; + { + m_active_level = m_levels[max_level_index]; + ChangeContentSerialNumber(); + } } while (level_count > max_level_index+1) @@ -139,6 +145,7 @@ void ON_SubDimple::ClearLowerSubdivisionLevels( if (nullptr != m_active_level && m_active_level->m_level_index < min_level_index) { m_active_level = m_levels[min_level_index]; + ChangeContentSerialNumber(); } for ( unsigned int level_index = 0; level_index < min_level_index; level_index++) @@ -162,17 +169,17 @@ void ON_SubDimple::ClearLowerSubdivisionLevels( for (ON_SubDVertex* vertex = level->m_vertex[0]; nullptr != vertex; vertex = const_cast(vertex->m_next_vertex)) { - vertex->m_level = new_level_index; + vertex->SetSubdivisionLevel(new_level_index); } for (ON_SubDEdge* edge = level->m_edge[0]; nullptr != edge; edge = const_cast(edge->m_next_edge)) { - edge->m_level = new_level_index; + edge->SetSubdivisionLevel(new_level_index); } for (ON_SubDFace* face = level->m_face[0]; nullptr != face; face = const_cast(face->m_next_face)) { - face->m_level = new_level_index; + face->SetSubdivisionLevel(new_level_index); face->m_parent_face_id = 0; face->m_zero_face_id = face->m_id; } @@ -196,6 +203,7 @@ void ON_SubDimple::Destroy() } m_levels.Destroy(); m_heap.Destroy(); + m_subd_content_serial_number = 0; } ON_SubDLevel* ON_SubDimple::ActiveLevel(bool bCreateIfNeeded) @@ -204,6 +212,7 @@ ON_SubDLevel* ON_SubDimple::ActiveLevel(bool bCreateIfNeeded) { unsigned int level_index = (m_levels.UnsignedCount() > 0) ? (m_levels.UnsignedCount()-1) : 0U; m_active_level = SubDLevel(level_index,bCreateIfNeeded && 0 == m_levels.UnsignedCount()); + ChangeContentSerialNumber(); } return m_active_level; } @@ -221,8 +230,11 @@ class ON_SubDLevel* ON_SubDimple::SubDLevel( level = new ON_SubDLevel(); level->m_level_index = level_index; m_levels.Append(level); - if ( nullptr == m_active_level ) + if (nullptr == m_active_level) + { m_active_level = level; + ChangeContentSerialNumber(); + } } return level; @@ -268,15 +280,148 @@ void ON_SubDAggregates::UpdateBoundingBox( } } } - m_bbox = bbox; + m_controlnet_bbox = bbox; m_bDirtyBoundingBox = false; } -ON_BoundingBox ON_SubDLevel::BoundingBox() const +ON_BoundingBox ON_SubDLevel::ControlNetBoundingBox() const { if ( m_aggregates.m_bDirtyBoundingBox ) m_aggregates.UpdateBoundingBox(this); - return m_aggregates.m_bbox; + return m_aggregates.m_controlnet_bbox; +} + +void ON_SubDAggregates::UpdateTopologicalAttributes( + const ON_SubDLevel* level +) +{ + m_topological_attributes = 0; + if (nullptr == level) + return; + + if (m_bDirtyBoundingBox) + { + UpdateBoundingBox(level); + if (m_bDirtyBoundingBox) + return; + } + + bool bIsManifold = level->m_edge_count >= 3 && level->m_face_count >= 1; + bool bIsOriented = bIsManifold; + bool bHasBoundary = false; + + for (const ON_SubDEdge* e = level->m_edge[0]; nullptr != e; e = e->m_next_edge) + { + if (1 == e->m_face_count) + { + bHasBoundary = true; + if (false == bIsManifold && false == bIsOriented) + break; + } + else if (2 == e->m_face_count) + { + if (ON_SUBD_FACE_DIRECTION(e->m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(e->m_face2[1].m_ptr)) + { + bIsOriented = false; + if (bHasBoundary && false == bIsManifold) + break; + } + } + else + { + bIsManifold = false; + bIsOriented = false; + if (bHasBoundary) + break; + } + } + + double vol = 0.0; + if (bIsManifold && bIsOriented && false == bHasBoundary) + { + const ON_3dVector B(m_controlnet_bbox.IsValid() ? ON_3dVector(m_controlnet_bbox.Center()) : ON_3dVector::ZeroVector); + ON_3dVector P, Q, R; + for (const ON_SubDFace* f = level->m_face[0]; nullptr != f && vol == vol; f = f->m_next_face) + { + if (false == f->GetSubdivisionPoint( &P.x)) + { + vol = ON_DBL_QNAN; + break; + } + P -= B; + const unsigned count = f->EdgeCount(); + if (count < 3) + { + vol = ON_DBL_QNAN; + break; + } + const ON_SubDVertex* v = f->Vertex(count - 1); + if (nullptr == v || false == v->GetSubdivisionPoint( &R.x)) + { + vol = ON_DBL_QNAN; + break; + } + R -= B; + for (unsigned fvi = 0; fvi < count; fvi++) + { + Q = R; + v = f->Vertex(fvi); + if (nullptr == v || false == v->GetSubdivisionPoint( &R.x)) + { + vol = ON_DBL_QNAN; + break; + } + R -= B; + // ON_TripleProduct(P, Q, R) = 6x signed volume of tetrahedron with trangle base (P,Q,R) and apex B. + vol += ON_TripleProduct(P, Q, R); + } + } + } + + // bit 1 indicates m_topological_attributes is set. + m_topological_attributes = 1; + + if (bIsManifold) + m_topological_attributes |= 2; + if (bIsOriented) + m_topological_attributes |= 4; + if (bHasBoundary) + m_topological_attributes |= 8; + + if (vol > 0.0) + m_topological_attributes |= 16; + else if (vol < 0.0) + m_topological_attributes |= 32; +} + +bool ON_SubDAggregates::GetTopologicalAttributes(bool & bIsManifold, bool & bIsOriented, bool & bHasBoundary, int & solid_orientation) const +{ + // if m_bDirtyBoundingBox is true, then m_topological_attributes is dirty as well. + const unsigned int topological_attributes = m_bDirtyBoundingBox ? 0U : m_topological_attributes; + + bIsManifold = 0 != (2 & topological_attributes); + bIsOriented = 0 != (4 & topological_attributes); + bHasBoundary = 0 != (8 & topological_attributes); + if (bIsManifold && bIsOriented && false == bHasBoundary) + { + if (0 != (16 & topological_attributes)) + solid_orientation = 1; + else if (0 != (32 & topological_attributes)) + solid_orientation = -1; + else + solid_orientation = 2; + } + else + solid_orientation = 0; + + return (0 != topological_attributes); +} + +bool ON_SubDAggregates::GetTopologicalAttributes(const ON_SubDLevel * level, bool &bIsManifold, bool & bIsOriented, bool & bHasBoundary, int & solid_orientation) +{ + if ( (m_bDirtyBoundingBox || 0 == m_topological_attributes) && nullptr != level) + UpdateTopologicalAttributes(level); + return GetTopologicalAttributes(bIsManifold, bIsOriented, bHasBoundary, solid_orientation); } ON_AggregateComponentStatus ON_SubDLevel::AggregateComponentStatus() const @@ -286,28 +431,28 @@ ON_AggregateComponentStatus ON_SubDLevel::AggregateComponentStatus() const return m_aggregates.m_aggregate_status; } -void ON_SubDAggregates::UpdateEdgeFlags( +void ON_SubDAggregates::UpdateAggregateEdgeAttributes( const ON_SubDLevel* level ) { if (nullptr != level) { - unsigned int edge_flags = 0; + unsigned int bits = 0; for (const ON_SubDEdge* e = level->m_edge[0]; nullptr != e; e = e->m_next_edge) - edge_flags |= e->EdgeFlags(); - m_edge_flags = edge_flags; + bits |= e->EdgeAttributes(); + m_aggregate_edge_attributes = bits; } - m_bDirtyEdgeFlags = 0; + m_bDirtyEdgeAttributes = false; } unsigned int ON_SubDLevel::EdgeFlags() const { - if (m_aggregates.m_bDirtyEdgeFlags) - m_aggregates.UpdateEdgeFlags(this); - return m_aggregates.m_edge_flags; + if (m_aggregates.m_bDirtyEdgeAttributes) + m_aggregates.UpdateAggregateEdgeAttributes(this); + return m_aggregates.m_aggregate_edge_attributes; } -unsigned int ON_SubD::EdgeFlags() const +unsigned int ON_SubD::AggregateEdgeAttributes() const { return ActiveLevel().EdgeFlags(); } @@ -428,7 +573,7 @@ static void TransformVector( V[2] = z; } -bool ON_SubDSectorLimitPoint::Transform( +bool ON_SubDSectorSurfacePoint::Transform( const ON_Xform& xform ) { @@ -450,44 +595,46 @@ bool ON_SubDVertex::Transform( ) { TransformPoint(&xform.m_xform[0][0],m_P); - if (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) - TransformVector(&xform.m_xform[0][0],m_displacement_V); - if (ON_SubD::SubDType::Unset != SavedSubdivisionPointType()) + Internal_TransformComponentBase(bTransformationSavedSubdivisionPoint, xform); + + // TODO: + // If the vertex + // is tagged as ON_SubD::VertexTag::Corner + // and bTransformationSavedSubdivisionPoint is true, + // and the corner sector(s) contains interior smooth edges, + // and the transformation changes the angle between a corner sector's crease boundary, + // then the sector's interior smooth edge's m_sector_coefficient[] could change + // and invalidate the subdivison points and limit points. + // This is only possible for uncommon (in practice) transformations + // and corner sectors and will require a fair bit of testing for + // now it's easier to simply set bTransformationSavedSubdivisionPoint to false + // at a higher level when these types of transformations are encountered. + if ( bTransformationSavedSubdivisionPoint && Internal_SurfacePointFlag() ) { - if (bTransformationSavedSubdivisionPoint) - TransformPoint(&xform.m_xform[0][0], m_saved_subd_point1); - else - ClearSavedSubdivisionPoint(); - } - if (ON_SubD::SubDType::Unset != this->SavedLimitPointType()) - { - if (bTransformationSavedSubdivisionPoint) - { - for (const ON_SubDSectorLimitPoint* lp = &m_limit_point; nullptr != lp; lp = lp->m_next_sector_limit_point) - const_cast< ON_SubDSectorLimitPoint* >(lp)->Transform(xform); - } - else - ClearSavedLimitPoints(); + for (const ON_SubDSectorSurfacePoint* lp = &m_limit_point; nullptr != lp; lp = lp->m_next_sector_limit_point) + const_cast(lp)->Transform(xform); } + else + Internal_ClearSurfacePointFlag(); + return true; } -bool ON_SubDVertex::SetLocation( - ON_3dPoint location, +bool ON_SubDVertex::SetControlNetPoint( + ON_3dPoint control_net_point, bool bClearNeighborhoodCache ) { - if (false == location.IsValid()) + if (false == control_net_point.IsValid()) return false; - if (!(m_P[0] == location.x && m_P[1] == location.y && m_P[2] == location.z)) + if (!(m_P[0] == control_net_point.x && m_P[1] == control_net_point.y && m_P[2] == control_net_point.z)) { - m_P[0] = location.x; - m_P[1] = location.y; - m_P[2] = location.z; - ClearSavedSubdivisionPoint(); - ClearSavedLimitPoints(); + m_P[0] = control_net_point.x; + m_P[1] = control_net_point.y; + m_P[2] = control_net_point.z; + ClearSavedSubdivisionPoints(); if (bClearNeighborhoodCache) { @@ -496,7 +643,7 @@ bool ON_SubDVertex::SetLocation( ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); if (nullptr == edge) continue; - edge->ClearSavedSubdivisionPoint(); + edge->ClearSavedSubdivisionPoints(); ON_SubDFacePtr* fptr = edge->m_face2; for (unsigned short efi = 0; efi < edge->m_face_count; efi++, fptr++) { @@ -509,7 +656,7 @@ bool ON_SubDVertex::SetLocation( ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr->m_ptr); if (nullptr == face) continue; - face->ClearSavedSubdivisionPoint(); + face->ClearSavedSubdivisionPoints(); ON_SubDEdgePtr* eptr = face->m_edge4; for (unsigned short fei = 0; fei < face->m_edge_count; fei++, eptr++) @@ -526,8 +673,7 @@ bool ON_SubDVertex::SetLocation( ON_SubDVertex* fvertex = const_cast(fedge->m_vertex[ON_SUBD_EDGE_DIRECTION(eptr->m_ptr)]); if (nullptr == fvertex) continue; - fvertex->ClearSavedSubdivisionPoint(); - fvertex->ClearSavedLimitPoints(); + fvertex->ClearSavedSubdivisionPoints(); } } } @@ -537,7 +683,7 @@ bool ON_SubDVertex::SetLocation( return true; } -bool ON_SubDEdge::Transform( +void ON_SubDComponentBase::Internal_TransformComponentBase( bool bTransformationSavedSubdivisionPoint, const class ON_Xform& xform ) @@ -545,13 +691,24 @@ bool ON_SubDEdge::Transform( if (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) TransformVector(&xform.m_xform[0][0],m_displacement_V); - if (ON_SubD::SubDType::Unset != SavedSubdivisionPointType()) + if ( SavedSubdivisionPointIsSet() ) { if (bTransformationSavedSubdivisionPoint) TransformPoint(&xform.m_xform[0][0], m_saved_subd_point1); else - ClearSavedSubdivisionPoint(); + ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags); } +} + +bool ON_SubDEdge::Transform( + bool bTransformationSavedSubdivisionPoint, + const class ON_Xform& xform + ) +{ + Internal_TransformComponentBase(bTransformationSavedSubdivisionPoint, xform); + + Internal_ClearSurfacePointFlag(); + return true; } @@ -560,16 +717,16 @@ bool ON_SubDFace::Transform( const class ON_Xform& xform ) { - if (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags)) - TransformVector(&xform.m_xform[0][0],m_displacement_V); + Internal_TransformComponentBase(bTransformationSavedSubdivisionPoint, xform); - if (ON_SubD::SubDType::Unset != SavedSubdivisionPointType()) + if (bTransformationSavedSubdivisionPoint && Internal_SurfacePointFlag() ) { - if (bTransformationSavedSubdivisionPoint) - TransformPoint(&xform.m_xform[0][0], m_saved_subd_point1); - else - ClearSavedSubdivisionPoint(); + for (ON_SubDMeshFragment* f = m_mesh_fragments; nullptr != f; f = f->m_next_fragment) + f->Transform(xform); } + else + Internal_ClearSurfacePointFlag(); + return true; } @@ -582,22 +739,28 @@ bool ON_SubDLevel::Transform( m_aggregates.m_bDirtyBoundingBox = true; - for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex && rc; vertex = vertex->m_next_vertex) + for (const ON_SubDVertex* vertex = m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex) { - rc = const_cast(vertex)->Transform(bTransformationSavedSubdivisionPoint,xform); + if (false == const_cast(vertex)->Transform(bTransformationSavedSubdivisionPoint, xform)) + rc = false; } - for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge && rc; edge = edge->m_next_edge) + for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge) { - rc = const_cast(edge)->Transform(bTransformationSavedSubdivisionPoint,xform); + if (false == const_cast(edge)->Transform(bTransformationSavedSubdivisionPoint, xform)) + rc = false; } - for (const ON_SubDFace* face = m_face[0]; nullptr != face && rc; face = face->m_next_face) + for (const ON_SubDFace* face = m_face[0]; nullptr != face; face = face->m_next_face) { - rc = const_cast(face)->Transform(bTransformationSavedSubdivisionPoint,xform); + if (false == const_cast(face)->Transform(bTransformationSavedSubdivisionPoint, xform)) + rc = false; } - if (false == m_limit_mesh.Transform(xform)) + if (false == m_surface_mesh.Transform(xform)) + rc = false; + + if (false == m_control_net_mesh.Transform(xform)) rc = false; if (rc) @@ -607,7 +770,7 @@ bool ON_SubDLevel::Transform( } -bool ON_SubDLimitMesh::Transform( +bool ON_SubDMesh::Transform( const ON_Xform& xform ) { @@ -617,7 +780,7 @@ bool ON_SubDLimitMesh::Transform( return true; if (xform.IsZero()) return false; - ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + ON_SubDMeshImpl* impl = m_impl_sp.get(); if ( nullptr == impl ) return true; // transform applied to empty mesh is true on purpose. Changing to false will break other code. return impl->Transform(xform); @@ -673,40 +836,71 @@ bool ON_SubDimple::Transform( } -bool ON_SubDLimitMeshFragment::Transform( +bool ON_SubDMeshFragment::Transform( const ON_Xform& xform ) { - if (0 == m_P_count) + const unsigned count = PointCount(); + if (0 == count) { - m_bbox = ON_BoundingBox::EmptyBoundingBox; + m_surface_bbox = ON_BoundingBox::EmptyBoundingBox; return true; } - if ( false == ON_TransformPointList(3,false,m_P_count,(int)m_P_stride,m_P,xform) ) + if ( false == ON_TransformPointList(3,false, count,(int)m_P_stride,m_P,xform) ) return ON_SUBD_RETURN_ERROR(false); - if ( false == ON_TransformVectorList(3,m_P_count,(int)m_P_stride,m_N,xform) ) - return ON_SUBD_RETURN_ERROR(false); - ON_GetPointListBoundingBox(3,0,m_P_count,(int)m_P_stride,m_P,&m_bbox.m_min.x,&m_bbox.m_max.x,false); + if (count == NormalCount()) + { + if (false == ON_TransformVectorList(3, count, (int)m_N_stride, m_N, xform)) + return ON_SUBD_RETURN_ERROR(false); + } + if (0 != (ON_SubDMeshFragment::EtcControlNetQuadBit & m_vertex_count_etc)) + { + for (int i = 0; i < 4; i++) + { + const ON_3dPoint A(m_ctrlnetP[i]); + if (A.IsValid()) + { + const ON_3dPoint B = xform * A; + m_ctrlnetP[i][0] = B.x; + m_ctrlnetP[i][1] = B.y; + m_ctrlnetP[i][2] = B.z; + } + } + } + if (0 != (ON_SubDMeshFragment::EtcControlNetQuadBit & m_vertex_capacity_etc)) + { + const ON_3dVector A(m_ctrlnetN); + if (A.IsNotZero()) + { + ON_3dVector B = xform * A; + if ( A.IsUnitVector() && false == B.IsUnitVector() ) + B = B.UnitVector(); + m_ctrlnetN[0] = B.x; + m_ctrlnetN[1] = B.y; + m_ctrlnetN[2] = B.z; + } + } + ON_GetPointListBoundingBox(3,0,count,(int)m_P_stride,m_P,&m_surface_bbox.m_min.x,&m_surface_bbox.m_max.x,false); return true; } -bool ON_SubDLimitMeshImpl::Transform( +bool ON_SubDMeshImpl::Transform( const ON_Xform& xform ) { m_bbox = ON_BoundingBox::EmptyBoundingBox; ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox; - for ( const ON_SubDLimitMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) + for ( const ON_SubDMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) { - if ( false == const_cast(fragment)->Transform(xform) ) + if ( false == const_cast(fragment)->Transform(xform) ) return ON_SUBD_RETURN_ERROR(false); if ( fragment == m_first_fragment ) - bbox = fragment->m_bbox; + bbox = fragment->m_surface_bbox; else - bbox.Union(fragment->m_bbox); + bbox.Union(fragment->m_surface_bbox); } m_bbox = bbox; - m_limit_mesh_content_serial_number = ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber(); + ChangeContentSerialNumber(); return true; } @@ -726,7 +920,7 @@ ON_BoundingBox ON_SubDVertex::ControlNetBoundingBox() const -ON_BoundingBox ON_SubDEdge::ControlNetBoundingBox() const +const ON_BoundingBox ON_SubDEdge::ControlNetBoundingBox() const { ON_BoundingBox bbox; if (nullptr != m_vertex[0] && nullptr != m_vertex[1]) @@ -739,7 +933,7 @@ ON_BoundingBox ON_SubDEdge::ControlNetBoundingBox() const return bbox; } -ON_BoundingBox ON_SubDFace::ControlNetBoundingBox() const +const ON_BoundingBox ON_SubDFace::ControlNetBoundingBox() const { ON_BoundingBox bbox; ON_3dPoint P[16]; diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h index 6dfd815f..aa066e55 100644 --- a/opennurbs_subd_data.h +++ b/opennurbs_subd_data.h @@ -46,8 +46,6 @@ #if !defined(OPENNURBS_SUBD_DATA_INC_) #define OPENNURBS_SUBD_DATA_INC_ -#if defined(OPENNURBS_SUBD_WIP) - bool ON_SubDFaceRegionBreakpoint( unsigned int level0_face_id, const class ON_SubDComponentRegionIndex& region_index @@ -71,25 +69,19 @@ public: // initial face const ON_SubDFace* m_face0 = nullptr; - // subdivision algorithm - ON_SubD::SubDType m_subd_type = ON_SubD::SubDType::Unset; + unsigned char m_reserved1 = 0; - // When the subd_type is ON_SubD::SubDType::QuadCatmullClark, - // the center vertex will be a smooth vertex with valence = m_face0->m_edge_count. + // The center vertex will be a smooth vertex with valence = m_face0->m_edge_count. // The edges and faces are sorted radially and all faces are quads. // - // When the subd_type is ON_SubD::SubDType::TriLoopWarren and m_face0 is not a triangle, - // m_center_vertex is a smooth vertex with valence = m_face0->m_edge_count. - // The edges and faces are sorted radially and all faces are tris. - // // In all other cases, m_center_vertex is null. const ON_SubDVertex* m_center_vertex1 = nullptr; // m_face1[] is a list of the subdivision faces that subdivide the - // original face. When the subd_type is ON_SubD::SubDType::TriLoopWarren - // and the initial face is a triangle, the first face is the interior - // subdivision triangle. In all othr cases, m_face1 is identical - // to m_center_vertex1->m_faces[]. The m_next_face pointers are set so + // original face. + + // m_face1 is identical to m_center_vertex1->m_faces[]. + // The m_next_face pointers are set so // that m_face1[i]->m_next_face = m_face1[i+1]. Note that // m_face1[m_face1_count-1]->m_next_face may not be null. unsigned int m_face1_count = 0; @@ -109,21 +101,15 @@ public: number of edges. */ bool Subdivide( - ON_SubD::SubDType subd_type, const ON_SubDFace* face ); private: - bool TriSubdivideHelper( - const ON_SubDFace* face - ); - bool QuadSubdivideHelper( const ON_SubDFace* face ); bool ReserveCapacity( - ON_SubD::SubDType subd_type, const ON_SubDFace* face ); @@ -179,7 +165,7 @@ public: // and there is a single exceptional crease vertex, // then m_center_edge_limit_point[n] = limit point of the middle of m_center_edges[n] // This information is needed to get NURBS patches near exceptional crease vertices. - ON_SubDSectorLimitPoint m_center_edge_limit_point[4]; + ON_SubDSectorSurfacePoint m_center_edge_limit_point[4]; public: const ON_SubDVertex* m_vertex_grid[4][4] = {}; // vertex net m_quad_face corners = ([1][1], [2][1], [2][2], [1][2]) @@ -432,73 +418,73 @@ public: // the first place something goes wrong in a complex calculation. // void ON_SubDIncrementErrorCount(); // defined in opennurbs_subd.cpp -#define ON_SUBD_RETURN_ERROR(rc) (ON_SubDIncrementErrorCount(),rc) #define ON_SUBD_ERROR(msg) (ON_SubDIncrementErrorCount(),ON_ERROR(msg)) +#define ON_SUBD_RETURN_ERROR(rc) (ON_SubDIncrementErrorCount(),rc) +#define ON_SUBD_RETURN_ERROR_MSG(msg,rc) (ON_SubDIncrementErrorCount(),ON_ERROR(msg),rc) ////////////////////////////////////////////////////////////////////////// // -// ON_SubDEdgePtr and ON_SubDFacePtr are unsigned ints that store a -// pointer to an ON_SubDEdge or ON_SubDFace along with a bit that -// is 0 or 1 which is used to indicate the end of the edge that is -// the "start" or if an edge is "reversed" along the side of a face. +// ON_SubDVertexPtr, ON_SubDEdgePtr, and ON_SubDFacePtr are unsigned ints +// that store a pointer to an ON_SubDVertex, ON_SubDEdge, or ON_SubDFace +// along with a direction bit that is 0 or 1. The direction bit is used +// to indicate if the component is being referenced with its +// natrual orientation (0) or the reverse of its natural orientaion (1). // -// This code assumes that ON_SubDVertex, ON_SubDEdge, and ON_SubDFace -// classes begin on an 8 bytes boundary because they contain doubles. +// ON_SubDComponentPtr is an unsigned int that stores a pointer to an +// ON_SubDVertex, ON_SubDEdge, or ON_SubDFace, the direction bit +// described above, and a type value used to indicate if the component is +// a vertex(2), edge(4), or face (6). +// +// This code assumes that the memory used to store ON_SubDVertex, ON_SubDEdge, +// and ON_SubDFace classes begins on an 8 bytes boundary (they contain doubles). // If this assumption is false, you will get lots of crashes. // -#define ON_SUBD_ELEMENT_MARK_MASK (0x1U) -#define ON_SUBD_ELEMENT_TYPE_MASK (0x6U) -#define ON_SUBD_ELEMENT_FLAGS_MASK (0x7U) +#define ON_SUBD_COMPONENT_DIRECTION_MASK (0x1U) +#define ON_SUBD_COMPONENT_TYPE_MASK (0x6U) +#define ON_SUBD_COMPONENT_FLAGS_MASK (0x7U) #if (8 == ON_SIZEOF_POINTER) -#define ON_SUBD_ELEMENT_POINTER_MASK (0xFFFFFFFFFFFFFFF8ULL) +#define ON_SUBD_COMPONENT_POINTER_MASK (0xFFFFFFFFFFFFFFF8ULL) #else -#define ON_SUBD_ELEMENT_POINTER_MASK (0xFFFFFFF8U) +#define ON_SUBD_COMPONENT_POINTER_MASK (0xFFFFFFF8U) #endif -#define ON_SUBD_ELEMENT_TYPE_VERTEX (0x2U) -#define ON_SUBD_ELEMENT_TYPE_EDGE (0x4U) -#define ON_SUBD_ELEMENT_TYPE_FACE (0x6U) +#define ON_SUBD_COMPONENT_TYPE_VERTEX (0x2U) +#define ON_SUBD_COMPONENT_TYPE_EDGE (0x4U) +#define ON_SUBD_COMPONENT_TYPE_FACE (0x6U) -#define ON_SUBD_ELEMENT_POINTER(p) ((void*)((p) & ON_SUBD_ELEMENT_POINTER_MASK)) -#define ON_SUBD_ELEMENT_MARK(p) ((p) & ON_SUBD_ELEMENT_MARK_MASK) -#define ON_SUBD_ELEMENT_TYPE(p) ((p) & ON_SUBD_ELEMENT_TYPE_MASK) -#define ON_SUBD_ELEMENT_FLAGS(p) ((p) & ON_SUBD_ELEMENT_FLAGS_MASK) +#define ON_SUBD_COMPONENT_DIRECTION(p) ((p) & ON_SUBD_COMPONENT_DIRECTION_MASK) +#define ON_SUBD_COMPONENT_TYPE(p) ((p) & ON_SUBD_COMPONENT_TYPE_MASK) +#define ON_SUBD_COMPONENT_FLAGS(p) ((p) & ON_SUBD_COMPONENT_FLAGS_MASK) +#define ON_SUBD_COMPONENT_POINTER(p) ((void*)((p) & ON_SUBD_COMPONENT_POINTER_MASK)) -#define ON_SUBD_VERTEX_POINTER(p) ((class ON_SubDVertex*)ON_SUBD_ELEMENT_POINTER(p)) -#define ON_SUBD_VERTEX_MARK(p) ON_SUBD_ELEMENT_MARK(p) - -#define ON_SUBD_EDGE_POINTER(p) ((class ON_SubDEdge*)ON_SUBD_ELEMENT_POINTER(p)) -#define ON_SUBD_FACE_POINTER(p) ((class ON_SubDFace*)ON_SUBD_ELEMENT_POINTER(p)) - -#define ON_SUBD_EDGE_DIRECTION(p) ON_SUBD_ELEMENT_MARK(p) -#define ON_SUBD_FACE_DIRECTION(p) ON_SUBD_ELEMENT_MARK(p) +#define ON_SUBD_VERTEX_DIRECTION(p) ON_SUBD_COMPONENT_DIRECTION(p) +#define ON_SUBD_EDGE_DIRECTION(p) ON_SUBD_COMPONENT_DIRECTION(p) +#define ON_SUBD_FACE_DIRECTION(p) ON_SUBD_COMPONENT_DIRECTION(p) +#define ON_SUBD_VERTEX_POINTER(p) ((class ON_SubDVertex*)ON_SUBD_COMPONENT_POINTER(p)) +#define ON_SUBD_EDGE_POINTER(p) ((class ON_SubDEdge*)ON_SUBD_COMPONENT_POINTER(p)) +#define ON_SUBD_FACE_POINTER(p) ((class ON_SubDFace*)ON_SUBD_COMPONENT_POINTER(p)) ////////////////////////////////////////////////////////////////////////// // // m_saved_points_flags // -//#define ON_SUBD_CACHE_TYPE_MASK (0x0FU) -//#define ON_SUBD_CACHE_POINT_FLAG_MASK (0x20U) -//#define ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK (0x40U) -//#define ON_SUBD_CACHE_LIMIT_FLAG_MASK (0x80U) -#define ON_SUBD_CACHE_TYPE_MASK ON_SubDComponentBase::SavedPointsFlags::SubDTypeMask - -#define ON_SUBD_CACHE_POINT_FLAG_MASK ON_SubDComponentBase::SavedPointsFlags::SubdivisionPointIsSet -#define ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK ON_SubDComponentBase::SavedPointsFlags::DisplacementVectorIsSet -#define ON_SUBD_CACHE_LIMIT_FLAG_MASK ON_SubDComponentBase::SavedPointsFlags::LimitPointIsSet - +#define ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT ON_SubDComponentBase::SavedPointsFlags::ControlNetFragmentBit +#define ON_SUBD_CACHE_POINT_FLAG_BIT ON_SubDComponentBase::SavedPointsFlags::SubdivisionPointBit +#define ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT ON_SubDComponentBase::SavedPointsFlags::SubdivisionDisplacementBit +#define ON_SUBD_CACHE_LIMITLOC_FLAG_BIT ON_SubDComponentBase::SavedPointsFlags::SurfacePointBit #define ON_SUBD_CACHE_FLAGS_MASK ON_SubDComponentBase::SavedPointsFlags::CachedPointMask -#define ON_SUBD_CACHE_TYPE(cache_subd_flags) (ON_SUBD_CACHE_TYPE_MASK&(cache_subd_flags)) #define ON_SUBD_CACHE_FLAGS(cache_subd_flags) (ON_SUBD_CACHE_FLAGS_MASK&(cache_subd_flags)) -#define ON_SUBD_CACHE_POINT_FLAG(cache_subd_flags) (ON_SUBD_CACHE_POINT_FLAG_MASK&(cache_subd_flags)) -#define ON_SUBD_CACHE_DISPLACEMENT_FLAG(cache_subd_flags) (ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK&(cache_subd_flags)) -#define ON_SUBD_CACHE_LIMIT_FLAG(cache_subd_flags) (ON_SUBD_CACHE_LIMIT_FLAG_MASK&(cache_subd_flags)) +#define ON_SUBD_CACHE_CTRLNETFRAG_FLAG(cache_subd_flags) (ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT&(cache_subd_flags)) +#define ON_SUBD_CACHE_POINT_FLAG(cache_subd_flags) (ON_SUBD_CACHE_POINT_FLAG_BIT&(cache_subd_flags)) +#define ON_SUBD_CACHE_DISPLACEMENT_FLAG(cache_subd_flags) (ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT&(cache_subd_flags)) +#define ON_SUBD_CACHE_LIMITLOC_FLAG(cache_subd_flags) (ON_SUBD_CACHE_LIMITLOC_FLAG_BIT&(cache_subd_flags)) -#define ON_SUBD_CACHE_CLEAR_POINT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_TYPE_MASK|ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK|ON_SUBD_CACHE_LIMIT_FLAG_MASK)) -#define ON_SUBD_CACHE_CLEAR_DISPLACEMENT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_TYPE_MASK|ON_SUBD_CACHE_POINT_FLAG_MASK|ON_SUBD_CACHE_LIMIT_FLAG_MASK)) -#define ON_SUBD_CACHE_CLEAR_LIMIT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_TYPE_MASK|ON_SUBD_CACHE_POINT_FLAG_MASK|ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK)) +#define ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_POINT_FLAG_BIT|ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT|ON_SUBD_CACHE_LIMITLOC_FLAG_BIT)) +#define ON_SUBD_CACHE_CLEAR_POINT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT|ON_SUBD_CACHE_LIMITLOC_FLAG_BIT|ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT)) +#define ON_SUBD_CACHE_CLEAR_DISPLACEMENT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_POINT_FLAG_BIT|ON_SUBD_CACHE_LIMITLOC_FLAG_BIT|ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT)) +#define ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_POINT_FLAG_BIT|ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT|ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT)) ////////////////////////////////////////////////////////////////////////// @@ -512,7 +498,7 @@ public: const ON_SubDLevel* level ); - void UpdateEdgeFlags( + void UpdateAggregateEdgeAttributes( const ON_SubDLevel* level ); @@ -520,18 +506,88 @@ public: const ON_SubDLevel* level ); - void MarkAllAsNotCurrent() + void UpdateTopologicalAttributes( + const ON_SubDLevel* level + ); + + /* + Description: + Get saved topological attributes. + Parameters; + bIsManifold - [out] + bIsOriented - [out] + bHasBoundary - [out] + solid_orientation - [out] + +2 subd is a solid but orientation cannot be computed + +1 subd is a solid with outward facing normals + -1 subd is a solid with inward facing normals + 0 subd is not a solid + Returns: + True if global topology attributes were set and retrieved. + False if global topology attributes are not available and need to be updated. + */ + bool GetTopologicalAttributes( + bool& bIsManifold, + bool& bIsOriented, + bool& bHasBoundary, + int& solid_orientation + ) const; + + bool GetTopologicalAttributes( + const ON_SubDLevel* level, + bool& bIsManifold, + bool& bIsOriented, + bool& bHasBoundary, + int& solid_orientation + ); + + void ClearTopologicalAttributes() { - m_bDirtyEdgeFlags = true; - m_bDirtyBoundingBox = true; - m_aggregate_status.MarkAsNotCurrent(); + m_topological_attributes = 0; } - bool m_bDirtyEdgeFlags = false; + void MarkAllAsNotCurrent() + { + m_bDirtyEdgeAttributes = true; + m_bDirtyBoundingBox = true; + m_aggregate_status.MarkAsNotCurrent(); + ClearTopologicalAttributes(); + } + + bool m_bDirtyEdgeAttributes = false; bool m_bDirtyBoundingBox = false; ON_AggregateComponentStatus m_aggregate_status = ON_AggregateComponentStatus::Empty; - unsigned int m_edge_flags; - ON_BoundingBox m_bbox = ON_BoundingBox::EmptyBoundingBox; + unsigned int m_aggregate_edge_attributes = 0; + +private: + // If m_topological_attributes is nonzero, then the topogical attributes are set. + // Otherwise the topogical attributes are unknown and + // UpdateTopologicalAttributes() or GetTopologicalAttributes(level, ...) + // must be called. + unsigned int m_topological_attributes = 0; + +public: + //ON_BoundingBox m_surface_bbox = ON_BoundingBox::EmptyBoundingBox; + ON_BoundingBox m_controlnet_bbox = ON_BoundingBox::EmptyBoundingBox; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentBaseLink +// +class ON_SubDComponentBaseLink : public ON_SubDComponentBase +{ +public: + const ON_SubDComponentBaseLink* m_prev; // m_prev_vertex / m_prev_edge / m_prev_face + const ON_SubDComponentBaseLink* m_next; // m_next_vertex / m_next_edge / m_next_face + + static int CompareId(ON_SubDComponentBaseLink const*const* lhs, ON_SubDComponentBaseLink const*const* rhs); +private: + // no instantiations permitted + ON_SubDComponentBaseLink() = delete; + ~ON_SubDComponentBaseLink() = delete; + ON_SubDComponentBaseLink(const ON_SubDComponentBaseLink&) = delete; + ON_SubDComponentBaseLink& operator=(const ON_SubDComponentBaseLink&) = delete; }; ////////////////////////////////////////////////////////////////////////// @@ -571,19 +627,25 @@ private: public: unsigned int m_level_index = ON_UNSET_UINT_INDEX; - ON_SubD::SubDType m_subdivision_type = ON_SubD::SubDType::QuadCatmullClark; - - unsigned char m_ordinary_vertex_valence = 0; // 0 = none, 4 = quads, 6 = triangles - unsigned char m_ordinary_face_edge_count = 0; // 0 = none, 4 = quads, 3 = triangles + // temporary + enum : unsigned char + { + m_ordinary_vertex_valence = 4 + }; + enum : unsigned char + { + m_ordinary_face_edge_count = 4 + }; private: - unsigned char m_reserved1 = 0; + unsigned int m_reserved1 = 0; public: bool CopyHelper( - const ON_SubDLevel& src, + const class ON_SubDimple& src_subdimple, + const ON_SubDLevel& src_level, class ON_SubDArchiveIdMap& eptrlist, - class ON_SubDimple& subdimple, + class ON_SubDimple& dest_subdimple, bool bCopyComponentStatus ); @@ -604,6 +666,14 @@ public: bool bUnsetValuesOnly ); + unsigned int ClearComponentDamagedState() const; + + unsigned int ClearComponentDamagedState( + bool bClearVerticesDamagedState, + bool bClearEdgesDamagedState, + bool bClearFacesDamagedState + ) const; + private: void DestroyOnError() { @@ -643,13 +713,18 @@ public: std::shared_ptr EdgeArray() const; std::shared_ptr FaceArray() const; - mutable ON_SubDLimitMesh m_limit_mesh; + mutable ON_SubDMesh m_surface_mesh; + mutable ON_SubDMesh m_control_net_mesh; /* Description: Sets the mutable m_archive_id value for every vertex, edge and face in this level. Parameters: + subdimple - [in] + The subdimple is used to generate id iterators + from the vertex/edge,face heaps when the ids are not + strictly increasing in the linked lists on the level. archive_id_partition - [out] archive_id_partition[0] = 1 = first vertex archive_id archive_id_partition[1]-1 = last vertex archive_id @@ -657,11 +732,26 @@ public: archive_id_partition[2]-1 = last edge archive_id archive_id_partition[2] = first face archive_id archive_id_partition[3]-1 = last face archive_id + bLevelLinkedListIncreasingId - [out] + + bLevelLinkedListIncreasingId[0] = true if the value + of ON_SubDVertex.m_id increases throughout the level's + linked list beginning with m_vertex[0]. False otherwise + in which case slower use of the subdimple.m_heap.m_fspv + must be used to iterate over the level's vertices in + increasing ON_SubDVertex.m_id order. + + bLevelLinkedListIncreasingId[1] m_edge[0] linked list id order. + + bLevelLinkedListIncreasingId[2] m_face[0] linked list id order. + Returns: The number of vertices, edges and faces on this level. */ unsigned int SetArchiveId( - unsigned int archive_id_partition[4] + const ON_SubDimple& subdimple, + unsigned int archive_id_partition[4], + bool bLevelLinkedListIncreasingId[3] ) const; void ClearArchiveId() const; @@ -673,6 +763,7 @@ public: ); bool Write( + const ON_SubDimple& subdimple, ON_BinaryArchive& archive ) const; @@ -694,10 +785,6 @@ public: const class ON_Xform& xform ); - bool SetSubDType( - ON_SubD::SubDType subdivision_type - ); - void ResetVertexArray() { if (m_vertex_array_count) @@ -732,7 +819,7 @@ public: bool RemoveVertex(class ON_SubDVertex* vertex) { m_aggregates.m_bDirtyBoundingBox = true; - if (nullptr == vertex || vertex->m_level != this->m_level_index ) + if (nullptr == vertex || vertex->SubdivisionLevel() != this->m_level_index ) return ON_SUBD_RETURN_ERROR(false); if ( 0 == m_vertex_count) return ON_SUBD_RETURN_ERROR(false); @@ -804,8 +891,8 @@ public: bool RemoveEdge(class ON_SubDEdge* edge) { - m_aggregates.m_bDirtyEdgeFlags = true; - if (nullptr == edge || edge->m_level != this->m_level_index ) + m_aggregates.m_bDirtyEdgeAttributes = true; + if (nullptr == edge || edge->SubdivisionLevel() != this->m_level_index ) return ON_SUBD_RETURN_ERROR(false); if ( 0 == m_edge_count) return ON_SUBD_RETURN_ERROR(false); @@ -877,7 +964,7 @@ public: bool RemoveFace(class ON_SubDFace* face) { - if (nullptr == face || face->m_level != this->m_level_index ) + if (nullptr == face || face->SubdivisionLevel() != this->m_level_index ) return ON_SUBD_RETURN_ERROR(false); if ( 0 == m_face_count) return ON_SUBD_RETURN_ERROR(false); @@ -970,7 +1057,7 @@ public: const ON_SubDEdge* AddEdge(class ON_SubDEdge* edge) { - m_aggregates.m_bDirtyEdgeFlags = true; + m_aggregates.m_bDirtyEdgeAttributes = true; if (nullptr == edge) return nullptr; if (nullptr == m_edge[1]) @@ -1019,9 +1106,6 @@ public: bool bUnsetEdgeSectorCoefficientsOnly ); - - void ClearSubdivisonAndLimitPoints() const; - /* Description: If a state is set in the status parameter, it will be cleared @@ -1033,6 +1117,18 @@ public: ON_ComponentStatus states_to_clear ) const; + /* + Description: + Clears all runtime marks for components on this level. + Returns: + Number of components where runtime mark was changed. + */ + unsigned int ClearRuntimeMarks( + bool bClearVertexMarks, + bool bClearEdgeMarks, + bool bClearFaceMarks + ) const; + unsigned int GetComponentsWithSetStates( ON_ComponentStatus states_filter, bool bAllEqualStates, @@ -1066,11 +1162,44 @@ public: m_aggregates.m_bDirtyBoundingBox = true; } - ON_BoundingBox BoundingBox() const; + void ClearEvaluationCache() const; + + void ClearTopologicalAttributes() const + { + m_aggregates.ClearTopologicalAttributes(); + } + + /* + Description: + Get saved topological attributes. + Parameters; + bIsManifold - [out] + bIsOriented - [out] + bHasBoundary - [out] + solid_orientation - [out] + +2 subd is a solid but orientation cannot be computed + +1 subd is a solid with outward facing normals + -1 subd is a solid with inward facing normals + 0 subd is not a solid + Returns: + True if global topology attributes were set and retrieved. + False if global topology attributes are not available and need to be updated. + */ + void GetTopologicalAttributes( + bool& bIsManifold, + bool& bIsOriented, + bool& bHasBoundary, + int& solid_orientation + ) const + { + m_aggregates.GetTopologicalAttributes(this, bIsManifold, bIsOriented, bHasBoundary, solid_orientation); + } + + ON_BoundingBox ControlNetBoundingBox() const; void ClearEdgeFlags() const { - m_aggregates.m_bDirtyEdgeFlags = true; + m_aggregates.m_bDirtyEdgeAttributes = true; } unsigned int EdgeFlags() const; @@ -1149,6 +1278,7 @@ public: void ReturnEdge(class ON_SubDEdge* e); class ON_SubDFace* AllocateFaceAndSetId(unsigned int& max_face_id); + class ON_SubDFace* AllocateFaceAndSetId(const ON_SubDFace* candidate_face, unsigned int& max_face_id); void ReturnFace(class ON_SubDFace* f); @@ -1245,6 +1375,71 @@ public: ON_SubDFace* f ); + /* + Description: + Allocates fragments reference by ON_SubDFace.m_limit_mesh_fragments + for faces in this SubD. The fragments have to be 16x16 or 8x8. + Parameters: + src_fragment - [in] + This fragment is copied to the returned fragment. + The fragment grid must be 16x16 or 8x8. + */ + class ON_SubDMeshFragment* AllocateMeshFragment( + const class ON_SubDMeshFragment& src_fragment + ); + + /* + Parameters: + fragment - [in] + A pointer to a no longer used fragment that + came from AllocateMeshFragment(). + */ + bool ReturnMeshFragment( + class ON_SubDMeshFragment* fragment + ); + + /* + Parameters: + face - [in] + A face in this subd. + */ + bool ReturnMeshFragments( + const class ON_SubDFace* face + ); + + + /* + Description: + Allocates limit curves reference by ON_SubDEdge.m_limit_curve + for edges in this SubD. + Parameters: + cv_capacity - [in] + desired cv_capacity <= ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity + */ + class ON_SubDEdgeSurfaceCurve* AllocateEdgeSurfaceCurve( + unsigned int cv_capacity + ); + + /* + Parameters: + limit_curve - [in] + A pointer to a no longer used limit_curve that + came from AllocateEdgeSurfaceCurve(). + */ + bool ReturnEdgeSurfaceCurve( + class ON_SubDEdgeSurfaceCurve* limit_curve + ); + + /* + Parameters: + edge - [in] + An edge in this subd. + */ + bool ReturnEdgeSurfaceCurve( + const class ON_SubDEdge* edge + ); + + /* Description: @@ -1280,13 +1475,56 @@ private: class tagWSItem* m_ws = nullptr; // oversized arrays of ON__UINT_PTRs +private: ON_FixedSizePool m_fspv; // element = ON_SubDVertex +public: + void InitializeVertexIdIterator( + class ON_SubDVertexIdIterator& vidit + ) const; + +private: ON_FixedSizePool m_fspe; // element = ON_SubDEdge +public: + void InitializeEdgeIdIterator( + class ON_SubDEdgeIdIterator& vidit + ) const; + +private: ON_FixedSizePool m_fspf; // element = ON_SubDFace +public: + void InitializeFaceIdIterator( + class ON_SubDFaceIdIterator& vidit + ) const; + +public: + void InitializeComponentIdIterator( + ON_SubDComponentPtr::Type ctype, + class ON_SubDComponentIdIterator& cidit + ) const; + +private: ON_FixedSizePool m_fsp5; // element = capacity + array of 4 ON__UINT_PTRs ON_FixedSizePool m_fsp9; // element = capacity + array of 8 ON__UINT_PTRs ON_FixedSizePool m_fsp17; // element = capacity + array of 16 ON__UINT_PTRs + // This pool is used to manage memory for + // + // 16x16 ON_SubDMeshFragment + // 8x8 ON_SubDMeshFragment + // ON_SubDEdgeSurfaceCurve + bool Internal_InitializeLimitBlockPool(); + ON_FixedSizePool m_limit_block_pool; + // Used to allocate 16x16 fragments for quads + size_t m_sizeof_full_fragment = 0; + class ON_FixedSizePoolElement* m_unused_full_fragments = nullptr; + // Used to allocate 8x8 fragments for N-gons with N != 4 + size_t m_sizeof_half_fragment = 0; + class ON_FixedSizePoolElement* m_unused_half_fragments = nullptr; + // Used to allocate edge curves + size_t m_sizeof_limit_curve = 0; + class ON_FixedSizePoolElement* m_unused_limit_curves = nullptr; + + ON_SubDVertex* m_unused_vertex = nullptr; ON_SubDEdge* m_unused_edge = nullptr; ON_SubDFace* m_unused_face = nullptr; @@ -1336,6 +1574,29 @@ public: The empty subd has runtime serial number = 0. */ const ON__UINT64 RuntimeSerialNumber; + + /* + Returns: + A runtime serial number that is incremented every time a the active level, + vertex location, vertex or edge flag, or subd topology is chaned. + */ + ON__UINT64 ContentSerialNumber() const; + + /* + Description: + Change the content serial number. + Returns: + The new value of ConentSerialNumber(). + Remarks: + The value can change by any amount. + */ + ON__UINT64 ChangeContentSerialNumber() const; + +private: + mutable ON__UINT64 m_subd_content_serial_number = 0; + +public: + #if defined(ON_SUBD_CENSUS) ON_SubDImpleCensusCounter m_census_counter; @@ -1345,11 +1606,30 @@ public: ~ON_SubDimple(); ON_SubDimple(const ON_SubDimple&); - void SetLimitMeshSubDWeakPointer( + void SetManagedMeshSubDWeakPointers( std::shared_ptr& subdimple_sp ); + /* + Description: + Get the SubD appearance (surface or control net); + Returns: + ON_SubDComponentLocation::Surface or ON_SubDComponentLocation::ControlNet. + */ + ON_SubDComponentLocation SubDAppearance() const; + + /* + Description: + Set the SubD appearance (surface or control net) for a given context. + Parameters: + subd_appearance - [in] + ON_SubDComponentLocation::Surface or ON_SubDComponentLocation::ControlNet. + Remarks: + This makes no changes to the information that defines the SubD. + */ + void SetSubDAppearance(ON_SubDComponentLocation subd_appearance) const; + bool IsValid( const ON_SubD& subd, bool bSilentError, @@ -1435,6 +1715,12 @@ public: const ON_SubDEdgePtr* edge ); + class ON_SubDFace* AddFace( + const ON_SubDFace* candidate_face, + unsigned int edge_count, + const ON_SubDEdgePtr* edge + ); + /* Description: Split a face into two faces by inserting and edge connecting the @@ -1459,10 +1745,6 @@ public: unsigned int fvi1 ); - bool SetSubDType( - ON_SubD::SubDType subdivision_type - ); - class ON_SubDVertex* AllocateVertex( ON_SubD::VertexTag vertex_tag, unsigned int level, @@ -1470,7 +1752,7 @@ public: ) { class ON_SubDVertex* v = m_heap.AllocateVertexAndSetId(m_max_vertex_id); - v->m_level = (unsigned short)level; + v->SetSubdivisionLevel(level); v->m_vertex_tag = vertex_tag; if (nullptr != P) { @@ -1500,19 +1782,19 @@ public: const class ON_SubDVertex* AddVertexToLevel(class ON_SubDVertex* v) { - class ON_SubDLevel* subd_level = SubDLevel(v->m_level,true); + class ON_SubDLevel* subd_level = SubDLevel(v->SubdivisionLevel(),true); return (subd_level) ? subd_level->AddVertex(v) : nullptr; } void ReturnVertex(class ON_SubDVertex* v) { - if (nullptr != v && v->m_level < m_levels.UnsignedCount()) + if (nullptr != v && v->SubdivisionLevel() < m_levels.UnsignedCount()) { - ON_SubDLevel* level = m_levels[v->m_level]; + ON_SubDLevel* level = m_levels[v->SubdivisionLevel()]; if (level) level->RemoveVertex(v); } - v->ClearSavedLimitPoints(); // return extras to pool + v->ClearSavedSubdivisionPoints(); // return extras to pool m_heap.ReturnVertex(v); } @@ -1532,7 +1814,7 @@ public: ) { class ON_SubDEdge* e = AllocateEdge(edge_tag); - e->m_level = (unsigned short)level; + e->SetSubdivisionLevel(level); if (face_capacity > 0 && face_capacity <= ON_SubDEdge::MaximumFaceCount ) m_heap.GrowEdgeFaceArray(e,face_capacity); return e; @@ -1540,15 +1822,15 @@ public: const class ON_SubDEdge* AddEdgeToLevel(class ON_SubDEdge* e) { - class ON_SubDLevel* subd_level = SubDLevel(e->m_level,true); + class ON_SubDLevel* subd_level = SubDLevel(e->SubdivisionLevel(),true); return (subd_level) ? subd_level->AddEdge(e) : nullptr; } void ReturnEdge(class ON_SubDEdge* e) { - if (nullptr != e && e->m_level < m_levels.UnsignedCount()) + if (nullptr != e && e->SubdivisionLevel() < m_levels.UnsignedCount()) { - ON_SubDLevel* level = m_levels[e->m_level]; + ON_SubDLevel* level = m_levels[e->SubdivisionLevel()]; if (level) level->RemoveEdge(e); } @@ -1561,6 +1843,12 @@ public: return f; } + class ON_SubDFace* AllocateFace(const ON_SubDFace* candidate_face) + { + class ON_SubDFace* f = m_heap.AllocateFaceAndSetId(candidate_face, m_max_face_id); + return f; + } + class ON_SubDFace* AllocateFace( unsigned int level, unsigned int edge_capacity @@ -1569,7 +1857,7 @@ public: class ON_SubDFace* f = AllocateFace(); if (nullptr != f) { - f->m_level = (unsigned short)level; + f->SetSubdivisionLevel(level); if (edge_capacity > sizeof(f->m_edge4)/sizeof(f->m_edge4[0]) && edge_capacity <= ON_SubDFace::MaximumEdgeCount) m_heap.GrowFaceEdgeArray(f,edge_capacity); } @@ -1578,15 +1866,15 @@ public: const class ON_SubDFace* AddFaceToLevel(class ON_SubDFace* f) { - class ON_SubDLevel* subd_level = SubDLevel(f->m_level,true); + class ON_SubDLevel* subd_level = SubDLevel(f->SubdivisionLevel(),true); return (subd_level) ? subd_level->AddFace(f) : nullptr; } void ReturnFace(class ON_SubDFace* f) { - if (nullptr != f && f->m_level < m_levels.UnsignedCount()) + if (nullptr != f && f->SubdivisionLevel() < m_levels.UnsignedCount()) { - ON_SubDLevel* level = m_levels[f->m_level]; + ON_SubDLevel* level = m_levels[f->SubdivisionLevel()]; if (level) level->RemoveFace(f); } @@ -1594,7 +1882,10 @@ public: } unsigned int DeleteComponents( - unsigned int level_index + unsigned int level_index, + bool bDeleteIsolatedEdges, + bool bUpdateTagsAndCoefficients, + bool bMarkDeletedFaceEdges ); /* @@ -1632,22 +1923,25 @@ public: return sz; } - bool Subdivide( - ON_SubD::SubDType subd_type, - unsigned int level_index, + bool GlobalSubdivide( unsigned int count ); + bool LocalSubdivide( + ON_SubDFace const*const* face_list, + size_t face_count + ); + /* Description: Apply global subdivision to m_levels[].Last(). */ - unsigned int GlobalSubdivide( - ON_SubD::SubDType subdivision_type, - bool bUseSavedSubdivisionPoints - ); + unsigned int GlobalSubdivide(); unsigned int MergeColinearEdges( + bool bMergeBoundaryEdges, + bool bMergeInteriorCreaseEdges, + bool bMergeInteriorSmoothEdges, double distance_tolerance, double maximum_aspect, double sin_angle_tolerance @@ -1677,15 +1971,51 @@ public: ); private: + friend class ON_Internal_SubDFaceMeshFragmentAccumulator; ON_SubDHeap m_heap; +public: + void InitializeVertexIdIterator( + class ON_SubDVertexIdIterator& vidit + ) const; + + void InitializeEdgeIdIterator( + class ON_SubDEdgeIdIterator& eidit + ) const; + + void InitializeFaceIdIterator( + class ON_SubDFaceIdIterator& fidit + ) const; + + void InitializeComponentIdIterator( + ON_SubDComponentPtr::Type ctype, + class ON_SubDComponentIdIterator& cidit + ) const; + + + const ON_MappingTag TextureMappingTag() const; + void SetTextureMappingTag(const ON_MappingTag& mapping_tag) const; + + enum ON_SubDTextureDomainType TextureDomainType() const; + void SetTextureDomainType( + ON_SubDTextureDomainType texture_domain_type + ) const; + +private: unsigned int m_max_vertex_id = 0; unsigned int m_max_edge_id = 0; unsigned int m_max_face_id = 0; + + + mutable ON_SubDComponentLocation m_subd_appearance = ON_SubD::DefaultSubDAppearance; + mutable ON_SubDTextureDomainType m_texture_domain_type = ON_SubDTextureDomainType::Unset; + unsigned short m_reserved = 0; + mutable ON_MappingTag m_texture_mapping_tag; + ON_SimpleArray< ON_SubDLevel* > m_levels; ON_SubDLevel* m_active_level = nullptr; // m_active_level = nullptr or m_active_level = m_levels[m_active_level->m_level_index]. - + public: unsigned int MaximumVertexId() const { @@ -1707,6 +2037,11 @@ public: { return (nullptr != m_active_level) ? *m_active_level : ON_SubDLevel::Empty; } + + const unsigned int ActiveLevelIndex() const + { + return (nullptr != m_active_level) ? m_active_level->m_level_index : 0; + } ON_SubDLevel* ActiveLevelPointer() { @@ -1757,39 +2092,107 @@ private: Number of quads added. When all input is valid the returned value is >= 4 and equal to face->m_edge_count. */ - unsigned int GlobalQuadSubdivideFace( - bool bUseSavedSubdivisionPoint, + unsigned int Internal_GlobalQuadSubdivideFace( const ON_SubDFace* face ); - /* - Parameters: - face - [in] - bUseSavedSubdivisionPoint - [in] - bUnsetEdgeWeight - [out] - false - new edges have weights set - true - the value of one or more ON_SubDEdge::m_vertex_weight[] - was set to ON_SubDSectorType::UnsetSectorWeight because it cannot - be calculated until the the rest of the subdivision is complete. - This happens when sector face counts are required - and a non-triangluar face is present in the level bing subdivided. - Returns: - Number of triangles added. - If the input is valid and face->m_edge_count = 3, then the - returned value will be 4. - If the input is valid and face->m_edge_count > 3, then the - returned value will be 2*face->m_edge_count. - */ - unsigned int GlobalTriSubdivideFace( - const ON_SubDFace* face, - bool bUseSavedSubdivisionPoint, - bool* bUnsetEdgeWeight - ); + private: ON_SubDimple& operator=(const ON_SubDimple&) = delete; }; +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentIdIterator +// +class ON_SubDComponentIdIterator : private ON_FixedSizePoolIterator +{ +public: + ON_SubDComponentIdIterator() = default; + ~ON_SubDComponentIdIterator() = default; + ON_SubDComponentIdIterator(const ON_SubDComponentIdIterator&) = default; + ON_SubDComponentIdIterator& operator=(const ON_SubDComponentIdIterator&) = default; + +public: + /* + Description: + In general, you want to use a ON_SubDVertexIterator to loop through SubD vertices. + This is a special tool for unusual sitiations wheh it is necessary to + iteratate through every vertex on every level of a SubD in order + of increasing m_id value. + Returns: + The vertex with the smallest id. + */ + const ON_SubDComponentBase* FirstComponent(); + + /* + Description: + In general, you want to use a ON_SubDVertexIterator to loop through SubD vertices. + This is a special tool for unusual sitiations wheh it is necessary to + iteratate through every vertex on every level of a SubD in order + of increasing m_id value. + Returns: + The vertex in order of increasing id. + */ + const ON_SubDComponentBase* NextComponent(); + + /* + Returns: + The most recently returned vertex from a call to FirstVertex() or NextVertex(). + */ + const ON_SubDComponentBase* CurrentComponent() const; + +private: + friend class ON_SubDHeap; + ON_SubDComponentPtr::Type m_component_type = ON_SubDComponentPtr::Type::Unset; +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDLevelComponentIdIterator +// +class ON_SubDLevelComponentIdIterator +{ +public: + ON_SubDLevelComponentIdIterator() = default; + ~ON_SubDLevelComponentIdIterator() = default; +private: + ON_SubDLevelComponentIdIterator(const ON_SubDLevelComponentIdIterator&) = delete; + ON_SubDLevelComponentIdIterator& operator=(const ON_SubDLevelComponentIdIterator&) = delete; + +public: + void Initialize( + bool bLevelLinkedListIncreasingId, + ON_SubDComponentPtr::Type ctype, + const ON_SubDimple& subdimple, + const ON_SubDLevel& level + ); + + const ON_SubDVertex* FirstVertex(); + const ON_SubDVertex* NextVertex(); + + const ON_SubDEdge* FirstEdge(); + const ON_SubDEdge* NextEdge(); + + const ON_SubDFace* FirstFace(); + const ON_SubDFace* NextFace(); + +private: + const ON_SubDComponentBase* InternalFirst(); + + const ON_SubDComponentBase* InternalNext(); + +private: + bool m_bLevelLinkedListIncreasingId = false; + ON_SubDComponentPtr::Type m_ctype = ON_SubDComponentPtr::Type::Unset; + unsigned short m_level_index = 0; + const ON_SubDComponentBaseLink* m_first = nullptr; + const ON_SubDComponentBaseLink* m_current = nullptr; + unsigned int m_count = 0; + unsigned int m_prev_id = 0; + ON_SubDComponentIdIterator m_cidit; +}; @@ -1908,44 +2311,39 @@ private: ////////////////////////////////////////////////////////////////////////// // -// ON_SubDLimitMesh +// ON_SubDMesh // -class /*DO NOT EXPORT*/ON_SubDLimitMeshImpl +class /*DO NOT EXPORT*/ON_SubDMeshImpl { #if defined(ON_SUBD_CENSUS) - ON_SubDLimitMeshImplCensusCounter m_census_counter; + ON_SubDMeshCensusCounter m_census_counter; #endif public: - ON_SubDLimitMeshImpl(); - ~ON_SubDLimitMeshImpl() = default; - ON_SubDLimitMeshImpl(const ON_SubDLimitMeshImpl& src); + ON_SubDMeshImpl(); + ~ON_SubDMeshImpl() = default; + ON_SubDMeshImpl(const ON_SubDMeshImpl& src); private: // no operator = - ON_SubDLimitMeshImpl& operator=(const ON_SubDLimitMeshImpl&) = delete; + ON_SubDMeshImpl& operator=(const ON_SubDMeshImpl&) = delete; public: - ON_SubD::FacetType m_facet_type = ON_SubD::FacetType::Unset; + ON__UINT64 ContentSerialNumber() const; + ON__UINT64 ChangeContentSerialNumber(); + private: - unsigned char m_reserved1 = 0; - unsigned short m_reserved2 = 0; - unsigned int m_reserved3 = 0; -public: - unsigned int m_limit_mesh_content_serial_number; -private: - static unsigned int Internal_NextContentSerialNumber(); + ON__UINT64 m_mesh_content_serial_number = 0; public: unsigned int m_display_density = 0; unsigned int m_fragment_count = 0; unsigned int m_fragment_point_count = 0; - ON_SubDLimitMeshFragment* m_first_fragment = nullptr; - ON_SubDLimitMeshFragment* m_last_fragment = nullptr; + ON_SubDMeshFragment* m_first_fragment = nullptr; + ON_SubDMeshFragment* m_last_fragment = nullptr; bool ReserveCapacity( unsigned int subd_fragment_count, - ON_SubD::FacetType facet_type, unsigned int display_density ); @@ -1953,20 +2351,20 @@ public: Description: ON_SubDLimitMeshImpl_CallbackContext::FragmentCallbackFunction() uses CopyCallbackFragment() to make a copy of callback_fragment - delivered by ON_SubD::GetLimitSurfaceMeshInFragments(). + delivered by ON_SubD::GetMeshFragments(). */ - ON_SubDLimitMeshFragment* CopyCallbackFragment( - const ON_SubDLimitMeshFragment* callback_fragment + ON_SubDMeshFragment* CopyCallbackFragment( + const ON_SubDMeshFragment* callback_fragment ); /* Description: ON_SubDLimitMeshImpl_CallbackContext::FragmentCallbackFunction() uses AddFinishedFragment() to add finished fragments to this - ON_SubDLimitMeshImpl's m_first_fragment ... m_list_fragment list. + ON_SubDMeshImpl's m_first_fragment ... m_list_fragment list. */ bool AddFinishedFragment( - ON_SubDLimitMeshFragment* fragment + ON_SubDMeshFragment* fragment ); /* @@ -1996,7 +2394,7 @@ public: // The weak pointer to the ON_SubDimple is used to // check that the ON_SubDimple managing the - // ON_SubDLimitMeshFragment.m_face pointers is valid + // ON_SubDMeshFragment.m_face pointers is valid // before those pointers are used. This must be a weak // pointer and not a shared_ptr because limit meshes // are stored on ON_SubDLevels that are members of @@ -2016,12 +2414,103 @@ private: ON_RTree* m_fragment_tree = nullptr; private: + // A fixed sized memory pool that allocates enough memory for a fragment and its points and normals. + // The fragments never get returned because the pool itself is destroyed in ~ON_SubDMeshImpl(). ON_FixedSizePool m_fsp; }; +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdgeSurfaceCurve +// + +class /*DO NOT EXPORT*/ ON_SubDEdgeSurfaceCurve +{ +public: + // Use CopyFrom() when proper managment of m_cvx is required. + // This class is used internally and never seen int developer SDK. + static const ON_SubDEdgeSurfaceCurve Unset; // all doubles are ON_UNSET_VALUE, everything else is zero. + static const ON_SubDEdgeSurfaceCurve Nan; // all doubles are ON_DBL_QNAN, everything else is zero + static const ON_SubDEdgeSurfaceCurve Zero; // all doubles are 0.0, everything else is zero + +public: + /* + Returns: + true if 4 <= m_cv_count <= 11 and all coordinates are valid doubles. + false otherwise. + */ + bool IsSet() const; + + bool Transform( + const ON_Xform& xform + ); + + void Clear(); + + /* + Parameters: + cv_count - [in] + 0 or 4 <= cv_count <= CVCapacity(). + cvs - [in] + cvs for a cubic uniform non-rational NURBS curve with unclamped knot vector + (-2,-1,0,1,2,3,...,cv_count-1). + Remarks: + The knot vector is unclamped to permit efficient joining of adjacent edge + curves into longer NURBS with simple interior knots. This occures frequently. + */ + bool SetCVs( + int cv_count, + const ON_3dPoint* cvs + ); + + unsigned int CVCount() const; + + unsigned int CVCapacity() const; + + /* + Parameters: + cv_capacity - [in] + maximum number of points the cvs[] array can contain. + cvs - [out] + cvs returned here + Returns: + number of set cvs. + The cvs are for a cubic uniform non-rational unclamped NURBS curve + with unclamped knot vector (-2,-1,0,1,2,3,...,cv_count-1). + Remarks: + The knot vector is unclamped to permit efficient joining of adjacent edge + curves into longer NURBS with simple interior knots. This occures frequently. + */ + unsigned int GetCVs( + size_t cv_capacity, + ON_3dPoint* cvs + ) const; + +public: + enum : unsigned char + { + MinimumControlPointCapacity = 5, + MaximumControlPointCapacity = 11 + }; + +private: + // It is critical that sizeof(ON_SubDEdgeSurfaceCurve) = 6*3*sizeof(double). + // The edge curve cache relies on this. + // Do not remove m_reserved* fields. + ON__UINT64 m_reserved0 = 0; // overlaps with ON_FixedSizePoolElement.m_next. +public: + unsigned char m_cv_count = 0; + unsigned char m_cv_capacity = ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity; + unsigned short m_reserved2 = 0; + unsigned int m_reserved3 = 0; + double m_cv5[ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity][3]; // initial 4 or 5 cvs are here. + // If m_cv_capacity > ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity and m_cvx != nullptr, + // m_cvx points to an array of 3*(ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity-ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity) + // doubles and m_cv_capacity = ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity; + double* m_cvx = nullptr; +}; + #endif // ON_COMPILING_OPENNURBS) -#endif // OPENNURBS_SUBD_WIP - #endif // OPENNURBS_SUBD_DATA_INC_ diff --git a/opennurbs_subd_eval.cpp b/opennurbs_subd_eval.cpp index c95dbc9c..f188897f 100644 --- a/opennurbs_subd_eval.cpp +++ b/opennurbs_subd_eval.cpp @@ -10,8 +10,6 @@ #include "opennurbs_subd_data.h" -#if defined(OPENNURBS_SUBD_WIP) - ON_SubD* ON_SubDSectorType::SectorRingSubD( double radius, double sector_angle_radians, @@ -39,14 +37,8 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( if (F != N && F + 1 != N) return ON_SUBD_RETURN_ERROR(nullptr); - const ON_SubD::SubDType subdivision_type = SubDType(); const ON_SubD::VertexTag vertex_tag = VertexTag(); - - const unsigned f_edge_count = ON_SubD::FacetEdgeCount(subdivision_type); - if (3 != f_edge_count && 4 != f_edge_count) - return ON_SUBD_RETURN_ERROR(nullptr); - - const unsigned int ring_ei_delta = f_edge_count - 2; + const unsigned int ring_ei_delta = 2; if (nullptr == subd) subd = new ON_SubD; @@ -96,12 +88,12 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( break; } - unsigned int sector_angle_index = ON_SubDSectorType::AngleIndexFromAngleRadians(sector_angle_radians); - if (sector_angle_index <= ON_SubDSectorType::MaximumAngleIndex - && fabs(ON_SubDSectorType::AngleRadiansFromAngleIndex(sector_angle_index) - sector_angle_radians) <= 1.0e-6 + unsigned int sector_angle_index = ON_SubDSectorType::CornerAngleIndexFromCornerAngleRadians(sector_angle_radians); + if (sector_angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex + && fabs(ON_SubDSectorType::AngleRadiansFromCornerAngleIndex(sector_angle_index) - sector_angle_radians) <= 1.0e-6 ) { - sector_angle_radians = ON_SubDSectorType::AngleRadiansFromAngleIndex(sector_angle_index); + sector_angle_radians = ON_SubDSectorType::AngleRadiansFromCornerAngleIndex(sector_angle_index); } else { @@ -138,9 +130,9 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( } else { - ON_SubDMatrix::EvaluateCosAndSin(2*sector_angle_index*vi, (R-1)*ON_SubDSectorType::MaximumAngleIndex,&cos_a,&sin_a); + ON_SubDMatrix::EvaluateCosAndSin(2*sector_angle_index*vi, (R-1)*ON_SubDSectorType::MaximumCornerAngleIndex,&cos_a,&sin_a); } - const double r = (3 == f_edge_count) || (1 == (vi%2)) ? radius : (2.0*radius); + const double r = (1 == (vi%2)) ? radius : (2.0*radius); vertexP.x = r*cos_a; vertexP.y = r*sin_a; } @@ -174,31 +166,23 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( ON_SubDEdgePtr f_edgeptr[4] = {}; f_vertex[0] = V[0]; - f_vertex[f_edge_count - 1] = const_cast(E[0]->m_vertex[1]); - f_edge[f_edge_count - 1] = E[0]; + f_vertex[3] = const_cast(E[0]->m_vertex[1]); + f_edge[3] = E[0]; for (unsigned int vfi = 0; vfi < F; vfi++) { - f_edge[0] = f_edge[f_edge_count - 1]; - f_edge[f_edge_count-1] = E[(vfi + 1) % N]; + f_edge[0] = f_edge[3]; + f_edge[3] = E[(vfi + 1) % N]; f_vertex[1] = const_cast(f_edge[0]->m_vertex[1]); - f_vertex[f_edge_count - 1] = const_cast(f_edge[f_edge_count - 1]->m_vertex[1]); + f_vertex[3] = const_cast(f_edge[3]->m_vertex[1]); f_edgeptr[0] = ON_SubDEdgePtr::Create(f_edge[0], 0); - f_edgeptr[f_edge_count - 1] = ON_SubDEdgePtr::Create(f_edge[f_edge_count - 1], 1); - if (4 == f_edge_count) - { - f_vertex[2] = V[2 + 2 * vfi]; - f_edge[1] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[1], ON_SubDSectorType::IgnoredSectorWeight, f_vertex[2], ON_SubDSectorType::IgnoredSectorWeight); - f_edge[2] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[2], ON_SubDSectorType::IgnoredSectorWeight, f_vertex[3], ON_SubDSectorType::IgnoredSectorWeight); - f_edgeptr[1] = ON_SubDEdgePtr::Create(f_edge[1], 0); - f_edgeptr[2] = ON_SubDEdgePtr::Create(f_edge[2], 0); - } - else - { - f_edge[1] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[1], ON_SubDSectorType::IgnoredSectorWeight, f_vertex[2], ON_SubDSectorType::IgnoredSectorWeight); - f_edgeptr[1] = ON_SubDEdgePtr::Create(f_edge[1], 0); - } - subd->AddFace(f_edge_count, f_edgeptr); + f_edgeptr[3] = ON_SubDEdgePtr::Create(f_edge[3], 1); + f_vertex[2] = V[2 + 2 * vfi]; + f_edge[1] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[1], ON_SubDSectorType::IgnoredSectorWeight, f_vertex[2], ON_SubDSectorType::IgnoredSectorWeight); + f_edge[2] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[2], ON_SubDSectorType::IgnoredSectorWeight, f_vertex[3], ON_SubDSectorType::IgnoredSectorWeight); + f_edgeptr[1] = ON_SubDEdgePtr::Create(f_edge[1], 0); + f_edgeptr[2] = ON_SubDEdgePtr::Create(f_edge[2], 0); + subd->AddFace(f_edgeptr,4); } return subd; @@ -248,38 +232,6 @@ static bool TestPoint( return true; } -//double ON_QNaN() -//{ -// const char* sIgnored = ""; -// return nan(sIgnored); -//} -// -//bool ON_IsNaN(double x) -//{ -// return (0 != isnan(x)); -//} -// -//void ON_SetToQNaN(double* x) -//{ -// if (nullptr != x) -// *x = ON_QNaN(); -//} -// -//bool ON_IsNaN(ON_3dPoint& point) -//{ -// return (ON_IsNaN(point.x) || ON_IsNaN(point.y) || ON_IsNaN(point.z)); -//} -// -//void ON_SetToQNaN(ON_3dPoint* point) -//{ -// if (nullptr != point) -// { -// point->x = ON_QNaN(); -// point->y = ON_QNaN(); -// point->z = ON_QNaN(); -// } -//} - static bool ClearCachedPoints( unsigned int component_ring_count, const ON_SubDComponentPtr* component_ring @@ -290,21 +242,20 @@ static bool ClearCachedPoints( ON_SubDVertex* vertex = component_ring[0].Vertex(); if ( nullptr == vertex) return ON_SUBD_RETURN_ERROR(false); - vertex->ClearSavedSubdivisionPoint(); - vertex->ClearSavedLimitPoints(); + vertex->ClearSavedSubdivisionPoints(); for (unsigned int i = 1; i < component_ring_count; i++) { ON_SubDEdge* edge = component_ring[i].Edge(); if ( nullptr == edge) return ON_SUBD_RETURN_ERROR(false); - edge->ClearSavedSubdivisionPoint(); + edge->ClearSavedSubdivisionPoints(); i++; if (i >= component_ring_count) break; ON_SubDFace* face = component_ring[i].Face(); if ( nullptr == face) return ON_SUBD_RETURN_ERROR(false); - face->ClearSavedSubdivisionPoint(); + face->ClearSavedSubdivisionPoints(); } return true; } @@ -317,9 +268,6 @@ double ON_SubDMatrix::TestEvaluation() const if (!m_sector_type.IsValid()) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); - const ON_SubD::SubDType subd_type = m_sector_type.SubDType(); - //ON_SubD::VertexTag center_vertex_tag = m_sector_type.VertexTag(); - const unsigned int F = m_sector_type.FaceCount(); if (0 == F) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); @@ -399,7 +347,7 @@ double ON_SubDMatrix::TestEvaluation() const const_cast(vertex_ring[vi])->m_P[Pi] = 1.0; - if ( R != ON_SubD::GetSectorPointRing(subd_type,false,component_ring_count,component_ring,_ringP0)) + if ( R != ON_SubD::GetSectorPointRing(false,component_ring_count,component_ring,_ringP0)) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); const ON_3dPoint* ringP0 = _ringP0.Array(); @@ -416,7 +364,7 @@ double ON_SubDMatrix::TestEvaluation() const return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); } - if ( R != ON_SubD::GetSectorSubdivisionPointRing(subd_type,component_ring_count,component_ring,_ringP1)) + if ( R != ON_SubD::GetSectorSubdivisionPointRing(component_ring, component_ring_count,_ringP1)) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); const ON_3dPoint* ringP1 = _ringP1.Array(); @@ -482,12 +430,12 @@ double ON_SubDMatrix::TestEvaluation() const } static bool GetSectorLimitPointHelper( - ON_SubD::SubDType subdivision_type, const ON_SubDSectorIterator& sit, - ON_SubDSectorLimitPoint& limit_point + bool bUndefinedNormalIsPossible, + ON_SubDSectorSurfacePoint& limit_point ) { - const ON_SubDSectorType sector_type = ON_SubDSectorType::Create(subdivision_type,sit); + const ON_SubDSectorType sector_type = ON_SubDSectorType::Create(sit); if (false == sector_type.IsValid()) return ON_SUBD_RETURN_ERROR(false); @@ -508,7 +456,8 @@ static bool GetSectorLimitPointHelper( point_ring_capacity = R; } - if ( R != ON_SubD::GetSectorPointRing(subdivision_type,true,sit,point_ring_capacity,point_ring_stride,point_ring) ) + const unsigned int point_ring_count = ON_SubD::GetSectorPointRing( true, sit, point_ring, point_ring_capacity, point_ring_stride); + if ( R != point_ring_count ) return ON_SUBD_RETURN_ERROR(false); bool rc = false; @@ -518,7 +467,7 @@ static bool GetSectorLimitPointHelper( if (R != SM.m_R || nullptr == SM.m_LP) break; - if (false == SM.EvaluateLimitPoint(R, point_ring_stride, point_ring, limit_point)) + if (false == SM.EvaluateSurfacePoint(point_ring, R, point_ring_stride, bUndefinedNormalIsPossible, limit_point)) break; rc = true; @@ -533,8 +482,8 @@ static bool GetSectorLimitPointHelper( : ON_SUBD_RETURN_ERROR(false); } -static ON_SubDSectorLimitPoint* LimitPointPool( - const ON_SubDSectorLimitPoint* pReturnToPool // If nullptr, then one is allocated +static ON_SubDSectorSurfacePoint* LimitPointPool( + const ON_SubDSectorSurfacePoint* pReturnToPool // If nullptr, then one is allocated ) { static ON_FixedSizePool limit_point_fsp; @@ -542,26 +491,37 @@ static ON_SubDSectorLimitPoint* LimitPointPool( { if (nullptr != pReturnToPool) return ON_SUBD_RETURN_ERROR(nullptr); - limit_point_fsp.Create(sizeof(ON_SubDSectorLimitPoint), 0, 0); + static ON_SleepLock initialize_lock; + initialize_lock.GetLock(); + if (0 == limit_point_fsp.SizeofElement()) + limit_point_fsp.Create(sizeof(ON_SubDSectorSurfacePoint), 0, 0); + initialize_lock.ReturnLock(); } if (nullptr != pReturnToPool) { - limit_point_fsp.ReturnElement((void*)pReturnToPool); + limit_point_fsp.ThreadSafeReturnElement((void*)pReturnToPool); return nullptr; } - ON_SubDSectorLimitPoint* lp = (ON_SubDSectorLimitPoint*)limit_point_fsp.AllocateDirtyElement(); + ON_SubDSectorSurfacePoint* lp = (ON_SubDSectorSurfacePoint*)limit_point_fsp.ThreadSafeAllocateDirtyElement(); if (nullptr == lp) return ON_SUBD_RETURN_ERROR(nullptr); return lp; } -bool ON_SubDVertex::GetLimitPoint( - ON_SubD::SubDType subd_type, +bool ON_SubDVertex::GetSurfacePoint( const ON_SubDFace* sector_face, - bool bUseSavedLimitPoint, - ON_SubDSectorLimitPoint& limit_point + ON_SubDSectorSurfacePoint& limit_point +) const +{ + return GetSurfacePoint( sector_face, false, limit_point); +} + +bool ON_SubDVertex::GetSurfacePoint( + const ON_SubDFace* sector_face, + bool bUndefinedNormalIsPossible, + ON_SubDSectorSurfacePoint& limit_point ) const { bool rc = false; @@ -581,14 +541,14 @@ bool ON_SubDVertex::GetLimitPoint( if (false == rc) { // sector_face is not referenced by this vertex - limit_point = ON_SubDSectorLimitPoint::Unset; + limit_point = ON_SubDSectorSurfacePoint::Unset; return ON_SUBD_RETURN_ERROR(false); } } - if (bUseSavedLimitPoint && subd_type == SavedLimitPointType() ) + if (this->SurfacePointIsSet() ) { - if (nullptr == m_limit_point.m_sector_face) + if (nullptr == m_limit_point.m_sector_face && nullptr == m_limit_point.m_next_sector_limit_point) { // single sector limit_point = m_limit_point; @@ -599,13 +559,13 @@ bool ON_SubDVertex::GetLimitPoint( if (nullptr == sector_face) { // this vertex has multiple sectors - limit_point = ON_SubDSectorLimitPoint::Unset; + limit_point = ON_SubDSectorSurfacePoint::Unset; return ON_SUBD_RETURN_ERROR(false); } if (nullptr == sit.Initialize(sector_face, 0, this)) { - limit_point = ON_SubDSectorLimitPoint::Unset; + limit_point = ON_SubDSectorSurfacePoint::Unset; return ON_SUBD_RETURN_ERROR(false); } @@ -614,11 +574,11 @@ bool ON_SubDVertex::GetLimitPoint( if (nullptr == limit_point_sector_face) { // no creases - limit_point = ON_SubDSectorLimitPoint::Unset; + limit_point = ON_SubDSectorSurfacePoint::Unset; return ON_SUBD_RETURN_ERROR(false); } - for (const ON_SubDSectorLimitPoint* lp = &m_limit_point; nullptr != lp; lp = lp->m_next_sector_limit_point) + for (const ON_SubDSectorSurfacePoint* lp = &m_limit_point; nullptr != lp; lp = lp->m_next_sector_limit_point) { if (limit_point_sector_face == lp->m_sector_face) { @@ -634,56 +594,109 @@ bool ON_SubDVertex::GetLimitPoint( if (nullptr == (nullptr == sector_face ? sit.Initialize(this) : sit.Initialize(sector_face, 0, this))) { - limit_point = ON_SubDSectorLimitPoint::Unset; + limit_point = ON_SubDSectorSurfacePoint::Unset; return ON_SUBD_RETURN_ERROR(false); } if (nullptr == sit.Initialize(sector_face,0,this)) { - limit_point = ON_SubDSectorLimitPoint::Unset; + limit_point = ON_SubDSectorSurfacePoint::Unset; return ON_SUBD_RETURN_ERROR(false); } limit_point_sector_face = sit.IncrementToCrease(-1); - rc = GetSectorLimitPointHelper(subd_type, sit, limit_point); + rc = GetSectorLimitPointHelper( sit, bUndefinedNormalIsPossible, limit_point); if (false == rc) { - limit_point = ON_SubDSectorLimitPoint::Unset; + limit_point = ON_SubDSectorSurfacePoint::Unset; return ON_SUBD_RETURN_ERROR(false); } - - limit_point.m_sector_face = limit_point_sector_face; + + limit_point.m_sector_face = this->IsSingleSectorVertex() ? nullptr : limit_point_sector_face; - if (bUseSavedLimitPoint) - { - ON_SubDSectorLimitPoint saved_limit_point = limit_point; - saved_limit_point.m_next_sector_limit_point = (ON_SubDSectorLimitPoint*)1; // causes unnecessary test to be skipped - SetSavedLimitPoint(subd_type, saved_limit_point); - } + ON_SubDSectorSurfacePoint saved_limit_point = limit_point; + saved_limit_point.m_next_sector_limit_point = (ON_SubDSectorSurfacePoint*)1; // causes unnecessary test to be skipped + SetSavedSurfacePoint( bUndefinedNormalIsPossible, saved_limit_point); return rc; } -ON_SubD::SubDType ON_SubDVertex::GetSavedLimitPointLocation( +bool ON_SubDVertex::GetSavedSurfacePoint( double limit_point[3] ) const { - const ON_SubD::SubDType subd_type = SavedLimitPointType(); - if (ON_SubD::SubDType::Unset != subd_type) + const bool rc = SurfacePointIsSet(); + if (rc) { limit_point[0] = m_limit_point.m_limitP[0]; limit_point[1] = m_limit_point.m_limitP[1]; limit_point[2] = m_limit_point.m_limitP[2]; } - return subd_type; + return rc; +} + +const ON_3dPoint ON_SubDVertex::SurfacePoint() const +{ + ON_3dPoint limit_point(ON_3dPoint::NanPoint); + return GetSurfacePoint(&limit_point.x) ? limit_point : ON_3dPoint::NanPoint; + +} + +const ON_3dPoint ON_SubDVertex::Point(ON_SubDComponentLocation point_location) const +{ + switch (point_location) + { + case ON_SubDComponentLocation::ControlNet: + return this->ControlNetPoint(); + break; + case ON_SubDComponentLocation::Surface: + return this->SurfacePoint(); + break; + } + return ON_3dPoint::NanPoint; } + +bool ON_SubDVertex::GetSurfacePoint( + double limit_point[3] +) const +{ + if (nullptr == limit_point) + return false; + + bool rc = SurfacePointIsSet(); + if ( rc ) + { + limit_point[0] = m_limit_point.m_limitP[0]; + limit_point[1] = m_limit_point.m_limitP[1]; + limit_point[2] = m_limit_point.m_limitP[2]; + } + else + { + ON_SubDSectorSurfacePoint lp; + rc = GetSurfacePoint(Face(0), true, lp); + if (rc) + { + limit_point[0] = lp.m_limitP[0]; + limit_point[1] = lp.m_limitP[1]; + limit_point[2] = lp.m_limitP[2]; + } + else + { + limit_point[0] = ON_DBL_QNAN; + limit_point[1] = ON_DBL_QNAN; + limit_point[2] = ON_DBL_QNAN; + } + } + return rc; +} + static bool SetLimitPointSectorCheck( const ON_SubDVertex* vertex, - ON_SubDSectorLimitPoint& limit_point + ON_SubDSectorSurfacePoint& limit_point ) { const unsigned int vertex_face_count = vertex->m_face_count; @@ -749,33 +762,32 @@ static bool SetLimitPointSectorCheck( return true; } -bool ON_SubDVertex::SetSavedLimitPoint( - ON_SubD::SubDType subd_type, - ON_SubDSectorLimitPoint limit_point +bool ON_SubDVertex::SetSavedSurfacePoint( + bool bUndefinedNormalIsPossible, + ON_SubDSectorSurfacePoint limit_point ) const { const bool bSkipSectorCheck = (1U == (ON__UINT_PTR)limit_point.m_next_sector_limit_point); limit_point.m_next_sector_limit_point = nullptr; - if ( ON_SubD::SubDType::Unset != subd_type - && limit_point.IsSet() + if ( limit_point.IsSet(bUndefinedNormalIsPossible) && (bSkipSectorCheck || SetLimitPointSectorCheck(this,limit_point)) ) { if (nullptr == limit_point.m_sector_face || ON_UNSET_VALUE == m_limit_point.m_limitP[0] - || 0 == ON_SUBD_CACHE_LIMIT_FLAG(m_saved_points_flags) + || false == Internal_SurfacePointFlag() ) { // vertex has 1 sector or this is the first cached limit point - ClearSavedLimitPoints(); + this->ClearSavedSurfacePoints(); m_limit_point = limit_point; m_limit_point.m_next_sector_limit_point = nullptr; } else { // get a point from the pool - ON_SubDSectorLimitPoint* lp = LimitPointPool(nullptr); + ON_SubDSectorSurfacePoint* lp = LimitPointPool(nullptr); if ( nullptr == lp) return ON_SUBD_RETURN_ERROR(false); @@ -783,59 +795,314 @@ bool ON_SubDVertex::SetSavedLimitPoint( *lp = limit_point; // first limit point location wins - ON_SubDLimitMeshFragment::SealPoints(true, m_limit_point.m_limitP, lp->m_limitP); + ON_SubDMeshFragment::SealPoints(true, m_limit_point.m_limitP, lp->m_limitP); // it is expected that the limit normal and tangents will be substantually different. lp->m_next_sector_limit_point = nullptr; // append the point to the vertex's linked list. - const ON_SubDSectorLimitPoint* p = &m_limit_point; + const ON_SubDSectorSurfacePoint* p = &m_limit_point; while(nullptr != p->m_next_sector_limit_point) { p = p->m_next_sector_limit_point; } - const_cast(p)->m_next_sector_limit_point = lp; + const_cast(p)->m_next_sector_limit_point = lp; } - if ( subd_type != ON_SubD::SubDTypeFromUnsigned(m_saved_points_flags & ON_SUBD_CACHE_TYPE_MASK) ) - m_saved_points_flags = 0U; - - m_saved_points_flags |= (ON_SUBD_CACHE_LIMIT_FLAG_MASK | ((unsigned char)subd_type)); + Internal_SetSavedSurfacePointFlag(true); return true; } - ClearSavedLimitPoints(); - - if (ON_SubD::SubDType::Unset != subd_type) - return ON_SUBD_RETURN_ERROR(false); - - return true; + return ON_SUBD_RETURN_ERROR(false); } -void ON_SubDVertex::ClearSavedLimitPoints() const + +void ON_SubDVertex::ClearSavedSurfacePoints() const { - ON_SUBD_CACHE_CLEAR_LIMIT_FLAG(m_saved_points_flags); + // clear vertex specific cache + Internal_ClearSurfacePointFlag(); if (ON_UNSET_VALUE != m_limit_point.m_limitP[0] && nullptr != m_limit_point.m_sector_face) { // return multiple sector limit points to pool - const ON_SubDSectorLimitPoint* next_p = m_limit_point.m_next_sector_limit_point; - for (const ON_SubDSectorLimitPoint* p = next_p; nullptr != p; p = next_p) + const ON_SubDSectorSurfacePoint* next_p = m_limit_point.m_next_sector_limit_point; + for (const ON_SubDSectorSurfacePoint* p = next_p; nullptr != p; p = next_p) { next_p = p->m_next_sector_limit_point; LimitPointPool(p); } } - m_limit_point = ON_SubDSectorLimitPoint::Unset; + m_limit_point = ON_SubDSectorSurfacePoint::Unset; } -ON_SubD::SubDType ON_SubDVertex::SavedLimitPointType() const +void ON_SubDVertex::ClearSavedSubdivisionPoints() const { - return - (0 != (ON_SUBD_CACHE_LIMIT_FLAG_MASK & m_saved_points_flags)) - ? ((ON_SubD::SubDType)ON_SUBD_CACHE_TYPE(m_saved_points_flags)) - : ON_SubD::SubDType::Unset; + // clear vertex specific limit point cache + ClearSavedSurfacePoints(); + // clear general subdivision point cache + ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags(); } -#endif +bool ON_SubDVertex::SurfacePointIsSet() const +{ + const bool rc = Internal_SurfacePointFlag(); + if (false == rc) + { + this->ClearSavedSurfacePoints(); + } + return rc; +} + + +void ON_SubDEdge::ClearSavedSubdivisionPoints() const +{ + // considering using a global pool for the limit curve cache - not yet. + ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags(); +} + +const ON_SubDMeshFragment * ON_SubDFace::MeshFragments() const +{ + // NOTE: + // Clearing the ON_SubDComponentBase::SavedPointsFlags::SurfacePointBit bit + // on m_saved_points_flags is used to mark mesh fragmants as dirty. + // They need to be regerated before being used. + return (0 != ON_SUBD_CACHE_LIMITLOC_FLAG(m_saved_points_flags)) ? m_mesh_fragments : nullptr; +} + +void ON_SubDFace::ClearSavedSubdivisionPoints() const +{ + // considering using a global pool for the limit fragment cache - not yet. + ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags(); +} + +const ON_3dPoint ON_SubDFace::ControlNetCenterPoint() const +{ + if (m_edge_count < 3) + return ON_3dPoint::NanPoint; + ON_3dPoint center(0.0, 0.0, 0.0); + const ON_SubDEdgePtr* eptr = m_edge4; + for (unsigned short fvi = 0; fvi < m_edge_count; ++fvi, ++eptr) + { + if (4 == fvi) + { + eptr = m_edgex; + if ( nullptr == eptr) + return ON_3dPoint::NanPoint; + } + const ON_SubDVertex* v = eptr->RelativeVertex(0); + if ( nullptr == v) + return ON_3dPoint::NanPoint; + center += v->ControlNetPoint(); + } + const double c = (double)m_edge_count; + center.x /= c; + center.y /= c; + center.z /= c; + return center; +} + +const ON_3dVector ON_SubDFace::ControlNetCenterNormal() const +{ + if (4 == m_edge_count) + { + // faster code for most common case + const ON_SubDVertex* v[4] = { m_edge4[0].RelativeVertex(0),m_edge4[1].RelativeVertex(0),m_edge4[2].RelativeVertex(0),m_edge4[3].RelativeVertex(0)}; + if (nullptr == v[0] || nullptr == v[1] || nullptr == v[2] || nullptr == v[3]) + return ON_3dVector::NanVector; + const ON_3dVector A = ON_3dPoint(v[2]->m_P) - ON_3dPoint(v[0]->m_P); + const ON_3dVector B = ON_3dPoint(v[3]->m_P) - ON_3dPoint(v[1]->m_P); + const ON_3dVector N = ON_CrossProduct(A, B); + return N.UnitVector(); + } + + if (3 == m_edge_count) + { + // faster code for 2nd most common case + const ON_SubDVertex* v[3] = { m_edge4[0].RelativeVertex(0),m_edge4[1].RelativeVertex(0),m_edge4[2].RelativeVertex(0)}; + if (nullptr == v[0] || nullptr == v[1] || nullptr == v[2]) + return ON_3dVector::NanVector; + const ON_3dPoint C0(v[0]->m_P); + const ON_3dVector A = ON_3dPoint(v[1]->m_P) - C0; + const ON_3dVector B = ON_3dPoint(v[2]->m_P) - C0; + const ON_3dVector N = ON_CrossProduct(A, B); + return N.UnitVector(); + } + + // less common cases + const ON_3dPoint C = ControlNetCenterPoint(); + if (false == (C.x == C.x)) + return ON_3dVector::NanVector; + + // If the face is planar (convex or non-convex), this will return + // the plane's normal with the correct orientation. + // Otherwise this will return a good choice of normal for + // a non-planar face. + ON_3dVector A; + ON_3dVector B = ControlNetPoint(m_edge_count - 1) - C; + ON_3dVector N = ON_3dVector::ZeroVector; + for (unsigned short fvi = 0; fvi < m_edge_count; ++fvi) + { + A = B; + B = ControlNetPoint(fvi) - C; + N += ON_CrossProduct(A, B); // do not untize this and then the code works for non-convex faces too. + } + + return N.UnitVector(); +} + +bool ON_SubDFace::IsConvex() const +{ + const ON_3dVector N = ControlNetCenterNormal(); + if (N.IsNotZero()) + { + if (3 == m_edge_count) + return true; + ON_3dPoint P = ControlNetPoint(m_edge_count - 2); + ON_3dPoint Q = ControlNetPoint(m_edge_count - 1); + ON_3dVector A; + ON_3dVector B = Q - P; + for (unsigned short fvi = 0; fvi < m_edge_count; ++fvi) + { + P = Q; + Q = ControlNetPoint(fvi); + A = B; + B = Q - P; + const ON_3dVector AxB = ON_CrossProduct(A, B); + const double d = AxB*N; + if ( false == (d > 0.0) && false == AxB.IsZero() ) + return false; + } + return true; + } + return false; +} + +bool ON_SubDFace::IsNotConvex() const +{ + const ON_3dVector N = ControlNetCenterNormal(); + if (N.IsNotZero()) + { + if (3 == m_edge_count) + return false; + bool bIsNotConvex = false; + ON_3dPoint P = ControlNetPoint(m_edge_count - 2); + ON_3dPoint Q = ControlNetPoint(m_edge_count - 1); + ON_3dVector A; + ON_3dVector B = Q - P; + for (unsigned short fvi = 0; fvi < m_edge_count; ++fvi) + { + P = Q; + Q = ControlNetPoint(fvi); + A = B; + B = Q - P; + const ON_3dVector AxB = ON_CrossProduct(A, B); + const double d = AxB * N; + if (false == (d == d)) + return false; // nan + if (false == (d > 0.0) && false == AxB.IsZero()) + bIsNotConvex = true; + } + return bIsNotConvex; + } + return false; +} + +bool ON_SubDFace::IsPlanar(double planar_tolerance) const +{ + const ON_3dPoint C = ControlNetCenterPoint(); + const ON_3dVector N = ControlNetCenterNormal(); + if (C.IsValid() && N.IsNotZero()) + { + if (3 == m_edge_count) + return true; + if (false == (planar_tolerance >= 0.0)) + planar_tolerance = ON_ZERO_TOLERANCE; + const ON_SubDEdgePtr* eptr = m_edge4; + double d0 = 0.0; + double d1 = 0.0; + for (unsigned short fvi = 0; fvi < m_edge_count; ++fvi, ++eptr) + { + if (4 == fvi) + { + eptr = m_edgex; + if (nullptr == eptr) + return false; + } + const ON_SubDVertex* v = eptr->RelativeVertex(0); + if (nullptr == v) + return false; + const double d = (ON_3dPoint(v->m_P) - C)*N; + if (false == (d == d)) + return false; // nan + if (d < d0) + d0 = d; + else if (d > d1) + d1 = d; + else + continue; + if ((d1 - d0) > planar_tolerance) + return false; + } + return true; + } + return false; +} + + +bool ON_SubDFace::IsNotPlanar(double planar_tolerance) const +{ + const ON_3dPoint C = ControlNetCenterPoint(); + const ON_3dVector N = ControlNetCenterNormal(); + if (C.IsValid() && N.IsNotZero()) + { + if (3 == m_edge_count) + return false; + if (false == (planar_tolerance >= 0.0)) + planar_tolerance = ON_ZERO_TOLERANCE; + const ON_SubDEdgePtr* eptr = m_edge4; + double d0 = 0.0; + double d1 = 0.0; + bool bNotPlanar = false; + for (unsigned short fvi = 0; fvi < m_edge_count; ++fvi, ++eptr) + { + if (4 == fvi) + { + eptr = m_edgex; + if (nullptr == eptr) + return false; + } + const ON_SubDVertex* v = eptr->RelativeVertex(0); + if (nullptr == v) + return false; + const double d = (ON_3dPoint(v->m_P) - C)*N; + if (false == (d == d)) + return false; // nan + if (d < d0) + d0 = d; + else if (d > d1) + d1 = d; + else + continue; + if ((d1 - d0) > planar_tolerance) + bNotPlanar = true; + } + return bNotPlanar; + } + return false; +} + + +const ON_Plane ON_SubDFace::ControlNetCenterFrame() const +{ + const ON_3dPoint C = ControlNetCenterPoint(); + const ON_3dVector N = ControlNetCenterNormal(); + if (C.IsValid() && N.IsNotZero()) + { + // TODO - better choices for x and y axes + ON_Plane center_frame; + if (center_frame.CreateFromNormal(C, N)) + return center_frame; + } + return ON_Plane::NanPlane; +} diff --git a/opennurbs_subd_fragment.cpp b/opennurbs_subd_fragment.cpp index 798b84af..d5424362 100644 --- a/opennurbs_subd_fragment.cpp +++ b/opennurbs_subd_fragment.cpp @@ -3,95 +3,448 @@ #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 +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined // and the opennurbs .h files alter what is declared and how it is declared. #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif #include "opennurbs_subd_data.h" -#if defined(OPENNURBS_SUBD_WIP) +/* +// +// Copyright (c) 1993-2019 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 . +// +//////////////////////////////////////////////////////////////// +*/ ///////////////////////////////////////////////////////////////////////////////////////// // -// ON_SubDLimitMeshFragment -// +// ON_SubDMeshFragment +// -unsigned int ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity( +void ON_SubDMeshFragment::Internal_Set3dPointArrayToNan(double* a, size_t a_count, size_t a_stride) +{ + if (nullptr == a || a_count <= 0 || a_stride < 3) + return; + double *a2 = a + a_count * a_stride; + if (3 == a_stride) + { + while (a < a2) + { + *a++ = ON_DBL_QNAN; + *a++ = ON_DBL_QNAN; + *a++ = ON_DBL_QNAN; + } + } + else + { + a_stride -= 3; + while (a < a2) + { + *a++ = ON_DBL_QNAN; + *a++ = ON_DBL_QNAN; + *a++ = ON_DBL_QNAN; + a += a_stride; + } + } +} + + +unsigned ON_SubDMeshFragment::VertexCapacity() const +{ + return (m_vertex_capacity_etc & ON_SubDMeshFragment::ValueMask); +} + +bool ON_SubDMeshFragment::ManagedArrays() const +{ + const size_t capacity = (ON_SubDMeshFragment::ValueMask & m_vertex_capacity_etc); + return ( + 0 != (ON_SubDMeshFragment::EtcManagedArraysBit & m_vertex_capacity_etc) + && capacity > 0 + && 3 == m_P_stride && 3 == m_N_stride && 3 == m_T_stride + && nullptr != m_P + && m_N == m_P + (capacity * 3) + && m_T == m_N + (capacity * 3) + ) ? true : false; +} + +bool ON_SubDMeshFragment::DeleteManagedArrays() +{ + if (ManagedArrays()) + { + void* ptr = m_P; + m_vertex_count_etc &= ON_SubDMeshFragment::EtcMask; + m_vertex_capacity_etc &= ON_SubDMeshFragment::EtcMask; + m_vertex_capacity_etc &= ~ON_SubDMeshFragment::EtcManagedArraysBit; + m_P = nullptr; + m_N = nullptr; + m_T = nullptr; + m_P_stride = 0; + m_N_stride = 0; + m_T_stride = 0; + onfree(ptr); + return true; + } + return false; +} + +bool ON_SubDMeshFragment::UnmanagedArrays() const +{ + return + (0 == (ON_SubDMeshFragment::EtcManagedArraysBit & m_vertex_capacity_etc) && 0 != (m_vertex_capacity_etc & ON_SubDMeshFragment::ValueMask) ) + ? true + : false; +} + +bool ON_SubDMeshFragment::SetUnmanagedVertexCapacity(size_t vertex_capacity) +{ + if (vertex_capacity < 0 || vertex_capacity > (size_t)ON_SubDMeshFragment::MaximumVertexCount) + return ON_SUBD_RETURN_ERROR(false); + if (ManagedArrays()) + { + // attempting to internally managed memory to externally managed memory + return ON_SUBD_RETURN_ERROR(false); + } + unsigned short etc = m_vertex_capacity_etc &= ON_SubDMeshFragment::EtcMask; + etc &= ~ON_SubDMeshFragment::EtcManagedArraysBit; + m_vertex_capacity_etc = ((unsigned short)vertex_capacity) | etc; + return true; +} + +bool ON_SubDMeshFragment::ReserveManagedVertexCapacity(size_t vertex_capacity) +{ + if (vertex_capacity < 0 || vertex_capacity >(size_t)ON_SubDMeshFragment::MaximumVertexCount) + return ON_SUBD_RETURN_ERROR(false); + if (vertex_capacity > (size_t)(ON_SubDMeshFragment::ValueMask)) + return ON_SUBD_RETURN_ERROR(false); // too big + if (UnmanagedArrays()) + { + // attempting to convert externally managed memory to internally managed memory. + return ON_SUBD_RETURN_ERROR(false); + } + const size_t current_capacity = (size_t)(ON_SubDMeshFragment::ValueMask & m_vertex_capacity_etc); + if (ManagedArrays()) + { + if (current_capacity >= vertex_capacity) + return true; + DeleteManagedArrays(); + } + unsigned short etc = m_vertex_capacity_etc &= ON_SubDMeshFragment::EtcMask; + etc |= ON_SubDMeshFragment::EtcManagedArraysBit; + double* p = (double*)onmalloc(9 * vertex_capacity); + m_P = p; + m_P_stride = 3; + m_N = m_P + (vertex_capacity*m_P_stride); + m_N_stride = 3; + m_T = m_N + (vertex_capacity*m_N_stride); + m_T_stride = 3; + return true; +} + +unsigned ON_SubDMeshFragment::VertexCount() const +{ + return (m_vertex_count_etc & ON_SubDMeshFragment::ValueMask); +} + +bool ON_SubDMeshFragment::SetVertexCount(size_t vertex_count) +{ + if (vertex_count < 0 || vertex_count > VertexCapacity()) + return ON_SUBD_RETURN_ERROR(false); + unsigned short etc = m_vertex_count_etc & ON_SubDMeshFragment::EtcMask; + m_vertex_count_etc = ((unsigned short)vertex_count) | etc; + return true; +} + +unsigned int ON_SubDMeshFragment::PointCount() const +{ + return (nullptr != m_P && m_P_stride >= 3) ? VertexCount() : 0U; +} + +unsigned int ON_SubDMeshFragment::NormalCount() const +{ + return (nullptr != m_N && m_N_stride >= 3) ? VertexCount() : 0U; +} + +unsigned int ON_SubDMeshFragment::PointCapacity() const +{ + return (nullptr != m_P && m_P_stride >= 3) ? VertexCapacity() : 0U; +} + +unsigned int ON_SubDMeshFragment::NormalCapacity() const +{ + return (nullptr != m_N && m_N_stride >= 3) ? VertexCapacity() : 0U; +} + +const double* ON_SubDMeshFragment::PointArray(ON_SubDComponentLocation subd_appearance)const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? &m_ctrlnetP[0][0] : m_P; +} + +size_t ON_SubDMeshFragment::PointArrayStride(ON_SubDComponentLocation subd_appearance)const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 3 : m_P_stride; +} + +unsigned ON_SubDMeshFragment::PointArrayCount(ON_SubDComponentLocation subd_appearance) const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 4U : PointCount(); +} + +const double* ON_SubDMeshFragment::NormalArray(ON_SubDComponentLocation subd_appearance)const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? m_ctrlnetN : m_N; +} + +size_t ON_SubDMeshFragment::NormalArrayStride(ON_SubDComponentLocation subd_appearance)const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 0 : m_N_stride; +} + +unsigned ON_SubDMeshFragment::NormalArrayCount(ON_SubDComponentLocation subd_appearance) const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 4U : NormalCount(); +} + +const ON_3dPoint ON_SubDMeshFragment::ControlNetQuadPoint( + bool bGridOrder, + unsigned point_index +) const +{ + if (point_index >= 4 || 0 == (ON_SubDMeshFragment::EtcControlNetQuadBit & m_vertex_count_etc)) + return ON_3dPoint::NanPoint; + if (false == bGridOrder) + { + if (2 == point_index) + point_index = 3; + else if (3 == point_index) + point_index = 2; + } + return ON_3dPoint(m_ctrlnetP[point_index]); +} + +const ON_SubDMeshFragment ON_SubDMeshFragment::ControlNetQuadFragmentForExperts() const +{ + ON_SubDMeshFragment q(*this); + for (;;) + { + if ((m_vertex_count_etc & ON_SubDMeshFragment::ValueMask) < 4) + break; + if ( 0 == (m_vertex_count_etc & ON_SubDMeshFragment::EtcControlNetQuadBit) ) + break; + + q.m_vertex_count_etc = 4; + q.m_vertex_count_etc |= ON_SubDMeshFragment::EtcControlNetQuadBit; + q.m_vertex_count_etc |= (ON_SubDMeshFragment::EtcTextureCoordinatesExistBit&m_vertex_count_etc); + + q.m_vertex_capacity_etc = 4; + + // 4 quad corner points in grid order with stride = 3 + q.m_P = const_cast(&m_ctrlnetP[0][0]); + q.m_P_stride = 3; + + // 4 identical normals with stride = 0 + q.m_N = const_cast(&m_ctrlnetN[0]); + q.m_N_stride = 0; + + // 4 quad corner texture coordinates in grid order with stride = 3 + q.m_T = const_cast(&m_ctrlnetT[0][0]); + q.m_T_stride = 3; + + // The grid is a single quad + q.m_grid = ON_SubDMeshFragmentGrid::OneQuadGrid; + + // both bounding boxes are equal. + q.m_surface_bbox = ControlNetQuadBoundingBox(); + return q; + } + + memset(&q, 0, sizeof(q)); + return ON_SUBD_RETURN_ERROR(q); +} + +bool ON_SubDMeshFragment::GetControlNetQuad( + bool bGridOrder, + ON_3dPoint quad_points[4], + ON_3dVector& quad_normal +) const +{ + const bool rc = 0 != (ON_SubDMeshFragment::EtcControlNetQuadBit & m_vertex_count_etc) ? true : false; + if (nullptr != quad_points) + { + if (rc) + { + int i; + quad_points[0] = ON_3dPoint(m_ctrlnetP[0]); + quad_points[1] = ON_3dPoint(m_ctrlnetP[1]); + i = bGridOrder ? 2 : 3; + quad_points[i] = ON_3dPoint(m_ctrlnetP[2]); + i = bGridOrder ? 3 : 2; + quad_points[i] = ON_3dPoint(m_ctrlnetP[3]); + quad_normal = ON_3dVector(m_ctrlnetN); + } + else + { + for (int i = 0; i < 4; i++) + quad_points[i] = ON_3dPoint::NanPoint; + quad_normal = ON_3dVector::ZeroVector; + } + } + return rc; +} + +const ON_SubDMeshFragmentGrid& ON_SubDMeshFragment::Grid(ON_SubDComponentLocation subd_appearance) const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? ON_SubDMeshFragmentGrid::OneQuadGrid : m_grid; +} + +void ON_SubDMeshFragment::SetControlNetQuad(bool bGridOrder, const ON_3dPoint quad_points[4], ON_3dVector quad_normal ) +{ + if (nullptr != quad_points && quad_points[0].IsValid() && quad_points[1].IsValid() && quad_points[2].IsValid() && quad_points[3].IsValid() && quad_normal.IsNotZero()) + { + int i; + + m_ctrlnetP[0][0] = quad_points[0].x; + m_ctrlnetP[0][1] = quad_points[0].y; + m_ctrlnetP[0][2] = quad_points[0].z; + + m_ctrlnetP[1][0] = quad_points[1].x; + m_ctrlnetP[1][1] = quad_points[1].y; + m_ctrlnetP[1][2] = quad_points[1].z; + + i = bGridOrder ? 2 : 3; + m_ctrlnetP[i][0] = quad_points[2].x; + m_ctrlnetP[i][1] = quad_points[2].y; + m_ctrlnetP[i][2] = quad_points[2].z; + + i = bGridOrder ? 3 : 2; + m_ctrlnetP[i][0] = quad_points[3].x; + m_ctrlnetP[i][1] = quad_points[3].y; + m_ctrlnetP[i][2] = quad_points[3].z; + + m_ctrlnetN[0] = quad_normal.x; + m_ctrlnetN[1] = quad_normal.y; + m_ctrlnetN[2] = quad_normal.z; + m_vertex_count_etc |= ON_SubDMeshFragment::EtcControlNetQuadBit; + } + else + { + UnsetControlNetQuad(); + } +} + +void ON_SubDMeshFragment::UnsetControlNetQuad() +{ + for (int i = 0; i < 4; ++i) + { + m_ctrlnetP[i][0] = m_ctrlnetP[i][1] = m_ctrlnetP[i][2] = ON_DBL_QNAN; + } + m_ctrlnetN[0] = m_ctrlnetN[1] = m_ctrlnetN[2] = ON_DBL_QNAN; + m_vertex_count_etc &= ~ON_SubDMeshFragment::EtcControlNetQuadBit; +} + +const ON_BoundingBox ON_SubDMeshFragment::SurfaceBoundingBox() const +{ + return m_surface_bbox; +} + +const ON_BoundingBox ON_SubDMeshFragment::ControlNetQuadBoundingBox() const +{ + ON_3dPoint P[4]; + ON_3dVector N; + if (this->GetControlNetQuad(false, P, N)) + { + ON_BoundingBox bbox; + bbox.Set(3, 0, 4, 3, &P[0].x, false); + return bbox; + } + return ON_BoundingBox::NanBoundingBox; +} + +const ON_BoundingBox ON_SubDMeshFragment::BoundingBox(ON_SubDComponentLocation subd_appearance) const +{ + return + (ON_SubDComponentLocation::ControlNet == subd_appearance) + ? ControlNetQuadBoundingBox() + : SurfaceBoundingBox(); +} + + +unsigned int ON_SubDMeshFragment::SideSegmentCountFromDisplayDensity( unsigned int display_density ) { - if (display_density <= ON_SubDLimitMesh::MaximumDisplayDensity) + if (display_density <= ON_SubDDisplayParameters::MaximumDensity) return (1 << display_density); - + return ON_SUBD_RETURN_ERROR(0); } -unsigned int ON_SubDLimitMeshFragment::DisplayDensityFromSideSegmentCount( +unsigned int ON_SubDMeshFragment::DisplayDensityFromSideSegmentCount( unsigned int side_segment_count ) { unsigned int display_density; - for (display_density = 0; display_density < ON_SubDLimitMesh::MaximumDisplayDensity; display_density++) + for (display_density = 0; display_density < ON_SubDDisplayParameters::MaximumDensity; display_density++) { if ( 1U << display_density >= side_segment_count ) break; } - if ( 1U << display_density == side_segment_count ) + if ( 1U << display_density == side_segment_count ) return display_density; return ON_SUBD_RETURN_ERROR(display_density); } -unsigned int ON_SubDLimitMeshFragment::PointCountFromDisplayDensity( - ON_SubD::FacetType facet_type, +unsigned int ON_SubDMeshFragment::PointCountFromDisplayDensity( unsigned int mesh_density ) { - unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); + unsigned int count = ON_SubDMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); if ( 0 == count) return 0; - if ( ON_SubD::FacetType::Quad == facet_type ) - return (count + 1)*(count+1); - - if ( ON_SubD::FacetType::Tri == facet_type ) - return ((count+1)*(count+2))/2; - - return ON_SUBD_RETURN_ERROR(0); + return (count + 1)*(count+1); } -unsigned int ON_SubDLimitMeshFragment::FaceCountFromDisplayDensity( +unsigned int ON_SubDMeshFragment::FaceCountFromDisplayDensity( unsigned int mesh_density ) { - unsigned int count = ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); + unsigned int count = ON_SubDMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); return count*count; // same for quads or tris } ///////////////////////////////////////////////////////////////////////////////////////// // -// ON_SubDManagedLimitMeshFragment -// +// ON_SubDManagedMeshFragment +// -ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT +ON_SubDManagedMeshFragment::ON_SubDManagedMeshFragment() ON_NOEXCEPT { memset(this, 0, sizeof(*this)); } -ON_SubDManagedLimitMeshFragment::~ON_SubDManagedLimitMeshFragment() ON_NOEXCEPT +ON_SubDManagedMeshFragment::~ON_SubDManagedMeshFragment() ON_NOEXCEPT { Destroy(); } -ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment(const ON_SubDManagedLimitMeshFragment& src) ON_NOEXCEPT +ON_SubDManagedMeshFragment::ON_SubDManagedMeshFragment(const ON_SubDManagedMeshFragment& src) ON_NOEXCEPT { Clear(); CopyHelper(src); } -ON_SubDManagedLimitMeshFragment& ON_SubDManagedLimitMeshFragment::operator=(const ON_SubDManagedLimitMeshFragment& src) ON_NOEXCEPT +ON_SubDManagedMeshFragment& ON_SubDManagedMeshFragment::operator=(const ON_SubDManagedMeshFragment& src) ON_NOEXCEPT { if (this != &src) { @@ -103,7 +456,7 @@ ON_SubDManagedLimitMeshFragment& ON_SubDManagedLimitMeshFragment::operator=(cons #if defined(ON_HAS_RVALUEREF) // rvalue copy constructor -ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment( ON_SubDManagedLimitMeshFragment&& src ) ON_NOEXCEPT +ON_SubDManagedMeshFragment::ON_SubDManagedMeshFragment( ON_SubDManagedMeshFragment&& src ) ON_NOEXCEPT { memcpy(this, &src, sizeof(*this)); src.m_storage = nullptr; @@ -111,7 +464,7 @@ ON_SubDManagedLimitMeshFragment::ON_SubDManagedLimitMeshFragment( ON_SubDManaged } // rvalue assignment operator -ON_SubDManagedLimitMeshFragment& ON_SubDManagedLimitMeshFragment::operator=( ON_SubDManagedLimitMeshFragment&& src ) ON_NOEXCEPT +ON_SubDManagedMeshFragment& ON_SubDManagedMeshFragment::operator=( ON_SubDManagedMeshFragment&& src ) ON_NOEXCEPT { if (this != &src) { @@ -125,14 +478,16 @@ ON_SubDManagedLimitMeshFragment& ON_SubDManagedLimitMeshFragment::operator=( ON_ #endif -bool ON_SubDManagedLimitMeshFragment::ReserveCapacity( - ON_SubD::FacetType facet_type, - unsigned int mesh_density +bool ON_SubDManagedMeshFragment::ReserveCapacity( + unsigned int display_density ) ON_NOEXCEPT { Clear(); - unsigned int fragment_side_count = ON_SubDManagedLimitMeshFragment::SideSegmentCountFromDisplayDensity(mesh_density); + if ( display_density > 8) + return ON_SUBD_RETURN_ERROR(false); + + unsigned int fragment_side_count = ON_SubDManagedMeshFragment::SideSegmentCountFromDisplayDensity(display_density); if ( 0 == fragment_side_count ) return true; @@ -142,18 +497,19 @@ bool ON_SubDManagedLimitMeshFragment::ReserveCapacity( const unsigned int short_capacity_sanity_check = 0xFFFFU; - const unsigned int P_capacity = ON_SubDManagedLimitMeshFragment::PointCountFromDisplayDensity(facet_type,mesh_density); - if ( P_capacity >= short_capacity_sanity_check ) + const unsigned int V_capacity = ON_SubDManagedMeshFragment::PointCountFromDisplayDensity(display_density); + if (V_capacity >= short_capacity_sanity_check ) return ON_SUBD_RETURN_ERROR(false); - const unsigned int F_capacity = ON_SubDManagedLimitMeshFragment::FaceCountFromDisplayDensity(mesh_density); + const unsigned int F_capacity = ON_SubDManagedMeshFragment::FaceCountFromDisplayDensity(display_density); if ( F_capacity >= short_capacity_sanity_check) return ON_SUBD_RETURN_ERROR(false); const size_t P_stride = 3; const size_t N_stride = 3; + const size_t T_stride = 3; - size_t storage_capacity = (P_stride + N_stride)*P_capacity; + size_t storage_capacity = (P_stride + N_stride + T_stride)*V_capacity; if (storage_capacity > m_storage_capacity || nullptr == m_storage) { if (m_storage_capacity > 0 && nullptr != m_storage) @@ -168,27 +524,33 @@ bool ON_SubDManagedLimitMeshFragment::ReserveCapacity( m_storage_capacity = storage_capacity; } - m_P_capacity = (unsigned short)P_capacity; + UnsetControlNetQuad(); + SetUnmanagedVertexCapacity(V_capacity); + SetVertexCount(V_capacity); m_P_stride = P_stride; m_N_stride = N_stride; + m_T_stride = T_stride; m_P = m_storage; - m_N = m_P + (3*P_capacity); + m_N = m_storage + (3*V_capacity); + m_T = m_storage + (6*V_capacity); - m_grid = ON_SubDLimitMeshFragmentGrid::Facets(facet_type,fragment_side_count,0U); + m_surface_bbox = ON_BoundingBox::NanBoundingBox; + + m_grid = ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity(display_density,0U); if ( nullptr == m_grid.m_F) return ON_SUBD_RETURN_ERROR(false); return true; } -void ON_SubDManagedLimitMeshFragment::Clear() ON_NOEXCEPT +void ON_SubDManagedMeshFragment::Clear() ON_NOEXCEPT { - memset(this, 0, sizeof(ON_SubDLimitMeshFragment)); + memset(this, 0, sizeof(ON_SubDMeshFragment)); } -void ON_SubDManagedLimitMeshFragment::Destroy() ON_NOEXCEPT +void ON_SubDManagedMeshFragment::Destroy() ON_NOEXCEPT { double* p = const_cast(m_P); memset(this, 0, sizeof(*this)); @@ -196,65 +558,154 @@ void ON_SubDManagedLimitMeshFragment::Destroy() ON_NOEXCEPT delete[] p; } -void ON_SubDManagedLimitMeshFragment::CopyHelper(const ON_SubDLimitMeshFragment& src) +void ON_SubDManagedMeshFragment::CopyHelper(const ON_SubDMeshFragment& src) { - unsigned short P_count = (nullptr != src.m_P && src.m_P_stride >= 3) ? src.m_P_count : 0U; - unsigned short N_count = (nullptr != src.m_N && src.m_N_stride >= 3) ? src.m_P_count : 0U; + if (this == &src) + return; - const size_t P_stride = 3; - const size_t N_stride = 3; + m_vertex_capacity_etc = 0; + m_vertex_count_etc = 0; + m_P = nullptr; + m_P_stride = 0; + m_N = nullptr; + m_N_stride = 0; + m_T = nullptr; + m_T_stride = 0; + m_surface_bbox = ON_BoundingBox::NanBoundingBox; + const size_t V_count = src.VertexCount(); + if (0 == V_count) + return; - size_t storage_capacity = P_stride*P_count + N_stride*N_count; - if (storage_capacity > 0) + const unsigned P_count = src.PointCount(); + if (0 != P_count && V_count != P_count) { - double* p = new(std::nothrow) double[storage_capacity]; - if (nullptr == p) - { - ON_SubDIncrementErrorCount(); - return; - } - m_storage = p; - m_storage_capacity = storage_capacity; - if (P_count > 0) - { - m_P = p; - m_P_count = P_count; - m_P_stride = P_stride; - double* p1 = p + (m_P_stride*P_count); - const double* srcP = src.m_P; - while (p < p1) - { - p[0] = srcP[0]; - p[1] = srcP[1]; - p[2] = srcP[2]; - p += P_stride; - srcP += src.m_P_stride; - } - } - if (N_count > 0) - { - m_N = p; - m_P_count = N_count; // correct m_P_count counts both m_P and m_N. - m_N_stride = N_stride; - double* p1 = p + (m_N_stride*N_count); - const double* srcN = src.m_N; - while (p < p1) - { - p[0] = srcN[0]; - p[1] = srcN[1]; - p[2] = srcN[2]; - p += N_stride; - srcN += src.m_N_stride; - } - } - - m_grid = src.m_grid; + ON_SUBD_ERROR("invalid counts"); + return; } + + const unsigned N_count = src.NormalCount(); + if (0 != N_count && V_count != N_count) + { + ON_SUBD_ERROR("invalid counts"); + return; + } + + const unsigned T_count = src.TextureCoordinateCount(); + if (0 != T_count && V_count != T_count) + { + ON_SUBD_ERROR("invalid counts"); + return; + } + + m_ctrlnetT[0][0] = src.m_ctrlnetT[0][0]; + m_ctrlnetT[0][1] = src.m_ctrlnetT[0][1]; + m_ctrlnetT[1][0] = src.m_ctrlnetT[1][0]; + m_ctrlnetT[1][1] = src.m_ctrlnetT[1][1]; + m_ctrlnetT[2][0] = src.m_ctrlnetT[2][0]; + m_ctrlnetT[2][1] = src.m_ctrlnetT[2][1]; + m_ctrlnetT[3][0] = src.m_ctrlnetT[3][0]; + m_ctrlnetT[3][1] = src.m_ctrlnetT[3][1]; + m_surface_bbox = src.m_surface_bbox; + m_grid = src.m_grid; + + const size_t storage_capacity = 9U * V_count; + m_storage = new(std::nothrow) double[storage_capacity]; + if (nullptr == m_storage) + { + ON_SubDIncrementErrorCount(); + return; + } + m_storage_capacity = storage_capacity; + SetUnmanagedVertexCapacity(V_count); + SetVertexCount(V_count); + + double buffer3[3]; + const double* src_p; + size_t src_p_stride; + double* p; + double* p1; + + m_P = m_storage; + m_P_stride = 3; + p = m_P; + p1 = p + (m_P_stride*V_count); + if ( P_count > 0 ) + { + src_p = src.m_P; + src_p_stride = src.m_P_stride; + } + else + { + buffer3[0] = buffer3[1] = buffer3[2] = ON_DBL_QNAN; + src_p = buffer3; + src_p_stride = 0; + } + while (p < p1) + { + p[0] = src_p[0]; + p[1] = src_p[1]; + p[2] = src_p[2]; + p += m_P_stride; + src_p += src_p_stride; + } + + if (N_count > 0) + { + src_p = src.m_N; + src_p_stride = src.m_N_stride; + } + else + { + buffer3[0] = buffer3[1] = buffer3[2] = 0.0; + src_p = buffer3; + src_p_stride = 0; + } + m_N = m_storage + (3* V_count); + m_N_stride = 3; + p = m_N; + p1 = p + (m_N_stride*V_count); + while (p < p1) + { + p[0] = src_p[0]; + p[1] = src_p[1]; + p[2] = src_p[2]; + p += m_N_stride; + src_p += src_p_stride; + } + + if (T_count > 0) + { + src_p = src.m_T; + src_p_stride = src.m_T_stride; + } + else + { + buffer3[0] = buffer3[1] = buffer3[2] = ON_DBL_QNAN; + src_p = buffer3; + src_p_stride = 0; + } + m_T = m_storage + (6*V_count); + m_T_stride = 3; + p = m_T; + p1 = p + (m_T_stride*V_count); + while (p < p1) + { + p[0] = src_p[0]; + p[1] = src_p[1]; + p[2] = src_p[2]; + p += m_T_stride; + src_p += src_p_stride; + } + + ON_3dPoint quad_points[4]; + ON_3dVector quad_normal; + src.GetControlNetQuad(false, quad_points, quad_normal); + SetControlNetQuad(false, quad_points, quad_normal); } -ON_SubDManagedLimitMeshFragment ON_SubDManagedLimitMeshFragment::Create(const ON_SubDLimitMeshFragment& src) ON_NOEXCEPT +ON_SubDManagedMeshFragment ON_SubDManagedMeshFragment::Create(const ON_SubDMeshFragment& src) ON_NOEXCEPT { - ON_SubDManagedLimitMeshFragment mf; + ON_SubDManagedMeshFragment mf; mf.CopyHelper(src); return mf; } @@ -262,30 +713,30 @@ ON_SubDManagedLimitMeshFragment ON_SubDManagedLimitMeshFragment::Create(const ON ///////////////////////////////////////////////////////////////////////////////////////// // -// ON_SubDLimitMeshFragmentGrid -// +// ON_SubDMeshFragmentGrid +// -unsigned int ON_SubDLimitMeshFragmentGrid::SetQuads( - unsigned int side_segment_count, - unsigned int level_of_detail, - size_t quad_capacity, - size_t quad_stride, - unsigned int* quads, - size_t side_capacity, - size_t side_stride, - unsigned int* sides - ) +unsigned int ON_SubDMeshFragmentGrid::SetQuads( + unsigned int side_segment_count, + unsigned int level_of_detail, + unsigned int* quads, + size_t quad_capacity, + size_t quad_stride, + unsigned int* sides, + size_t side_capacity, + size_t side_stride +) { - if ( false == ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ) + if ( false == ON_SubDMeshFragment::SideSegmentCountIsValid(side_segment_count) ) return ON_SUBD_RETURN_ERROR(0); - + if ( side_segment_count <= 1 ) level_of_detail = 0; else if (level_of_detail > 0) { if (level_of_detail >= 32 || 1U << level_of_detail > side_segment_count) { - level_of_detail = 1; + level_of_detail = 1; while ( 2*level_of_detail < side_segment_count ) level_of_detail*= 2; } @@ -328,7 +779,7 @@ unsigned int ON_SubDLimitMeshFragmentGrid::SetQuads( return ON_SUBD_RETURN_ERROR(0); if (side_capacity < 4*side_quad_count +1 ) return ON_SUBD_RETURN_ERROR(0); - + unsigned int vi = 0; for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) @@ -342,13 +793,13 @@ unsigned int ON_SubDLimitMeshFragmentGrid::SetQuads( *sides = vi; vi += P_dj; } - + for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) { *sides = vi; vi -= P_di; } - + for (unsigned int* sides1 = sides + side_quad_count; sides < sides1; sides += side_stride) { *sides = vi; @@ -361,11 +812,11 @@ unsigned int ON_SubDLimitMeshFragmentGrid::SetQuads( return side_quad_count*side_quad_count; } -bool ON_SubDLimitMeshFragment::SideSegmentCountIsValid( +bool ON_SubDMeshFragment::SideSegmentCountIsValid( unsigned int side_segment_count ) { - if (side_segment_count > 0 && side_segment_count <= ON_SubDLimitMeshFragment::MaximumSideSegmentCount) + if (side_segment_count > 0 && side_segment_count <= ON_SubDMeshFragment::MaximumSideSegmentCount) { for (unsigned int n = 1; n <= side_segment_count; n *= 2) { @@ -377,37 +828,252 @@ bool ON_SubDLimitMeshFragment::SideSegmentCountIsValid( return ON_SUBD_RETURN_ERROR(false); } -unsigned int ON_SubDLimitMeshFragment::SidePointCountFromSideCount( +unsigned int ON_SubDMeshFragment::SidePointCountFromSideCount( unsigned int side_segment_count ) { - return ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ? (side_segment_count+1) : 0U; + return ON_SubDMeshFragment::SideSegmentCountIsValid(side_segment_count) ? (side_segment_count+1) : 0U; } -unsigned int ON_SubDLimitMeshFragment::QuadGridPointCountFromSideCount( +unsigned int ON_SubDMeshFragment::QuadGridPointCountFromSideCount( unsigned int side_segment_count ) { - unsigned int side_point_count = ON_SubDLimitMeshFragment::SidePointCountFromSideCount(side_segment_count); + unsigned int side_point_count = ON_SubDMeshFragment::SidePointCountFromSideCount(side_segment_count); return side_point_count*side_point_count; } -unsigned int ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount( +unsigned int ON_SubDMeshFragment::QuadGridQuadCountFromSideCount( unsigned int side_segment_count ) { - return ON_SubDLimitMeshFragment::SideSegmentCountIsValid(side_segment_count) ? (side_segment_count*side_segment_count) : 0U; + return ON_SubDMeshFragment::SideSegmentCountIsValid(side_segment_count) ? (side_segment_count*side_segment_count) : 0U; } +const ON_SubDFace* ON_SubDMeshFragment::SubDFace() const +{ + return m_face; +} -const class ON_SubDEdge* ON_SubDLimitMeshFragment::Edge( +bool ON_SubDMeshFragment::IsFullFaceFragment() const +{ + return + nullptr != m_face + && 4 == m_face->m_edge_count + && 1 == m_face_fragment_count + && 0 == m_face_fragment_index + && 0 == m_face_vertex_index[0] + && 1 == m_face_vertex_index[1] + && 2 == m_face_vertex_index[2] + && 3 == m_face_vertex_index[3] + && m_grid.m_side_segment_count > 0 && nullptr != m_grid.m_S + ; +} + +bool ON_SubDMeshFragment::IsFaceCornerFragment() const +{ + return ON_UNSET_UINT_INDEX != FaceCornerIndex(); +} + +unsigned int ON_SubDMeshFragment::FaceCornerIndex() const +{ + return ( + nullptr != m_face + && (3 == m_face->m_edge_count || m_face->m_edge_count >= 5) + && m_face->m_edge_count == m_face_fragment_count + && m_face_fragment_index < m_face_fragment_count + && m_face_vertex_index[2] < m_face->m_edge_count + && m_face_vertex_index[0] > ON_SubDFace::MaximumEdgeCount + && m_face_vertex_index[1] > ON_SubDFace::MaximumEdgeCount + && m_face_vertex_index[3] > ON_SubDFace::MaximumEdgeCount + && m_grid.m_side_segment_count > 0 && nullptr != m_grid.m_S + ) + ? ((unsigned int)m_face_vertex_index[2]) + : ON_UNSET_UINT_INDEX + ; +} + +const ON_SubDMeshFragment* ON_SubDMeshFragment::FirstFaceFragment() const +{ + if (IsFullFaceFragment()) + return this; + if (false == IsFaceCornerFragment()) + return nullptr; + + const ON_SubDMeshFragment* first = this; + while (nullptr != first && first->m_face_fragment_index > 0) + first = first->PreviousFaceFragment(false); + if (nullptr == first) + return nullptr; + const ON_SubDMeshFragment* last = first->NextFaceFragment(false); + while (nullptr != last && last->m_face_fragment_index + 1 < last->m_face_fragment_count) + last = last->NextFaceFragment(false); + if (nullptr == last) + return nullptr; + return first; +} + +const ON_SubDMeshFragment* ON_SubDMeshFragment::LastFaceFragment() const +{ + if (IsFullFaceFragment()) + return this; + if (false == IsFaceCornerFragment()) + return nullptr; + + const ON_SubDMeshFragment* last = this; + while (nullptr != last && last->m_face_fragment_index + 1 < m_face_fragment_count) + last = last->NextFaceFragment(false); + if (nullptr == last) + return nullptr; + const ON_SubDMeshFragment* first = last->PreviousFaceFragment(false); + while (nullptr != first && first->m_face_fragment_index > 0) + first = first->PreviousFaceFragment(false); + if (nullptr == first) + return nullptr; + return last; +} + +const ON_SubDMeshFragment* ON_SubDMeshFragment::PreviousFaceFragment( + bool bLastFromFirst +) const +{ + if ( nullptr != m_face && m_face_fragment_index < m_face_fragment_count ) + { + if (bLastFromFirst && 0 == m_face_fragment_index) + { + return LastFaceFragment(); + } + else if ( + m_face_fragment_index > 0 + && nullptr != m_prev_fragment + && m_face == m_prev_fragment->m_face + && m_face_fragment_count == m_prev_fragment->m_face_fragment_count + && m_face_fragment_index - 1 == m_prev_fragment->m_face_fragment_index + ) + { + return m_prev_fragment; + } + } + return nullptr; +} + +const ON_SubDMeshFragment* ON_SubDMeshFragment::NextFaceFragment( + bool bFirstFromLast +) const +{ + if ( nullptr != m_face && m_face_fragment_index < m_face_fragment_count ) + { + if (bFirstFromLast && m_face_fragment_index+1 == m_face_fragment_count) + { + return FirstFaceFragment(); + } + else if ( + m_face_fragment_index + 1 < m_face_fragment_count + && nullptr != m_next_fragment + && m_face == m_next_fragment->m_face + && m_face_fragment_count == m_next_fragment->m_face_fragment_count + && m_face_fragment_index + 1 == m_next_fragment->m_face_fragment_index + ) + { + return m_next_fragment; + } + } + return nullptr; +} + +unsigned int ON_SubDMeshFragment::FaceFragmentCount() const +{ + return m_face_fragment_count; +} + +unsigned int ON_SubDMeshFragment::GetFaceFragments( + const ON_SubDMeshFragment** fragments, + size_t fragments_capacity +) const +{ + const unsigned int fragment_count = FaceFragmentCount(); + if (fragments_capacity < (size_t)(fragment_count > 0 ? fragment_count : 1U)) + return 0; + const ON_SubDMeshFragment* fragment = FirstFaceFragment(); + for (unsigned i = 0; i < fragment_count; ++i, fragment = fragment->m_next_fragment) + { + if (nullptr == fragment) + return 0; + fragments[i] = fragment; + } + return fragment_count; +} + +unsigned int ON_SubDMeshFragment::GetFaceFragments( + ON_SimpleArray& fragments +) const +{ + unsigned int fragment_count = FaceFragmentCount(); + fragments.SetCount(0); + fragments.Reserve(fragment_count); + fragment_count = GetFaceFragments(fragments.Array(), fragment_count); + fragments.SetCapacity(fragment_count); + return fragment_count; +} + +const ON_3dPoint ON_SubDMeshFragment::VertexPoint( + ON_2udex grid2dex +) const +{ + return VertexPoint(m_grid.PointIndexFromGrid2dex(grid2dex.i, grid2dex.j)); +} + +const ON_3dPoint ON_SubDMeshFragment::VertexPoint( + unsigned grid2dex_i, + unsigned grid2dex_j +) const +{ + return VertexPoint(m_grid.PointIndexFromGrid2dex(grid2dex_i, grid2dex_j)); +} + +const ON_3dPoint ON_SubDMeshFragment::VertexPoint( + unsigned grid_point_index +) const +{ + if (grid_point_index >= (unsigned)PointCount()) + return ON_3dPoint::NanPoint; + return ON_3dPoint(m_P + grid_point_index * m_P_stride); +} + +const ON_3dVector ON_SubDMeshFragment::VertexNormal( + ON_2udex grid2dex +) const +{ + return VertexNormal(m_grid.PointIndexFromGrid2dex(grid2dex.i, grid2dex.j)); +} + +const ON_3dVector ON_SubDMeshFragment::VertexNormal( + unsigned grid2dex_i, + unsigned grid2dex_j +) const +{ + return VertexNormal(m_grid.PointIndexFromGrid2dex(grid2dex_i, grid2dex_j)); +} + +const ON_3dVector ON_SubDMeshFragment::VertexNormal( + unsigned grid_point_index +) const +{ + return + (grid_point_index >= NormalCount()) + ? ON_3dVector::ZeroVector + : ON_3dVector(m_N + grid_point_index * m_N_stride) + ; +} + +const class ON_SubDEdge* ON_SubDMeshFragment::SubDEdge( unsigned int grid_side_index ) const { - return EdgePtr(grid_side_index).Edge(); + return SubDEdgePtr(grid_side_index).Edge(); } -const class ON_SubDEdgePtr ON_SubDLimitMeshFragment::EdgePtr( +const class ON_SubDEdgePtr ON_SubDMeshFragment::SubDEdgePtr( unsigned int grid_side_index ) const { @@ -424,7 +1090,7 @@ const class ON_SubDEdgePtr ON_SubDLimitMeshFragment::EdgePtr( return ON_SubDEdgePtr::Null; } -const class ON_SubDVertex* ON_SubDLimitMeshFragment::Vertex( +const class ON_SubDVertex* ON_SubDMeshFragment::SubDVertex( unsigned int grid_corner_index ) const { @@ -437,201 +1103,278 @@ const class ON_SubDVertex* ON_SubDLimitMeshFragment::Vertex( return nullptr; } -const ON_3dPoint ON_SubDLimitMeshFragment::CornerPoint( +const ON_3dPoint ON_SubDMeshFragment::CornerPoint( unsigned int grid_corner_index ) const { if ( grid_corner_index >= 4 || nullptr == m_P || m_P_stride <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::NanPoint; - + const unsigned int i = m_grid.m_S[grid_corner_index * m_grid.m_side_segment_count]; return ON_3dPoint(m_P + (i*m_P_stride)); } -const ON_3dVector ON_SubDLimitMeshFragment::CornerNormal(unsigned int grid_corner_index) const +const ON_3dVector ON_SubDMeshFragment::CornerNormal(unsigned int grid_corner_index) const { if ( grid_corner_index >= 4 || nullptr == m_N || m_N_stride <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::NanPoint; - + const unsigned int i = m_grid.m_S[grid_corner_index * m_grid.m_side_segment_count]; return ON_3dVector(m_N + (i*m_N_stride)); } -const ON_3dPoint ON_SubDLimitMeshFragment::SidePoint(unsigned int grid_side_index) const +const ON_3dPoint ON_SubDMeshFragment::SidePoint(unsigned int grid_side_index) const { if ( grid_side_index >= 4 || nullptr == m_P || m_P_stride <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::NanPoint; - + const unsigned int i = grid_side_index*m_grid.m_side_segment_count + m_grid.m_side_segment_count/2; return ON_3dPoint(m_P + (i*m_P_stride)); } -const ON_3dVector ON_SubDLimitMeshFragment::SideNormal(unsigned int grid_side_index) const +const ON_3dVector ON_SubDMeshFragment::SideNormal(unsigned int grid_side_index) const { if ( grid_side_index >= 4 || nullptr == m_N || m_N_stride <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::NanPoint; - + const unsigned int i = grid_side_index*m_grid.m_side_segment_count + m_grid.m_side_segment_count/2; return ON_3dVector(m_N + (i*m_N_stride)); } -const ON_3dPoint ON_SubDLimitMeshFragment::CenterPoint() const +const ON_3dPoint ON_SubDMeshFragment::CenterPoint() const { - if ( nullptr == m_P || m_P_stride <= 0 || nullptr == m_grid.m_S ) + if ( nullptr == m_P || m_P_stride < 3 || m_grid.m_side_segment_count <= 0 || nullptr == m_grid.m_S ) return ON_3dPoint::NanPoint; - - const unsigned int i = (m_grid.m_side_segment_count*(m_grid.m_side_segment_count + 2)) / 2; + if (1 == m_grid.m_side_segment_count) + { + return ON_3dPoint( + 0.25*(m_P[0] + m_P[m_P_stride] + m_P[2 * m_P_stride] + m_P[3 * m_P_stride]), + 0.25*(m_P[1] + m_P[1+m_P_stride] + m_P[1 + 2 * m_P_stride] + m_P[1 + 3 * m_P_stride]), + 0.25*(m_P[2] + m_P[2+m_P_stride] + m_P[2 + 2 * m_P_stride] + m_P[2 + 3 * m_P_stride]) + ); + } + + // otherwise there is an actual point at the center of the grid. + const unsigned int i = (m_grid.m_side_segment_count*(m_grid.m_side_segment_count + 2)) / 2; return ON_3dPoint(m_P + (i*m_P_stride)); } -const ON_3dVector ON_SubDLimitMeshFragment::CenterNormal() const +const ON_3dVector ON_SubDMeshFragment::CenterNormal() const { - if ( nullptr == m_N || m_N_stride <= 0 || nullptr == m_grid.m_S ) - return ON_3dPoint::NanPoint; - + if (nullptr == m_N || (m_N_stride != 0 && m_N_stride < 3) || m_grid.m_side_segment_count <= 0 || nullptr == m_grid.m_S) + return ON_3dVector::NanVector; + + if (1 == m_grid.m_side_segment_count) + { + const ON_3dVector N( + (m_N[0] + m_N[m_N_stride] + m_N[2 * m_N_stride] + m_N[3 * m_N_stride]), + (m_N[1] + m_N[1 + m_N_stride] + m_N[1 + 2 * m_N_stride] + m_N[1 + 3 * m_N_stride]), + (m_N[2] + m_N[2 + m_N_stride] + m_N[2 + 2 * m_N_stride] + m_N[2 + 3 * m_N_stride]) + ); + return N.UnitVector(); + } + const unsigned int i = (m_grid.m_side_segment_count*(m_grid.m_side_segment_count + 2)) / 2; return ON_3dVector(m_N + (i*m_N_stride)); } -bool ON_SubDLimitMeshFragment::Internal_GetFrameHelper( +bool ON_SubDMeshFragment::Internal_GetFrameHelper( unsigned int P_dex, unsigned int Q_dex, ON_Plane& frame ) const { - const unsigned int P_count = m_P_count; - if ( P_dex < P_count + const unsigned int P_count = PointCount(); + if ( P_dex < P_count && Q_dex < P_count - && nullptr != m_P - && m_P_stride > 0 - && nullptr != m_N - && m_N_stride > 0 + && NormalCount() == PointCount() ) { - const ON_3dVector Z(m_N + (P_dex*m_N_stride)); - const ON_3dPoint P(m_P + (P_dex*m_P_stride)); - const ON_3dPoint Q (m_P + (Q_dex*m_P_stride)); - ON_3dVector V = (Q - P).UnitVector(); - ON_3dVector X = (V - (frame.zaxis*V)*V).UnitVector(); - if (X.IsUnitVector()) + const ON_3dPoint P(VertexPoint(P_dex)); + const ON_3dVector Z(VertexNormal(P_dex)); + if (P.IsValid() && Z.IsNotZero()) { - frame.origin = P; - frame.xaxis = X; - frame.yaxis = ON_CrossProduct(Z, X); - frame.zaxis = Z; - frame.UpdateEquation(); + const ON_3dPoint Q(VertexPoint(Q_dex)); + ON_3dVector V = (Q - P).UnitVector(); + ON_3dVector X = (V - (frame.zaxis*V)*V).UnitVector(); + if (X.IsUnitVector()) + { + frame.origin = P; + frame.xaxis = X; + frame.yaxis = ON_CrossProduct(Z, X); + frame.zaxis = Z; + frame.UpdateEquation(); + } + else + { + frame = ON_Plane(P, Z); + } + return true; } - else - { - frame = ON_Plane(P, Z); - } - return true; } return false; } -bool ON_SubDLimitMeshFragment::GetCornerFrame( - unsigned int grid_corner_index, - ON_Plane& frame +const ON_Plane ON_SubDMeshFragment::CornerFrame( + unsigned int grid_corner_index ) const { if (grid_corner_index < 4 && m_grid.m_side_segment_count > 0 && nullptr != m_grid.m_S) { unsigned int S_dex = grid_corner_index * m_grid.m_side_segment_count; - if (Internal_GetFrameHelper(m_grid.m_S[S_dex], m_grid.m_S[S_dex + 1], frame)) - return true; + ON_Plane corner_frame; + if (Internal_GetFrameHelper(m_grid.m_S[S_dex], m_grid.m_S[S_dex + 1], corner_frame)) + return corner_frame; } - frame = ON_Plane::NanPlane; - return false; + return ON_Plane::NanPlane; } -bool ON_SubDLimitMeshFragment::GetSideFrame(unsigned int grid_side_index, ON_Plane & frame) const +const ON_Plane ON_SubDMeshFragment::SideFrame(unsigned int grid_side_index) const { const unsigned int count = m_grid.m_side_segment_count; if (grid_side_index < 4U && count > 0 && nullptr != m_grid.m_S) { const unsigned int S_dex = grid_side_index * count + count / 2U; const unsigned int S_dex1 = (S_dex < (4U * count)) ? (S_dex + 1U) : (S_dex - 1U); - if (Internal_GetFrameHelper(m_grid.m_S[S_dex], m_grid.m_S[S_dex1], frame)) - return true; + ON_Plane side_frame; + if (Internal_GetFrameHelper(m_grid.m_S[S_dex], m_grid.m_S[S_dex1], side_frame)) + return side_frame; } - frame = ON_Plane::NanPlane; - return false; + return ON_Plane::NanPlane; } -bool ON_SubDLimitMeshFragment::GetCenterFrame(ON_Plane & frame) const + +const bool ON_SubDMeshFragment::HasValidPointAndNormalGrid() const { - if (m_grid.m_side_segment_count > 0 && nullptr != m_grid.m_S) - { - const unsigned int n = m_grid.m_side_segment_count; - const unsigned int P_dex = (n * (n + 2))/2; // central point index - if (Internal_GetFrameHelper(P_dex, P_dex+1, frame)) - return true; - } - - frame = ON_Plane::NanPlane; - return false; + return + nullptr != m_P + && m_P_stride >= 3 + && nullptr != m_N + && ( 0 == m_N_stride || m_N_stride >= 3) + && m_grid.m_side_segment_count > 0 + && nullptr != m_grid.m_S + ; } -const class ON_SubDVertexPtr ON_SubDLimitMeshFragment::VertexPtr( +const ON_Plane ON_SubDMeshFragment::CenterFrame() const +{ + if (false == HasValidPointAndNormalGrid() ) + return ON_Plane::NanPlane; + + if (1 == m_grid.m_side_segment_count) + { + const ON_3dPoint P( + 0.25*(m_P[0] + m_P[m_P_stride] + m_P[2 * m_P_stride] + m_P[3 * m_P_stride]), + 0.25*(m_P[1] + m_P[1 + m_P_stride] + m_P[1 + 2 * m_P_stride] + m_P[1 + 3 * m_P_stride]), + 0.25*(m_P[2] + m_P[2 + m_P_stride] + m_P[2 + 2 * m_P_stride] + m_P[2 + 3 * m_P_stride]) + ); + if( false == (P.x == P.x)) + return ON_Plane::NanPlane; + ON_3dVector N = ON_3dVector( + (m_N[0] + m_N[m_N_stride] + m_N[2 * m_N_stride] + m_N[3 * m_N_stride]), + (m_N[1] + m_N[1 + m_N_stride] + m_N[1 + 2 * m_N_stride] + m_N[1 + 3 * m_N_stride]), + (m_N[2] + m_N[2 + m_N_stride] + m_N[2 + 2 * m_N_stride] + m_N[2 + 3 * m_N_stride]) + ).UnitVector(); + if (false == N.IsUnitVector()) + N = ON_3dVector(m_N).UnitVector(); + if ( N.IsUnitVector() ) + { + ON_Plane center_frame(P, N); + const ON_3dPoint Q( + 0.5*(m_P[m_P_stride] + m_P[3 * m_P_stride]), + 0.5*(m_P[1 + m_P_stride] + m_P[1 + 3 * m_P_stride]), + 0.5*(m_P[2 + m_P_stride] + m_P[2 + 3 * m_P_stride]) + ); + const ON_3dVector V = (Q - P).UnitVector(); + const ON_3dVector X = (V - (N*V)*V).UnitVector(); + if (X.IsUnitVector()) + { + center_frame.xaxis = X; + center_frame.yaxis = ON_CrossProduct(N, X); + } + return center_frame; + } + } + else + { + const unsigned int P_dex = (m_grid.m_side_segment_count*(m_grid.m_side_segment_count + 2)) / 2; + ON_Plane center_frame; + if (Internal_GetFrameHelper(P_dex, P_dex + 1, center_frame)) + return center_frame; + } + + return ON_Plane::NanPlane; +} + +const class ON_SubDVertexPtr ON_SubDMeshFragment::SubDVertexPtr( unsigned int grid_corner_index ) const { - return ON_SubDVertexPtr::Create(Vertex(grid_corner_index)); + return ON_SubDVertexPtr::Create(SubDVertex(grid_corner_index)); } -ON_ComponentStatus ON_SubDLimitMeshFragment::Status() const +ON_ComponentStatus ON_SubDMeshFragment::Status() const { return (nullptr == m_face) ? ON_ComponentStatus::NoneSet : m_face->m_status; } -bool ON_SubDLimitMeshFragment::IsPartialFragment() const +unsigned int ON_SubDMeshFragmentGrid::LevelOfDetail() const { - return ( - nullptr != m_face - && m_face->m_edge_count < ON_SubDFace::MaximumEdgeCount - && m_face_vertex_index[0] >= ON_SubDFace::MaximumEdgeCount - && m_face_vertex_index[1] >= ON_SubDFace::MaximumEdgeCount - && m_face_vertex_index[2] < m_face->m_edge_count - && m_face_vertex_index[3] >= ON_SubDFace::MaximumEdgeCount - && m_grid.m_side_segment_count > 0 && nullptr != m_grid.m_S - ); + return m_F_level_of_detail; } -bool ON_SubDLimitMeshFragment::IsCompleteFragment() const +unsigned int ON_SubDMeshFragmentGrid::DisplayDensity() const { - // For a complete fragment, - // m_face_vertex_index[i] = {0,1,2,3} for quads - // m_face_vertex_index[i] = {0,1,2,?} for tris - return ( - nullptr != m_face - && m_face->m_edge_count < ON_SubDFace::MaximumEdgeCount - && m_face_vertex_index[0] < m_face->m_edge_count - && m_face_vertex_index[1] < m_face->m_edge_count - && m_face_vertex_index[2] < m_face->m_edge_count - && m_grid.m_side_segment_count > 0 && nullptr != m_grid.m_S - ); + unsigned int side_segment_count = SideSegmentCount(); + if (0 == side_segment_count) + return ON_UNSET_UINT_INDEX; + unsigned int display_density = 0; + while ((side_segment_count /= 2) > 0) + display_density++; + return display_density; } -unsigned int ON_SubDLimitMeshFragmentGrid::SideSegmentCount() const +unsigned int ON_SubDMeshFragmentGrid::SideSegmentCount() const { - unsigned int side_segment_count = 1; - while( side_segment_count*side_segment_count < m_F_count) - side_segment_count *= 2; - return (side_segment_count*side_segment_count == m_F_count) ? side_segment_count : 0; + return m_side_segment_count; + //unsigned int side_segment_count = 1; + //while( side_segment_count*side_segment_count < m_F_count) + // side_segment_count *= 2; + //return (side_segment_count*side_segment_count == m_F_count) ? side_segment_count : 0; } -unsigned int ON_SubDLimitMeshFragmentGrid::GridId() const +unsigned int ON_SubDMeshFragmentGrid::SidePointCount() const +{ + const unsigned int i = SideSegmentCount(); + return (i > 0U) ? (i + 1U) : 0U; +} + +unsigned int ON_SubDMeshFragmentGrid::GridFaceCount() const +{ + // TODO: Suport tri fragments + unsigned int side_segment_count = SideSegmentCount(); + return side_segment_count * side_segment_count; +} + + +unsigned int ON_SubDMeshFragmentGrid::GridPointCount() const +{ + // TODO: Suport tri fragments + unsigned int side_segment_count = SideSegmentCount(); + return (side_segment_count > 0U) ? ((side_segment_count + 1U)*(side_segment_count + 1U)) : 0U; +} + +unsigned int ON_SubDMeshFragmentGrid::GridId() const { for (;;) { @@ -646,31 +1389,55 @@ unsigned int ON_SubDLimitMeshFragmentGrid::GridId() const id /= 2; // id = "n" unsigned int lod = (m_F_level_of_detail > id) ? id : (unsigned int)m_F_level_of_detail; id = 32*id + 2*lod; - if ( ON_SubD::FacetType::Tri == m_F_type) - id += 1; - else if ( ON_SubD::FacetType::Quad != m_F_type) - break; return id; } } } - + return 0; } -unsigned int ON_SubDLimitMeshFragmentGrid::GridFacetSideCount() const +const ON_2udex ON_SubDMeshFragmentGrid::Grid2dexFromPointIndex( + unsigned int grid_point_index +) const { - switch (m_F_type) + for (;;) { - case ON_SubD::FacetType::Tri: - return 3; - case ON_SubD::FacetType::Quad: - return 4; + const unsigned int side_segment_count = SideSegmentCount(); + if (0 == side_segment_count) + break; + const unsigned int grid_side_point_count = side_segment_count + 1; + if (grid_point_index >= grid_side_point_count * grid_side_point_count) + break; + const ON_2udex griddex( + grid_point_index % grid_side_point_count, + grid_point_index / grid_side_point_count + ); + return griddex; } - return 0; + return ON_SUBD_RETURN_ERROR(ON_2udex::Unset); } -bool ON_SubDLimitMeshFragmentGrid::GetGridParameters( +unsigned int ON_SubDMeshFragmentGrid::PointIndexFromGrid2dex( + unsigned int i, + unsigned int j +) const +{ + for (;;) + { + const unsigned int side_segment_count = SideSegmentCount(); + if (0 == side_segment_count) + break; + const unsigned int grid_side_point_count = side_segment_count + 1; + if (i >= grid_side_point_count && j >= grid_side_point_count) + break; + return grid_side_point_count * i + j; + } + return ON_UNSET_UINT_INDEX; +} + + +bool ON_SubDMeshFragmentGrid::GetGridParameters( unsigned int grid_point_index, double grid_parameters[2] ) const @@ -683,20 +1450,16 @@ bool ON_SubDLimitMeshFragmentGrid::GetGridParameters( const unsigned int grid_side_point_count = side_segment_count + 1; if (grid_point_index >= grid_side_point_count*grid_side_point_count) break; - unsigned int i = grid_point_index % grid_side_point_count; - unsigned int j = grid_point_index / grid_side_point_count; - if ( 0 == i ) - grid_parameters[0] = 0.0; - else if ( i == grid_side_point_count) - grid_parameters[0] = 1.0; - else - grid_parameters[0] = i*1.0 / ((double)grid_side_point_count); - if ( 0 == j ) - grid_parameters[1] = 0.0; - else if ( j == grid_side_point_count) - grid_parameters[1] = 1.0; - else - grid_parameters[1] = j*1.0 / ((double)grid_side_point_count); + const unsigned int i = grid_point_index % grid_side_point_count; + grid_parameters[0] + = (i < grid_side_point_count) + ? (((double)i) / ((double)grid_side_point_count)) + : 1.0; + const unsigned int j = grid_point_index / grid_side_point_count; + grid_parameters[1] + = (j < grid_side_point_count) + ? (((double)j) / ((double)grid_side_point_count)) + : 1.0; return true; } grid_parameters[0] = ON_UNSET_VALUE; @@ -704,41 +1467,30 @@ bool ON_SubDLimitMeshFragmentGrid::GetGridParameters( return false; } -ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Facets( - ON_SubD::FacetType facet_type, + +ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::QuadGridFromSideSegmentCount( unsigned int side_segment_count, unsigned int level_of_detail - ) +) { - if (ON_SubD::FacetType::Quad == facet_type) - return ON_SubDLimitMeshFragmentGrid::Quads(side_segment_count, level_of_detail); - - if (ON_SubD::FacetType::Tri == facet_type) - return ON_SubDLimitMeshFragmentGrid::Quads(side_segment_count, level_of_detail); - - return ON_SubDLimitMeshFragmentGrid::Empty; + const unsigned int display_density = ON_SubDMeshFragment::DisplayDensityFromSideSegmentCount(side_segment_count); + if ( side_segment_count != ON_SubDMeshFragment::SideSegmentCountFromDisplayDensity(display_density) ) + return ON_SUBD_RETURN_ERROR(ON_SubDMeshFragmentGrid::Empty); + return ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity(display_density,level_of_detail); } -ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Tris( - unsigned int side_segment_count, + +ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity( + unsigned int display_density, unsigned int level_of_detail ) { - return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty); -} + static const ON_SubDMeshFragmentGrid* grid_cache[9] = { 0 }; // side_segment_count = 1,2,4,8,16,32,64,128,256 -ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( - unsigned int side_segment_count, - unsigned int level_of_detail - ) -{ - static const ON_SubDLimitMeshFragmentGrid* grid_cache[9] = { 0 }; // side_segment_count = 1,2,4,8,16,32,64,128,256 + if ( ((size_t)display_density) >= sizeof(grid_cache)/sizeof(grid_cache[0])) + return ON_SUBD_RETURN_ERROR(ON_SubDMeshFragmentGrid::Empty); - const unsigned int grid_cache_index = ON_SubDLimitMeshFragment::DisplayDensityFromSideSegmentCount(side_segment_count); - if ( side_segment_count != ON_SubDLimitMeshFragment::SideSegmentCountFromDisplayDensity(grid_cache_index) ) - return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty); - - const ON_SubDLimitMeshFragmentGrid* fragment_grid = grid_cache[grid_cache_index]; + const ON_SubDMeshFragmentGrid* fragment_grid = grid_cache[display_density]; if (nullptr != fragment_grid) { while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail) @@ -752,8 +1504,17 @@ ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( static ON_SleepLock lock; const bool bReturnLock = lock.GetLock(50,ON_SleepLock::OneMinute,true); + // try again in case cache was made while waiting + fragment_grid = grid_cache[display_density]; + if (nullptr != fragment_grid) + { + lock.ReturnLock(); + while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail) + fragment_grid = fragment_grid->m_next_level_of_detail; + return *fragment_grid; + } - // The ON_SubDLimitMeshFragmentGrid classes created below are created one time as needed + // The ON_SubDMeshFragmentGrid classes created below are created one time as needed // and used millions of times after that. These are app workspace allocations // and not memory leaks. Once a grid exists, it is saved in grid_cache[] and returned // above the next zillion times it is required. @@ -763,13 +1524,13 @@ ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( unsigned int quad_capacity = 0; unsigned int side_segment_capacity = 0; unsigned int grid_count = 0; - unsigned int grid_cache_index0 = grid_cache_index; - unsigned int grid_cache_index1 = grid_cache_index; - if (grid_cache_index <= 4) + unsigned int grid_cache_index0 = display_density; + unsigned int grid_cache_index1 = display_density; + if (display_density <= ON_SubDDisplayParameters::DefaultDensity) { // make all the common small grids grid_cache_index0 = 0; - grid_cache_index1 = 4; + grid_cache_index1 = ON_SubDDisplayParameters::DefaultDensity; } for (unsigned int i = grid_cache_index0; i <= grid_cache_index1; i++) { @@ -777,22 +1538,21 @@ ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( unsigned int s1 = (1U << i); for (unsigned int s2 = s1; s2 > 0; s2 /= 2) { - quad_capacity += ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount(s2); + quad_capacity += ON_SubDMeshFragment::QuadGridQuadCountFromSideCount(s2); side_segment_capacity += 4*s2 + 1; grid_count++; } - } + } const unsigned int vdex_capacity = (quad_capacity*4 + side_segment_capacity); - size_t sz1 = grid_count*sizeof(ON_SubDLimitMeshFragmentGrid); + size_t sz1 = grid_count*sizeof(ON_SubDMeshFragmentGrid); size_t sz2 = vdex_capacity*sizeof(unsigned int); - if (0 != sz2 % sizeof(ON_SubDLimitMeshFragmentGrid)) - sz2 = (1 + sz2/sizeof(ON_SubDLimitMeshFragmentGrid))*sizeof(ON_SubDLimitMeshFragmentGrid); - ON_SubDLimitMeshFragmentGrid* grids = new (std::nothrow) ON_SubDLimitMeshFragmentGrid[(sz1 + sz2)/sizeof(ON_SubDLimitMeshFragmentGrid)]; + if (0 != sz2 % sizeof(ON_SubDMeshFragmentGrid)) + sz2 = (1 + sz2/sizeof(ON_SubDMeshFragmentGrid))*sizeof(ON_SubDMeshFragmentGrid); + ON_SubDMeshFragmentGrid* grids = new (std::nothrow) ON_SubDMeshFragmentGrid[(sz1 + sz2)/sizeof(ON_SubDMeshFragmentGrid)]; - ON_SubDLimitMeshFragmentGrid grid = ON_SubDLimitMeshFragmentGrid::Empty; - grid.m_F_type = ON_SubD::FacetType::Quad; + ON_SubDMeshFragmentGrid grid = ON_SubDMeshFragmentGrid::Empty; grid.m_F_stride = 4; unsigned int* vdex0 = (unsigned int*)(grids + grid_count); unsigned int* vdex1 = vdex0 + vdex_capacity; @@ -801,12 +1561,12 @@ ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( for (unsigned int i = grid_cache_index0; i <= grid_cache_index1; i++) { const unsigned int s1 = (1U << i); - ON_SubDLimitMeshFragmentGrid* first_lod = grids; - ON_SubDLimitMeshFragmentGrid* prev_lod = nullptr; + ON_SubDMeshFragmentGrid* first_lod = grids; + ON_SubDMeshFragmentGrid* prev_lod = nullptr; grid.m_F_level_of_detail = 0; for (unsigned int s2 = s1; s2 > 0; s2 /= 2, grids++) { - const unsigned int grid_F_capacity = ON_SubDLimitMeshFragment::QuadGridQuadCountFromSideCount(s2); + const unsigned int grid_F_capacity = ON_SubDMeshFragment::QuadGridQuadCountFromSideCount(s2); const unsigned int grid_S_capacity = 4*s2 + 1; grid.m_side_segment_count = (unsigned char)s2; @@ -822,15 +1582,15 @@ ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( break; } - ON_SubDLimitMeshFragmentGrid::SetQuads( + ON_SubDMeshFragmentGrid::SetQuads( s1, // top level side_segment_count grid.m_F_level_of_detail, + const_cast(grid.m_F), grid_F_capacity, grid.m_F_stride, - const_cast(grid.m_F), + const_cast(grid.m_S), grid_S_capacity, - 1U, - const_cast(grid.m_S) + 1U ); *grids = grid; @@ -849,7 +1609,7 @@ ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( // This way if the lock is stolen for some unforseen reason, we risk leaking memory // but we will not crash. grid_cache[i] = first_lod; - } + } if ( bReturnLock ) lock.ReturnLock(); @@ -859,7 +1619,7 @@ ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( ON_SubDIncrementErrorCount(); } - fragment_grid = grid_cache[grid_cache_index]; + fragment_grid = grid_cache[display_density]; if (nullptr != fragment_grid) { while ( fragment_grid->m_F_level_of_detail < level_of_detail && nullptr != fragment_grid->m_next_level_of_detail) @@ -867,15 +1627,15 @@ ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Quads( return *fragment_grid; } - return ON_SUBD_RETURN_ERROR(ON_SubDLimitMeshFragmentGrid::Empty); + return ON_SUBD_RETURN_ERROR(ON_SubDMeshFragmentGrid::Empty); } ////////////////////////////////////////////////////////////////////////// // -// ON_SubDLimitMesh +// ON_SubDMesh // -void ON_SubDLimitMeshImpl::ClearFragmentFacePointers( +void ON_SubDMeshImpl::ClearFragmentFacePointers( bool bResetSubDWeakPtr ) { @@ -890,14 +1650,14 @@ void ON_SubDLimitMeshImpl::ClearFragmentFacePointers( } } // -//bool ON_SubDLimitMeshImpl::ReserveCapacityx( +//bool ON_SubDMeshImpl::ReserveCapacityx( // const class ON_SubD& subd, // ON_SubDDisplayParameters limit_mesh_parameters // ) //{ // const unsigned int level_index = limit_mesh_parameters.m_level_index; // -// unsigned int subd_fragment_count = subd.LimitSurfaceMeshFragmentCount(level_index); +// unsigned int subd_fragment_count = subd.MeshFragmentCount(level_index); // if ( subd_fragment_count < 1 ) // return ON_SUBD_RETURN_ERROR(false); // @@ -910,114 +1670,339 @@ void ON_SubDLimitMeshImpl::ClearFragmentFacePointers( // level_index); //} -bool ON_SubDLimitMeshImpl::ReserveCapacity( +bool ON_SubDMeshImpl::ReserveCapacity( unsigned int subd_fragment_count, - ON_SubD::FacetType facet_type, unsigned int display_density ) { ClearTree(); m_display_density = 0; - m_facet_type = ON_SubD::FacetType::Unset; m_fragment_count = 0; m_fragment_point_count = 0; m_first_fragment = nullptr; - if ( display_density > ON_SubDLimitMesh::MaximumDisplayDensity) + if ( display_density > ON_SubDDisplayParameters::MaximumDensity) return ON_SUBD_RETURN_ERROR(false); - unsigned int fragment_point_count = ON_SubDLimitMeshFragment::PointCountFromDisplayDensity(facet_type, display_density); + unsigned int fragment_point_count = ON_SubDMeshFragment::PointCountFromDisplayDensity(display_density); if ( subd_fragment_count < 1 ) return ON_SUBD_RETURN_ERROR(false); - size_t sizeof_point_and_normals = 6*fragment_point_count*sizeof(double); - size_t sizeof_fragment = sizeof(ON_SubDLimitMeshFragment); + size_t sizeof_PNT = 9*fragment_point_count*sizeof(double); + size_t sizeof_fragment = sizeof(ON_SubDMeshFragment); if (0 != sizeof_fragment % sizeof(double)) { sizeof_fragment = (1 + sizeof_fragment / sizeof(double))*sizeof(double); } - if( false == m_fsp.Create(sizeof_fragment + sizeof_point_and_normals,subd_fragment_count,0)) + if( false == m_fsp.Create(sizeof_fragment + sizeof_PNT,subd_fragment_count,0)) return ON_SUBD_RETURN_ERROR(false); m_display_density = display_density; - m_facet_type = facet_type; m_fragment_point_count = fragment_point_count; return true; } -ON_SubDLimitMeshFragment* ON_SubDLimitMeshImpl::CopyCallbackFragment( - const ON_SubDLimitMeshFragment* callback_fragment +size_t ON_SubDMeshFragment::SizeofFragment(unsigned int display_density) +{ + if (display_density > ON_SubDDisplayParameters::MaximumDensity) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned side_seg_count = ON_SubDMeshFragment::SideSegmentCountFromDisplayDensity(display_density); + if (side_seg_count < 1) + return ON_SUBD_RETURN_ERROR(0); + + const unsigned vertex_capacity = (side_seg_count + 1)*(side_seg_count + 1); + size_t sz = sizeof(ON_SubDMeshFragment); + while (0 != (sz % sizeof(double))) + ++sz; + sz += vertex_capacity * 9 * sizeof(double); // 8=3+3+3 (3d points in m_P[], 3d vectors in m_N[], and 3d points in m_T[]). + return sz; +} + +bool ON_SubDMeshFragment::CopyFrom( + const ON_SubDMeshFragment& src_fragment +) +{ + unsigned int display_density = ON_UNSET_UINT_INDEX; + return ON_SubDMeshFragment::CopyFrom(src_fragment, display_density); +} + +bool ON_SubDMeshFragment::CopyFrom( + const ON_SubDMeshFragment& src_fragment, + unsigned int display_density + ) +{ + m_face = src_fragment.m_face; + m_face_vertex_index[0] = src_fragment.m_face_vertex_index[0]; + m_face_vertex_index[1] = src_fragment.m_face_vertex_index[1]; + m_face_vertex_index[2] = src_fragment.m_face_vertex_index[2]; + m_face_vertex_index[3] = src_fragment.m_face_vertex_index[3]; + m_face_fragment_count = src_fragment.m_face_fragment_count; + m_face_fragment_index = src_fragment.m_face_fragment_index; + m_vertex_count_etc = 0; + m_grid = ON_SubDMeshFragmentGrid::Empty; + m_surface_bbox = ON_BoundingBox::NanBoundingBox; + // Note well: Do not change capacity on this. + SetVertexCount(0); + + ON_3dPoint quad_points[4]; + ON_3dVector quad_normal; + src_fragment.GetControlNetQuad(false, quad_points, quad_normal); + SetControlNetQuad(false, quad_points, quad_normal); + + m_ctrlnetT[0][0] = src_fragment.m_ctrlnetT[0][0]; + m_ctrlnetT[0][1] = src_fragment.m_ctrlnetT[0][1]; + m_ctrlnetT[0][2] = src_fragment.m_ctrlnetT[0][2]; + + m_ctrlnetT[1][0] = src_fragment.m_ctrlnetT[1][0]; + m_ctrlnetT[1][1] = src_fragment.m_ctrlnetT[1][1]; + m_ctrlnetT[1][2] = src_fragment.m_ctrlnetT[1][2]; + + m_ctrlnetT[2][0] = src_fragment.m_ctrlnetT[2][0]; + m_ctrlnetT[2][1] = src_fragment.m_ctrlnetT[2][1]; + m_ctrlnetT[2][2] = src_fragment.m_ctrlnetT[2][2]; + + m_ctrlnetT[3][0] = src_fragment.m_ctrlnetT[3][0]; + m_ctrlnetT[3][1] = src_fragment.m_ctrlnetT[3][1]; + m_ctrlnetT[3][2] = src_fragment.m_ctrlnetT[3][2]; + + if ( display_density > 8 && ON_UNSET_UINT_INDEX != display_density) + return ON_SUBD_RETURN_ERROR(false); + + const unsigned src_V_count = src_fragment.VertexCount(); + if ( 0 == src_V_count || src_V_count != src_fragment.m_grid.GridPointCount() ) + return ON_SUBD_RETURN_ERROR(false); + + if (src_V_count != src_fragment.PointCount()) + return ON_SUBD_RETURN_ERROR(false); + + const ON_SubDMeshFragmentGrid grid + = (ON_UNSET_UINT_INDEX == display_density) + ? src_fragment.m_grid + : ON_SubDMeshFragmentGrid::QuadGridFromDisplayDensity(display_density,0); + const unsigned V_count = grid.GridPointCount(); + if (VertexCapacity() < V_count) + { + if (false == this->ReserveManagedVertexCapacity(V_count)) + return ON_SUBD_RETURN_ERROR(false); + } + if ( V_count > VertexCapacity() || V_count > src_fragment.VertexCount()) + return ON_SUBD_RETURN_ERROR(false); + + m_grid = grid; + + double* p = m_P; + size_t p_stride = m_P_stride; + const double* src_p = src_fragment.m_P; + size_t src_p_stride = src_fragment.m_P_stride; + const double* src_p1 = src_p + src_p_stride * src_V_count; + if (V_count == src_V_count) + { + // Most common case where srf_fragment and target have the same density - faster way to copy. + + // copy m_P[] + while (src_p < src_p1) + { + p[0] = src_p[0]; + p[1] = src_p[1]; + p[2] = src_p[2]; + p += p_stride; + src_p += src_p_stride; + } + + if (V_count <= NormalCapacity() ) + { + if (V_count == src_fragment.NormalCount()) + { + // copy m_N[] + p = m_N; + p_stride = m_N_stride; + src_p = src_fragment.m_N; + src_p_stride = src_fragment.m_N_stride; + src_p1 = src_p + src_p_stride * src_V_count; + while (src_p < src_p1) + { + p[0] = src_p[0]; + p[1] = src_p[1]; + p[2] = src_p[2]; + p += p_stride; + src_p += src_p_stride; + } + } + else + { + ON_SubDMeshFragment::Internal_Set3dPointArrayToNan(m_N, V_count, m_N_stride); + } + } + + if (V_count <= TextureCoordinateCapacity()) + { + if (V_count == src_fragment.TextureCoordinateCount()) + { + // copy m_T[] + SetTextureCoordinatesExist(true); + p = m_T; + p_stride = m_T_stride; + src_p = src_fragment.m_T; + src_p_stride = src_fragment.m_T_stride; + src_p1 = src_p + src_p_stride * src_V_count; + while (src_p < src_p1) + { + p[0] = src_p[0]; + p[1] = src_p[1]; + p[2] = src_p[2]; + p += p_stride; + src_p += src_p_stride; + } + } + else + { + ON_SubDMeshFragment::Internal_Set3dPointArrayToNan(m_T, V_count, m_T_stride); + } + } + } + else + { + // srf_fragment is more dense than target. Copy a subset of the points. + const unsigned int s0 = grid.SideSegmentCount(); + const unsigned int s1 = src_fragment.m_grid.SideSegmentCount(); + unsigned int x = 1; + while (x*s0 < s1) + x *= 2; + const unsigned int m = src_fragment.m_grid.SidePointCount(); + const unsigned int di = src_fragment.m_grid.PointIndexFromGrid2dex(x, 0); + const unsigned int dj = src_fragment.m_grid.PointIndexFromGrid2dex(0, x); + + // copy m_P[] + size_t i_stride = di * src_fragment.m_P_stride; + size_t j_stride = m * dj * src_fragment.m_P_stride; + for (unsigned int j = 0; j < m; j += dj) + { + src_p = src_fragment.m_P + j * j_stride; + for (unsigned int i = 0; i < m; i += di) + { + p[0] = src_p[0]; + p[1] = src_p[1]; + p[2] = src_p[2]; + p += p_stride; + src_p += i_stride; + } + } + + if (V_count <= NormalCapacity()) + { + // copy m_N[] + if (V_count <= src_fragment.NormalCount()) + { + p = m_N; + p_stride = m_N_stride; + i_stride = di * src_fragment.m_N_stride; + j_stride = m * dj * src_fragment.m_N_stride; + for (unsigned int j = 0; j < m; j += dj) + { + src_p = src_fragment.m_N + j * j_stride; + for (unsigned int i = 0; i < m; i += di) + { + p[0] = src_p[0]; + p[1] = src_p[1]; + p[2] = src_p[2]; + p += p_stride; + src_p += i_stride; + } + } + } + else + { + ON_SubDMeshFragment::Internal_Set3dPointArrayToNan(m_N, V_count, m_N_stride); + } + } + + if (V_count <= TextureCoordinateCapacity()) + { + // copy m_T[] + if (V_count <= src_fragment.TextureCoordinateCount()) + { + SetTextureCoordinatesExist(true); + p = m_T; + p_stride = m_T_stride; + i_stride = di * src_fragment.m_T_stride; + j_stride = m * dj * src_fragment.m_T_stride; + for (unsigned int j = 0; j < m; j += dj) + { + src_p = src_fragment.m_T + j * j_stride; + for (unsigned int i = 0; i < m; i += di) + { + p[0] = src_p[0]; + p[1] = src_p[1]; + p[2] = src_p[2]; + p += p_stride; + src_p += i_stride; + } + } + } + else + { + ON_SubDMeshFragment::Internal_Set3dPointArrayToNan(m_T, V_count, m_T_stride); + } + } + } + + SetVertexCount(V_count); + m_surface_bbox = src_fragment.m_surface_bbox; + + return true; +} + +ON_SubDMeshFragment* ON_SubDMeshImpl::CopyCallbackFragment( + const ON_SubDMeshFragment* callback_fragment ) { if ( nullptr == callback_fragment) return ON_SUBD_RETURN_ERROR(nullptr); - if ( 0 == callback_fragment->m_P_count ) + if ( 0 == callback_fragment->VertexCount() ) return ON_SUBD_RETURN_ERROR(nullptr); - if ( callback_fragment->m_P_count > m_fragment_point_count ) + if ( callback_fragment->VertexCount() > m_fragment_point_count ) return ON_SUBD_RETURN_ERROR(nullptr); - if ( nullptr == callback_fragment->m_P || nullptr == callback_fragment->m_N ) + if ( 0 == callback_fragment->PointCount() || 0 == callback_fragment->NormalCount() ) return ON_SUBD_RETURN_ERROR(nullptr); - double* P = (double*)m_fsp.AllocateDirtyElement(); - if ( nullptr == P) + // m_fsp.AllocateElement() allocates room for the fragment and the m_P[], m_N[], and m_T[] arrays. + ON_SubDMeshFragment* fragment = (ON_SubDMeshFragment*)m_fsp.AllocateElement(); + if ( nullptr == fragment) return ON_SUBD_RETURN_ERROR(nullptr); - double* N = P + 3*m_fragment_point_count; - ON_SubDLimitMeshFragment* fragment = (ON_SubDLimitMeshFragment*)(N + 3*m_fragment_point_count); - - *fragment = *callback_fragment; + double* P = (double*)(fragment + 1); + double* N = P + 3 * m_fragment_point_count; + double* T = N + 3 * m_fragment_point_count; - const size_t fragment_stride = 3; - fragment->m_P_capacity = (unsigned short)m_fragment_point_count; - fragment->m_P_stride = fragment_stride; + fragment->SetUnmanagedVertexCapacity(m_fragment_point_count); // m_fsp manages array memory + fragment->SetVertexCount(m_fragment_point_count); fragment->m_P = P; - fragment->m_N_stride = fragment_stride; + fragment->m_P_stride = 3; fragment->m_N = N; + fragment->m_N_stride = 3; + fragment->m_T = T; + fragment->m_T_stride = 3; + fragment->CopyFrom(*callback_fragment); - size_t srcP_stride = callback_fragment->m_P_stride; - const double* srcP = callback_fragment->m_P; - const double* srcP1 = srcP + srcP_stride*callback_fragment->m_P_count; - while ( srcP < srcP1 ) - { - P[0] = srcP[0]; - P[1] = srcP[1]; - P[2] = srcP[2]; - P += fragment_stride; - srcP += srcP_stride; - } - - srcP_stride = callback_fragment->m_N_stride; - srcP = callback_fragment->m_N; - srcP1 = srcP + srcP_stride*callback_fragment->m_P_count; - while ( srcP < srcP1 ) - { - N[0] = srcP[0]; - N[1] = srcP[1]; - N[2] = srcP[2]; - N += fragment_stride; - srcP += srcP_stride; - } - - m_limit_mesh_content_serial_number = ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber(); + ChangeContentSerialNumber(); return fragment; } -bool ON_SubDLimitMeshImpl::AddFinishedFragment( - ON_SubDLimitMeshFragment* finished_fragment +bool ON_SubDMeshImpl::AddFinishedFragment( + ON_SubDMeshFragment* finished_fragment ) { if ( nullptr == finished_fragment) return ON_SUBD_RETURN_ERROR(false); - if ( 0 == finished_fragment->m_P_count ) + if ( 0 == finished_fragment->PointCount() || 0 == finished_fragment->NormalCount() ) return ON_SUBD_RETURN_ERROR(false); - if ( finished_fragment->m_P_count > m_fragment_point_count ) - return ON_SUBD_RETURN_ERROR(false); - if ( nullptr == finished_fragment->m_P || nullptr == finished_fragment->m_N ) + if ( finished_fragment->PointCount() > m_fragment_point_count ) return ON_SUBD_RETURN_ERROR(false); m_fragment_count++; @@ -1026,50 +2011,21 @@ bool ON_SubDLimitMeshImpl::AddFinishedFragment( { m_first_fragment = finished_fragment; m_last_fragment = finished_fragment; - m_bbox = finished_fragment->m_bbox; + m_bbox = finished_fragment->m_surface_bbox; } else { m_last_fragment->m_next_fragment = finished_fragment; finished_fragment->m_prev_fragment = m_last_fragment; m_last_fragment = finished_fragment; - m_bbox.Union(finished_fragment->m_bbox); + m_bbox.Union(finished_fragment->m_surface_bbox); } - - m_limit_mesh_content_serial_number = ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber(); + + ChangeContentSerialNumber(); return true; } - -////class ON_SubDLimitMeshSealVertexInfo -////{ -////public: -//// ON_SubDLimitMeshSealVertexInfo() = default; -//// ~ON_SubDLimitMeshSealVertexInfo() = default; -//// ON_SubDLimitMeshSealVertexInfo(const ON_SubDLimitMeshSealVertexInfo&) = default; -//// ON_SubDLimitMeshSealVertexInfo& operator=(const ON_SubDLimitMeshSealVertexInfo&) = default; -//// -//// static const ON_SubDLimitMeshSealVertexInfo Unset; -//// -//// enum Bits : unsigned char -//// { -//// // Set if the vertex is smooth and normals should be sealed along with location. -//// SmoothVertex = 0x01, -//// -//// // Set if the edge is smooth and normals should be sealed along with location. -//// SmoothEdge = 0x02, -//// }; -//// -////public: -//// unsigned int m_vertex_id = 0; -//// unsigned int m_edge_id = 0; -//// unsigned char m_bits = 0; -//// double* m_P = nullptr; -//// double* m_N = nullptr; -////}; -////const ON_SubDLimitMeshSealVertexInfo ON_SubDLimitMeshSealVertexInfo::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDLimitMeshSealVertexInfo); - class ON_SubDLimitMeshSealEdgeInfo { public: @@ -1110,13 +2066,13 @@ public: if (m_face_edge_count < 3 || m_face_edge_count > ON_SubDFace::MaximumEdgeCount) break; // bogus face - const ON_SubDEdgePtr eptr = m_fragment->EdgePtr(grid_side_dex); + const ON_SubDEdgePtr eptr = m_fragment->SubDEdgePtr(grid_side_dex); const ON_SubDEdge* edge = eptr.Edge(); m_edge_id = (nullptr != edge && edge->m_face_count >= 2) ? edge->m_id : 0; if (0 == m_edge_id) break; // nothing to seal - const bool bCompleteFragment = m_fragment->IsCompleteFragment(); + const bool bCompleteFragment = m_fragment->IsFullFaceFragment(); const bool bPartialFragment = (false == bCompleteFragment) && m_fragment->m_face_vertex_index[0] > ON_SubDFace::MaximumEdgeCount @@ -1217,7 +2173,7 @@ public: unsigned char m_grid_side_dex = 0; // 0,1,2,or 3 unsigned short m_face_edge_count = 0; - ON_SubDLimitMeshFragment* m_fragment = nullptr; + ON_SubDMeshFragment* m_fragment = nullptr; }; const ON_SubDLimitMeshSealEdgeInfo ON_SubDLimitMeshSealEdgeInfo::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDLimitMeshSealEdgeInfo); @@ -1249,7 +2205,7 @@ bool ON_SubDLimitMeshSealEdgeInfo::Seal( //// v = src.m_fragment->Vertex((src.m_grid_side_dex+1)%4); //// if ( nullptr !=v ) //// vid[1] = v->m_id; - ////} + ////} // src_dir = 0 if SubD edge and fragment side have compatible natural orientations const unsigned char src_dir = (src.m_bits&ON_SubDLimitMeshSealEdgeInfo::Bits::EdgeDir); @@ -1257,7 +2213,7 @@ bool ON_SubDLimitMeshSealEdgeInfo::Seal( if (src_half != dst_half || src_side_segment_count != dst_side_segment_count) { if ( - 0 == src_half + 0 == src_half && 4 == src.m_face_edge_count && 4 != dst.m_face_edge_count && 2 * dst_side_segment_count == src_side_segment_count) @@ -1302,7 +2258,7 @@ bool ON_SubDLimitMeshSealEdgeInfo::Seal( else { // Either the parent subd is invalid or information - // set in the fragments, or the sorting in ON_SubDLimitMeshImpl::SealEdges() + // set in the fragments, or the sorting in ON_SubDMeshImpl::SealEdges() // is not valid (or some other bug). ON_SUBD_ERROR("unexpected sealing fragment portions"); return false; @@ -1313,7 +2269,7 @@ bool ON_SubDLimitMeshSealEdgeInfo::Seal( const bool bSameDirection = (src_dir == (dst.m_bits&ON_SubDLimitMeshSealEdgeInfo::Bits::EdgeDir)); const unsigned int j0 = dst.m_grid_side_dex*dst_side_segment_count + (bSameDirection?0:dst_side_segment_count); const unsigned int j1 = bSameDirection ? (j0+dst_side_segment_count) : (j0-dst_side_segment_count); - ON_SubDLimitMeshFragment::SealAdjacentSides( + ON_SubDMeshFragment::SealAdjacentSides( true, // bTestNearEqual, bSealNormals, *src.m_fragment, @@ -1327,7 +2283,7 @@ bool ON_SubDLimitMeshSealEdgeInfo::Seal( } -void ON_SubDLimitMeshImpl::SealEdges() +void ON_SubDMeshImpl::SealEdges() { ON_SimpleArray fe_list(m_fragment_count); ON_SubDLimitMeshSealEdgeInfo fe; @@ -1347,7 +2303,7 @@ void ON_SubDLimitMeshImpl::SealEdges() unsigned int i0 = 0; while ( i0 < fe_list_count ) { - fe = fe_list[i0]; + fe = fe_list[i0]; const unsigned char src_half_mask = (fe.m_bits & ON_SubDLimitMeshSealEdgeInfo::Bits::HalfMask); for ( i0++; i0 < fe_list_count && fe.m_edge_id == fe_list[i0].m_edge_id; i0++ ) { @@ -1358,34 +2314,41 @@ void ON_SubDLimitMeshImpl::SealEdges() } } - -unsigned int ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber() +ON__UINT64 ON_SubDMesh::ContentSerialNumber() const { - static unsigned int serial_number = 0; - serial_number++; - return serial_number; + ON_SubDMeshImpl* imple = SubLimple(); + return (nullptr != imple) ? imple->ContentSerialNumber() : 0; } -ON_SubDLimitMeshImpl::ON_SubDLimitMeshImpl() - : m_limit_mesh_content_serial_number(ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber()) -{} - -ON_SubDLimitMeshImpl::ON_SubDLimitMeshImpl( - const class ON_SubDLimitMeshImpl& src - ) - : m_limit_mesh_content_serial_number(ON_SubDLimitMeshImpl::Internal_NextContentSerialNumber()) +ON__UINT64 ON_SubDMeshImpl::ContentSerialNumber() const { - if ( nullptr != src.m_first_fragment && ReserveCapacity((unsigned int)src.m_fsp.ActiveElementCount(), src.m_facet_type, src.m_display_density ) ) + return m_mesh_content_serial_number; +} + +ON__UINT64 ON_SubDMeshImpl::ChangeContentSerialNumber() +{ + return (m_mesh_content_serial_number = ON_NextContentSerialNumber()); +} + +ON_SubDMeshImpl::ON_SubDMeshImpl() +{ + ChangeContentSerialNumber(); +} + +ON_SubDMeshImpl::ON_SubDMeshImpl( const class ON_SubDMeshImpl& src ) +{ + ChangeContentSerialNumber(); + if ( nullptr != src.m_first_fragment && ReserveCapacity((unsigned int)src.m_fsp.ActiveElementCount(), src.m_display_density ) ) { - for (const ON_SubDLimitMeshFragment* src_fragment = src.m_first_fragment; nullptr != src_fragment; src_fragment = src_fragment->m_next_fragment) + for (const ON_SubDMeshFragment* src_fragment = src.m_first_fragment; nullptr != src_fragment; src_fragment = src_fragment->m_next_fragment) { - ON_SubDLimitMeshFragment* fragment = CopyCallbackFragment(src_fragment); + ON_SubDMeshFragment* fragment = CopyCallbackFragment(src_fragment); AddFinishedFragment(fragment); } } } -void ON_SubDLimitMeshImpl::ClearTree() +void ON_SubDMeshImpl::ClearTree() { if (nullptr != m_fragment_tree) { @@ -1394,45 +2357,22 @@ void ON_SubDLimitMeshImpl::ClearTree() } } -const ON_RTree& ON_SubDLimitMeshImpl::FragmentTree() const +const ON_RTree& ON_SubDMeshImpl::FragmentTree() const { if (nullptr != m_fragment_tree && nullptr != m_first_fragment) { - double Pbox[2][3]; ON_RTree* fragment_tree = new ON_RTree(); - for (const ON_SubDLimitMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) + for (const ON_SubDMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) { - const double* P = fragment->m_P; - const size_t P_stride = fragment->m_P_stride; - if (nullptr == P || P_stride < 3 || fragment->m_P_count <= 0) - continue; - const double* P1 = P + P_stride*fragment->m_P_count; - Pbox[0][0] = Pbox[1][0] = P[0]; - Pbox[0][1] = Pbox[1][1] = P[1]; - Pbox[0][2] = Pbox[1][2] = P[2]; - for (P += P_stride; P < P1; P += P_stride) - { - if (P[0] < Pbox[0][0]) - Pbox[0][0] = P[0]; - else if (P[0] > Pbox[1][0]) - Pbox[1][0] = P[0]; - if (P[1] < Pbox[0][1]) - Pbox[0][1] = P[1]; - else if (P[1] > Pbox[1][1]) - Pbox[1][1] = P[1]; - if (P[2] < Pbox[0][2]) - Pbox[0][2] = P[2]; - else if (P[2] > Pbox[1][2]) - Pbox[1][2] = P[2]; - fragment_tree->Insert(Pbox[0], Pbox[1], (void*)fragment); - } + if (fragment->PointCount() > 0 ) + fragment_tree->Insert(&(fragment->m_surface_bbox.m_min.x), &(fragment->m_surface_bbox.m_max.x), (void*)fragment); } - const_cast< ON_SubDLimitMeshImpl* >(this)->m_fragment_tree = fragment_tree; + const_cast< ON_SubDMeshImpl* >(this)->m_fragment_tree = fragment_tree; } return (nullptr == m_fragment_tree ) ? ON_RTree::Empty : *m_fragment_tree; } -bool ON_SubDLimitMeshImpl::GetTightBoundingBox( +bool ON_SubDMeshImpl::GetTightBoundingBox( ON_BoundingBox& bbox, bool bGrowBox, const ON_Xform* xform @@ -1448,10 +2388,8 @@ bool ON_SubDLimitMeshImpl::GetTightBoundingBox( } else { - for (const ON_SubDLimitMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) - { - ON_GetPointListBoundingBox(3, 0, fragment->m_P_count, (int)fragment->m_P_stride, fragment->m_P, local_bbox, fragment != m_first_fragment, xform); - } + for (const ON_SubDMeshFragment* fragment = m_first_fragment; nullptr != fragment; fragment = fragment->m_next_fragment) + ON_GetPointListBoundingBox(3, 0, fragment->PointCount(), (int)fragment->m_P_stride, fragment->m_P, local_bbox, fragment != m_first_fragment, xform); } if (bGrowBox && bbox.IsValid()) @@ -1463,11 +2401,11 @@ bool ON_SubDLimitMeshImpl::GetTightBoundingBox( } #if defined(ON_HAS_RVALUEREF) -ON_SubDLimitMesh::ON_SubDLimitMesh( ON_SubDLimitMesh&& src) ON_NOEXCEPT +ON_SubDMesh::ON_SubDMesh( ON_SubDMesh&& src) ON_NOEXCEPT : m_impl_sp(std::move(src.m_impl_sp)) {} -ON_SubDLimitMesh& ON_SubDLimitMesh::operator=(ON_SubDLimitMesh&& src) +ON_SubDMesh& ON_SubDMesh::operator=(ON_SubDMesh&& src) { if (this != &src) { @@ -1478,49 +2416,49 @@ ON_SubDLimitMesh& ON_SubDLimitMesh::operator=(ON_SubDLimitMesh&& src) } #endif -ON_SubDDisplayParameters ON_SubDLimitMesh::DisplayParameters() const +ON_SubDDisplayParameters ON_SubDMesh::DisplayParameters() const { - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + const ON_SubDMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) { ON_SubDDisplayParameters dp; - dp.m_display_density = impl->m_display_density; + dp.SetDisplayDensity(impl->m_display_density); return dp; } return ON_SubDDisplayParameters::Empty; } -unsigned int ON_SubDLimitMesh::DisplayDensity() const +unsigned int ON_SubDMesh::DisplayDensity() const { - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + const ON_SubDMeshImpl* impl = m_impl_sp.get(); return (nullptr != impl) ? impl->m_display_density : 0; } -unsigned int ON_SubDLimitMesh::FragmentCount() const +unsigned int ON_SubDMesh::FragmentCount() const { - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + const ON_SubDMeshImpl* impl = m_impl_sp.get(); return (nullptr != impl) ? impl->m_fragment_count : 0; } -const ON_SubDLimitMeshFragment* ON_SubDLimitMesh::FirstFragment() const +const ON_SubDMeshFragment* ON_SubDMesh::FirstFragment() const { - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + const ON_SubDMeshImpl* impl = m_impl_sp.get(); return (nullptr != impl) ? impl->m_first_fragment : nullptr; } - -const ON_SubDLimitMeshFragment* ON_SubDLimitMesh::FaceFragment( + +const ON_SubDMeshFragment* ON_SubDMesh::FaceFragment( const class ON_SubDFace* face ) const { if (nullptr == face) return nullptr; - for (const ON_SubDLimitMeshFragment* fragment = FirstFragment(); nullptr != fragment; fragment = fragment->m_next_fragment) + for (const ON_SubDMeshFragment* fragment = FirstFragment(); nullptr != fragment; fragment = fragment->m_next_fragment) { if (face == fragment->m_face) return fragment; @@ -1528,7 +2466,7 @@ const ON_SubDLimitMeshFragment* ON_SubDLimitMesh::FaceFragment( return nullptr; } -bool ON_SubDLimitMesh::GetFaceCenterPointAndNormal( +bool ON_SubDMesh::GetFaceCenterPointAndNormal( const class ON_SubDFace* face, double* P, double* N @@ -1546,15 +2484,15 @@ bool ON_SubDLimitMesh::GetFaceCenterPointAndNormal( N[1] = ON_DBL_QNAN; N[2] = ON_DBL_QNAN; } - const ON_SubDLimitMeshFragment* fragment = FaceFragment(face); + const ON_SubDMeshFragment* fragment = FaceFragment(face); if (nullptr == fragment) return false; if (nullptr == fragment->m_P || nullptr == fragment->m_N) return false; const unsigned int n = fragment->m_grid.m_side_segment_count; - const unsigned int P_dex = fragment->IsCompleteFragment() ? (n*(n + 2) / 2) : 0; - if (P_dex >= (unsigned int)fragment->m_P_count) - return nullptr; + const unsigned int P_dex = fragment->IsFullFaceFragment() ? (n*(n + 2) / 2) : 0; + if (P_dex >= fragment->PointCount()) + return false; const double* fragment_P = fragment->m_P + (P_dex * fragment->m_P_stride); const double* fragment_N = fragment->m_N + (P_dex * fragment->m_N_stride); if (nullptr != P) @@ -1573,7 +2511,7 @@ bool ON_SubDLimitMesh::GetFaceCenterPointAndNormal( } -bool ON_SubDLimitMesh::GetEdgeCenterPointAndNormal( +bool ON_SubDMesh::GetEdgeCenterPointAndNormal( const class ON_SubDEdge* edge, unsigned int edge_face_index, double* P, @@ -1604,17 +2542,17 @@ bool ON_SubDLimitMesh::GetEdgeCenterPointAndNormal( unsigned int P_dex = ON_UNSET_UINT_INDEX; bool bIsPartialFragment = false; - const ON_SubDLimitMeshFragment* fragment = nullptr; + const ON_SubDMeshFragment* fragment = nullptr; for ( fragment = FaceFragment(face); - nullptr != fragment && fragment->m_face == face; + nullptr != fragment && fragment->m_face == face; fragment = bIsPartialFragment ? fragment->m_next_fragment : nullptr ) { - bIsPartialFragment = fragment->IsPartialFragment(); + bIsPartialFragment = fragment->IsFaceCornerFragment(); for (unsigned int grid_side_index = 0; grid_side_index < 4; grid_side_index++) { - const ON_SubDEdge* grid_side_edge = fragment->Edge(grid_side_index); + const ON_SubDEdge* grid_side_edge = fragment->SubDEdge(grid_side_index); if (edge != grid_side_edge) continue; const unsigned int n = fragment->m_grid.m_side_segment_count; @@ -1622,7 +2560,7 @@ bool ON_SubDLimitMesh::GetEdgeCenterPointAndNormal( break; if (bIsPartialFragment) { - const ON_SubDVertex* v = fragment->Vertex(grid_side_index); + const ON_SubDVertex* v = fragment->SubDVertex(grid_side_index); if (nullptr != v) { const unsigned int evi = (0 == face->EdgeDirection(fei)) ? 0U : 1U; @@ -1631,7 +2569,7 @@ bool ON_SubDLimitMesh::GetEdgeCenterPointAndNormal( P_dex = fragment->m_grid.m_S[grid_side_index*n]; } } - else if (fragment->IsCompleteFragment()) + else if (fragment->IsFullFaceFragment()) { P_dex = fragment->m_grid.m_S[grid_side_index*n + n / 2]; } @@ -1642,7 +2580,7 @@ bool ON_SubDLimitMesh::GetEdgeCenterPointAndNormal( } if (nullptr == fragment) return false; - if (P_dex >= (unsigned int)fragment->m_P_count) + if (P_dex >= fragment->PointCount()) return false; const double* fragment_P = fragment->m_P + (P_dex * fragment->m_P_stride); const double* fragment_N = fragment->m_N + (P_dex * fragment->m_N_stride); @@ -1661,38 +2599,38 @@ bool ON_SubDLimitMesh::GetEdgeCenterPointAndNormal( return true; } -bool ON_SubDLimitMesh::Update( +bool ON_SubDMesh::Update( bool bShareUpdate ) { return false; } -bool ON_SubDLimitMesh::IsEmpty() const +bool ON_SubDMesh::IsEmpty() const { - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + const ON_SubDMeshImpl* impl = m_impl_sp.get(); return (nullptr == impl || 0 == impl->m_fragment_count ); } -const ON_RTree& ON_SubDLimitMesh::FragmentTree() const +const ON_RTree& ON_SubDMesh::FragmentTree() const { - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + const ON_SubDMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) return impl->FragmentTree(); return ON_RTree::Empty; } -ON_BoundingBox ON_SubDLimitMesh::BoundingBox() const +ON_BoundingBox ON_SubDMesh::BoundingBox() const { - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + const ON_SubDMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) return impl->m_bbox; return ON_BoundingBox::EmptyBoundingBox; } -ON_SubD ON_SubDLimitMesh::SubD() const +ON_SubD ON_SubDMesh::SubD() const { - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + const ON_SubDMeshImpl* impl = m_impl_sp.get(); if ( nullptr == impl ) return ON_SubD::Empty; ON_SubD subd; @@ -1700,9 +2638,9 @@ ON_SubD ON_SubDLimitMesh::SubD() const return subd; } -ON_SubDRef ON_SubDLimitMesh::SubDRef() const +ON_SubDRef ON_SubDMesh::SubDRef() const { - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + const ON_SubDMeshImpl* impl = m_impl_sp.get(); if ( nullptr == impl ) return ON_SubDRef::Empty; ON_SubDRef subd_ref; @@ -1711,68 +2649,54 @@ ON_SubDRef ON_SubDLimitMesh::SubDRef() const return subd_ref; } -bool ON_SubDLimitMesh::GetTightBoundingBox( +bool ON_SubDMesh::GetTightBoundingBox( ON_BoundingBox& bbox, bool bGrowBox, const ON_Xform* xform ) const { - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + const ON_SubDMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) return impl->GetTightBoundingBox(bbox,bGrowBox,xform); return (bGrowBox && bbox.IsValid()); } -ON_SubD::FacetType ON_SubDLimitMesh::GridType() const -{ - const ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); - if (nullptr != impl) - return impl->m_facet_type; - return ON_SubD::FacetType::Unset; -} - -void ON_SubDLimitMesh::Clear() +void ON_SubDMesh::Clear() { m_impl_sp.reset(); } -void ON_SubDLimitMesh::ClearTree() +void ON_SubDMesh::ClearTree() { - ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); + ON_SubDMeshImpl* impl = m_impl_sp.get(); if (nullptr != impl) impl->ClearTree(); } -unsigned int ON_SubDLimitMesh::ContentSerialNumber() const -{ - ON_SubDLimitMeshImpl* impl = m_impl_sp.get(); - return (nullptr != impl) ? impl->m_limit_mesh_content_serial_number : 0; -} - -ON_SubDLimitMesh& ON_SubDLimitMesh::CopyFrom( - const ON_SubDLimitMesh& src +ON_SubDMesh& ON_SubDMesh::CopyFrom( + const ON_SubDMesh& src ) { if (this != &src) { m_impl_sp.reset(); - const ON_SubDLimitMeshImpl* src_impl = src.m_impl_sp.get(); + const ON_SubDMeshImpl* src_impl = src.m_impl_sp.get(); if (nullptr != src_impl) { - ON_SubDLimitMeshImpl* impl = new ON_SubDLimitMeshImpl(*src_impl); - std::shared_ptr< ON_SubDLimitMeshImpl > new_impl_sp(impl); + ON_SubDMeshImpl* impl = new ON_SubDMeshImpl(*src_impl); + std::shared_ptr< ON_SubDMeshImpl > new_impl_sp(impl); m_impl_sp.swap(new_impl_sp); } } return *this; } -void ON_SubDLimitMesh::Swap( - ON_SubDLimitMesh& a, - ON_SubDLimitMesh& b +void ON_SubDMesh::Swap( + ON_SubDMesh& a, + ON_SubDMesh& b ) { - if (&a == &ON_SubDLimitMesh::Empty || &b == &ON_SubDLimitMesh::Empty) + if (&a == &ON_SubDMesh::Empty || &b == &ON_SubDMesh::Empty) { ON_SubDIncrementErrorCount(); } @@ -1782,11 +2706,11 @@ void ON_SubDLimitMesh::Swap( } } -ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensity( +const ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensity( unsigned int display_density ) { - if ( display_density > ON_SubDLimitMesh::MaximumDisplayDensity ) + if ( display_density > ON_SubDDisplayParameters::MaximumDensity ) return ON_SUBD_RETURN_ERROR(ON_SubDDisplayParameters::Empty); ON_SubDDisplayParameters limit_mesh_parameters; @@ -1794,4 +2718,474 @@ ON_SubDDisplayParameters ON_SubDDisplayParameters::CreateFromDisplayDensity( return limit_mesh_parameters; } -#endif +unsigned int ON_SubDDisplayParameters::DisplayDensity() const +{ + return m_display_density; +} + +void ON_SubDDisplayParameters::SetDisplayDensity(unsigned int display_density) +{ + if (display_density <= ON_SubDDisplayParameters::MaximumDensity) + m_display_density = display_density; +} + +ON_SubDComponentLocation ON_SubDDisplayParameters::MeshLocation() const +{ + return m_bControlNetMesh ? ON_SubDComponentLocation::ControlNet : ON_SubDComponentLocation::Surface; +} + +void ON_SubDDisplayParameters::SetMeshLocation(ON_SubDComponentLocation mesh_location) +{ + m_bControlNetMesh = (ON_SubDComponentLocation::ControlNet == mesh_location) ? true : false; +} + +bool ON_SubDDisplayParameters::AddNgons() const +{ + return m_bSkipMeshNgons ? false : true; +} + +void ON_SubDDisplayParameters::SetAddNgons(bool bAddNgons) +{ + m_bSkipMeshNgons = bAddNgons ? false : true; +} + +bool ON_SubDDisplayParameters::AddFakeEvaluationParameters() const +{ + return m_bSkipFakeEvaluationParameters ? false : true; +} + +void ON_SubDDisplayParameters::SetAddFakeEvaluationParameters(bool bAddFakeEvaluationParameters) +{ + m_bSkipFakeEvaluationParameters = bAddFakeEvaluationParameters ? false : true; +} + +unsigned char ON_SubDDisplayParameters::EncodeAsUnsignedChar() const +{ + unsigned char encoded_parameters; + if ( + ON_SubDDisplayParameters::Default.m_display_density == m_display_density + && ON_SubDDisplayParameters::Default.MeshLocation() == MeshLocation() + && ON_SubDDisplayParameters::Default.m_bSkipMeshNgons == m_bSkipMeshNgons + && ON_SubDDisplayParameters::Default.m_bSkipFakeEvaluationParameters == m_bSkipFakeEvaluationParameters + ) + { + // use defaults + encoded_parameters = 0; + } + else + { + const unsigned int density + = (m_display_density <= ON_SubDDisplayParameters::MaximumDensity) + ? m_display_density + : ON_SubDDisplayParameters::DefaultDensity + ; + + const unsigned char density_as_char + = ((unsigned char)(density & ((unsigned int)ON_SubDDisplayParameters::subd_mesh_density_mask))); + + const unsigned char location + = (ON_SubDComponentLocation::ControlNet == MeshLocation()) + ? ON_SubDDisplayParameters::subd_mesh_location_bit + : 0; + + const unsigned char skip_ngons + = (m_bSkipMeshNgons) + ? ON_SubDDisplayParameters::subd_mesh_skip_ngons_bit + : 0; + + const unsigned char skip_fakeevalparams + = (m_bSkipFakeEvaluationParameters) + ? ON_SubDDisplayParameters::subd_mesh_skip_fakeevalparams_bit + : 0; + + const unsigned char nondefault_settings = ON_SubDDisplayParameters::subd_mesh_nondefault_bit; + + encoded_parameters + = nondefault_settings + | density_as_char + | location + | skip_ngons + | skip_fakeevalparams + ; + } + return encoded_parameters; +} + +const ON_SubDDisplayParameters ON_SubDDisplayParameters::DecodeFromUnsignedChar( + unsigned char encoded_parameters +) +{ + ON_SubDDisplayParameters p(ON_SubDDisplayParameters::Default); + + if (0 != (ON_SubDDisplayParameters::subd_mesh_nondefault_bit & encoded_parameters)) + { + const unsigned char density = (ON_SubDDisplayParameters::subd_mesh_density_mask & encoded_parameters); + const unsigned char location = (ON_SubDDisplayParameters::subd_mesh_location_bit & encoded_parameters); + const unsigned char skip_ngons = (ON_SubDDisplayParameters::subd_mesh_skip_ngons_bit & encoded_parameters); + const unsigned char skip_fakeevalparams = (ON_SubDDisplayParameters::subd_mesh_skip_fakeevalparams_bit & encoded_parameters); + p.m_display_density = (unsigned int)density; + if (0 != location) + p.SetMeshLocation( ON_SubDComponentLocation::ControlNet ); + if (0 != skip_ngons) + p.m_bSkipMeshNgons = true; + if (0 != skip_fakeevalparams) + p.m_bSkipFakeEvaluationParameters = true; + } + + return p; +} + +bool ON_SubDDisplayParameters::UseMultipleThreads() const +{ + return m_bUseMultipleThreads; +} + +void ON_SubDDisplayParameters::SetUseMultipleThreads(bool bUseMultipleThreads) +{ + m_bUseMultipleThreads = bUseMultipleThreads ? true : false; +} + +ON_Terminator * ON_SubDDisplayParameters::Terminator() const +{ + return m_terminator; +} + +void ON_SubDDisplayParameters::SetTerminator(ON_Terminator * terminator) +{ + m_terminator = terminator; +} + +ON_ProgressReporter * ON_SubDDisplayParameters::ProgressReporter() const +{ + return m_progress_reporter; +} + +const ON_Interval ON_SubDDisplayParameters::ProgressReporterInterval() const +{ + return m_progress_reporter_interval; +} + +void ON_SubDDisplayParameters::SetProgressReporter(ON_ProgressReporter * progress_reporter, ON_Interval progress_reporter_interval) +{ + m_progress_reporter = progress_reporter; + m_progress_reporter_interval = progress_reporter_interval; +} + +ON_SubDMeshFragmentIterator::ON_SubDMeshFragmentIterator(const class ON_SubDMesh limit_mesh) +{ + m_limit_mesh = limit_mesh; + m_subd.ShareDimple(m_limit_mesh.SubD()); +} + +ON_SubDMeshFragmentIterator::ON_SubDMeshFragmentIterator(ON_SubDRef& subd_ref) +{ + m_subd.ShareDimple(subd_ref.SubD()); + m_fit = m_subd.FaceIterator(); + m_bFromFaceFragments = true; +} + +ON_SubDMeshFragmentIterator::ON_SubDMeshFragmentIterator(const ON_SubD& subd) +{ + m_subd.ShareDimple(subd); + m_fit = m_subd.FaceIterator(); + m_bFromFaceFragments = true; +} + +ON_SubDMeshFragmentIterator::ON_SubDMeshFragmentIterator(const ON_SubDFaceIterator& fit) +{ + m_subd.ShareDimple(fit.SubD()); + m_fit = fit; + m_bFromFaceFragments = true; +} + +void ON_SubDMeshFragmentIterator::Internal_CopyFrom(const ON_SubDMeshFragmentIterator& src) +{ + m_limit_mesh = src.m_limit_mesh; + m_subd.ShareDimple(src.m_subd); // <- reason default copy ctor and op= can't be used. + m_fit = src.m_fit; + m_current_fragment = src.m_current_fragment; + m_bFromFaceFragments = src.m_bFromFaceFragments; + m_bHaveCounts = src.m_bHaveCounts; + m_maximum_mesh_density = src.m_maximum_mesh_density; + m_full_size_fragment_count = src.m_full_size_fragment_count; + m_half_size_fragment_count = src.m_half_size_fragment_count; +} + +ON_SubDMeshFragmentIterator::ON_SubDMeshFragmentIterator(const ON_SubDMeshFragmentIterator& src) +{ + Internal_CopyFrom(src); +} + +ON_SubDMeshFragmentIterator& ON_SubDMeshFragmentIterator::operator=(const ON_SubDMeshFragmentIterator& src) +{ + if (this != &src) + Internal_CopyFrom(src); + return *this; +} + + +const ON_BoundingBox ON_SubDMeshFragmentIterator::BoundingBox() const +{ + return + (ON_SubDComponentLocation::ControlNet == SubDAppearance()) + ? ControlNetQuadBoundingBox() + : SurfaceBoundingBox(); +} + + +const ON_BoundingBox ON_SubDMeshFragmentIterator::SurfaceBoundingBox() const +{ + ON_BoundingBox bbox; + if (m_bFromFaceFragments) + m_subd.GetTightBoundingBox(bbox); + else + bbox = m_limit_mesh.BoundingBox(); + return bbox; +} + +const ON_BoundingBox ON_SubDMeshFragmentIterator::ControlNetQuadBoundingBox() const +{ + ON_BoundingBox bbox; + if (m_bFromFaceFragments) + bbox = m_subd.BoundingBox(); + else + { + ON_SubDMeshFragmentIterator local_frit(*this); + for (const ON_SubDMeshFragment* fragment = local_frit.FirstFragment(); nullptr != fragment; fragment = local_frit.NextFragment()) + { + bbox.Union(fragment->ControlNetQuadBoundingBox()); + } + } + return bbox; +} + +bool ON_SubDMeshFragmentIterator::IsEmpty() const +{ + const ON_SubDMeshFragment* f0 = nullptr; + if (m_bFromFaceFragments) + { + ON_SubDFaceIterator local_fit(m_fit); // cannot modify fit + for (const ON_SubDFace* f = local_fit.FirstFace(); nullptr != f; f = local_fit.NextFace()) + { + f0 = f->MeshFragments(); + if (nullptr != f0) + break; + } + } + else + { + f0 = m_limit_mesh.FirstFragment(); + } + return (nullptr == f0); +} + +const ON_SubDMeshFragment* ON_SubDMeshFragmentIterator::FirstFragment() +{ + if (m_bFromFaceFragments) + { + m_current_fragment = nullptr; + for (const ON_SubDFace* f = m_fit.FirstFace(); nullptr != f; f = m_fit.NextFace()) + { + m_current_fragment = f->MeshFragments(); + if (nullptr != m_current_fragment) + break; + } + } + else + { + m_current_fragment = m_limit_mesh.FirstFragment(); + } + return m_current_fragment; +} + +const ON_SubDMeshFragment* ON_SubDMeshFragmentIterator::NextFragment() +{ + if (nullptr != m_current_fragment) + { + m_current_fragment = m_current_fragment->m_next_fragment; + if (nullptr == m_current_fragment && m_bFromFaceFragments) + { + for (const ON_SubDFace* f = m_fit.NextFace(); nullptr != f; f = m_fit.NextFace()) + { + m_current_fragment = f->MeshFragments(); + if (nullptr != m_current_fragment) + break; + } + } + } + return m_current_fragment; +} + +const ON_SubDMeshFragment* ON_SubDMeshFragmentIterator::CurrentFragment() +{ + return m_current_fragment; +} + +const ON_SubD& ON_SubDMeshFragmentIterator::SubD() const +{ + return m_subd; +} + +unsigned int ON_SubDMeshFragmentIterator::FragmentCount() const +{ + if ( false == m_bHaveCounts ) + { + // lazy evaluation of this value because it's frequently never used. + ON_SubDMeshFragmentIterator frit(*this); + unsigned int side_count = 0; + unsigned int full_size_fragment_count = 0; + unsigned int half_size_fragment_count = 0; + for (const ON_SubDMeshFragment* fragment = frit.FirstFragment(); nullptr != fragment; fragment = frit.NextFragment()) + { + if ( 1 == fragment->m_face_fragment_count ) + { + ++full_size_fragment_count; + if (0 == side_count) + side_count = fragment->m_grid.SideSegmentCount(); + } + else if (fragment->m_face_fragment_count > 1) + { + ++half_size_fragment_count; + if (0 == side_count) + side_count = 2*fragment->m_grid.SideSegmentCount(); + } + } + unsigned int mesh_density = 0; + for (unsigned int i = 1; i < side_count; i *= 2) + ++mesh_density; + + m_maximum_mesh_density = mesh_density; + m_full_size_fragment_count = full_size_fragment_count; + m_half_size_fragment_count = half_size_fragment_count; + + m_bHaveCounts = true; + } + return m_full_size_fragment_count + m_half_size_fragment_count; +} + +unsigned int ON_SubDMeshFragmentIterator::MaximumMeshDensity() const +{ + return FragmentCount() > 0 ? m_maximum_mesh_density : 0; +} + +unsigned int ON_SubDMeshFragmentIterator::GetFragmentCounts( + unsigned int& full_size_fragment_count, + unsigned int& half_size_fragment_count + ) const +{ + unsigned int fragment_count = FragmentCount(); + + if (fragment_count > 0) + { + full_size_fragment_count = m_full_size_fragment_count; + half_size_fragment_count = m_half_size_fragment_count; + } + else + { + full_size_fragment_count = 0; + half_size_fragment_count = 0; + } + return fragment_count; +} + +unsigned int ON_SubDMeshFragmentIterator::FullSizeFragmentCount() const +{ + return FragmentCount() > 0 ? m_full_size_fragment_count : 0; +} + +unsigned int ON_SubDMeshFragmentIterator::HalfSizeFragmentCount() const +{ + return FragmentCount() > 0 ? m_half_size_fragment_count : 0; +} + + +unsigned int ON_SubDMeshFragmentIterator::MinimumMeshDensity() const +{ + return (MaximumMeshDensity() > 0 && HalfSizeFragmentCount() > 0) ? 1 : 0; +} + +unsigned int ON_SubDMeshFragmentIterator::ClampMeshDensity(unsigned int candidate_mesh_density) const +{ + if (candidate_mesh_density > MaximumMeshDensity()) + return MaximumMeshDensity(); + if (candidate_mesh_density < MinimumMeshDensity()) + return MinimumMeshDensity(); + return candidate_mesh_density; +} + +unsigned int ON_SubDMeshFragment::FullFragmentMeshQuadCountFromDensity( + unsigned int mesh_density +) +{ + if (mesh_density > ON_SubDDisplayParameters::MaximumDensity) + return 0; + const unsigned int grid_side_count = 1 << mesh_density; + return grid_side_count * grid_side_count; +} + +unsigned int ON_SubDMeshFragment::HalfFragmentMeshQuadCountFromDensity( + unsigned int mesh_density +) +{ + return + (mesh_density >= 1 && mesh_density <= ON_SubDDisplayParameters::MaximumDensity) + ? ON_SubDMeshFragment::FullFragmentMeshQuadCountFromDensity(mesh_density - 1) + : 0; +} + +unsigned int ON_SubDMeshFragment::FullFragmentMeshPointCountFromDensity( + unsigned int mesh_density +) +{ + if (mesh_density > ON_SubDDisplayParameters::MaximumDensity) + return 0; + const unsigned int grid_side_count = 1 << mesh_density; + return (grid_side_count+1) * (grid_side_count+1); +} + +unsigned int ON_SubDMeshFragment::HalfFragmentMeshPointCountFromDensity( + unsigned int mesh_density +) +{ + return + (mesh_density >= 1 && mesh_density <= ON_SubDDisplayParameters::MaximumDensity) + ? ON_SubDMeshFragment::FullFragmentMeshPointCountFromDensity(mesh_density - 1) + : 0; +} + +unsigned int ON_SubDMeshFragmentIterator::TotalQuadCount( + unsigned int mesh_density + ) const +{ + mesh_density = ClampMeshDensity(mesh_density); + unsigned int full_size_fragment_count = 0; + unsigned int half_size_fragment_count = 0; + ON_SubDMeshFragmentIterator::GetFragmentCounts(full_size_fragment_count, half_size_fragment_count); + const unsigned int full_size_quad_count = ON_SubDMeshFragment::FullFragmentMeshQuadCountFromDensity(mesh_density); + const unsigned int half_size_quad_count = ON_SubDMeshFragment::HalfFragmentMeshQuadCountFromDensity(mesh_density); + return full_size_fragment_count * full_size_quad_count + half_size_fragment_count * half_size_quad_count; +} + +unsigned int ON_SubDMeshFragmentIterator::TotalPointCount( + unsigned int mesh_density + ) const +{ + mesh_density = ClampMeshDensity(mesh_density); + unsigned int full_size_fragment_count = 0; + unsigned int half_size_fragment_count = 0; + ON_SubDMeshFragmentIterator::GetFragmentCounts(full_size_fragment_count, half_size_fragment_count); + // const unsigned int grid_side_count = 1 << mesh_density; + const unsigned int full_size_point_count = ON_SubDMeshFragment::FullFragmentMeshPointCountFromDensity(mesh_density); + const unsigned int half_size_point_count = ON_SubDMeshFragment::HalfFragmentMeshPointCountFromDensity(mesh_density); + return full_size_fragment_count * full_size_point_count + half_size_fragment_count * half_size_point_count; +} + +unsigned int ON_SubDMeshFragmentIterator::MaximumDensityQuadCount() const +{ + return TotalQuadCount(MaximumMeshDensity()); +} + +unsigned int ON_SubDMeshFragmentIterator::MaximumDensityPointCount() const +{ + return TotalPointCount(MaximumMeshDensity()); +} diff --git a/opennurbs_subd_frommesh.cpp b/opennurbs_subd_frommesh.cpp index 6a948dfb..b2983642 100644 --- a/opennurbs_subd_frommesh.cpp +++ b/opennurbs_subd_frommesh.cpp @@ -99,9 +99,166 @@ static bool TagCoincidentEdgeAsCrease( return false; } +static bool Internal_CandidateTagIsBetterCreaseEnd( + ON_SubD::VertexTag current_tag, + const ON_SubDVertex* candidate +) +{ + if (nullptr == candidate) + return false; + switch(current_tag) + { + case ON_SubD::VertexTag::Unset: + if (ON_SubD::VertexTag::Unset != candidate->m_vertex_tag ) + return true; + break; + case ON_SubD::VertexTag::Smooth: + if (candidate->IsDartOrCreaseOrCorner()) + return true; + break; + case ON_SubD::VertexTag::Dart: + if (candidate->IsCreaseOrCorner()) + return true; + break; + case ON_SubD::VertexTag::Crease: + if (candidate->IsCorner()) + return true; + break; + case ON_SubD::VertexTag::Corner: + break; + default: + break; + } + return false; +} + +static bool Internal_CreateFromMesh_ValidateNonmanifoldVertexSector( + const ON_SubDVertex* v, + const ON_SubDEdge* e, + ON_SubDSectorIterator& sit +) +{ + // e is non manifold edge + // v = nonmanifold corner vertex on e + // sit is a sector of v with e as a starting boundary + if (nullptr == v || v != sit.CenterVertex() || e != sit.CurrentEdge(0)) + return false; + + // k is used to protect against infinite looping if the topology + // around v is invalid. + const ON_SubDEdge* other_crease = nullptr; + const ON_SubDEdge* best_candidate_edge = nullptr; + const ON_SubDVertex* best_canditate_v1 = nullptr; + const ON_3dVector dir = -e->ControlNetDirectionFrom(v); + double best_dot = ON_DBL_QNAN; + for (unsigned short k = 0; k <= v->m_face_count; ++k) + { + const ON_SubDEdge* e1 = sit.CurrentEdge(1); + if (e1->IsCrease()) + { + other_crease = e1; + break; + } + const ON_SubDVertex* v1 = e1->OtherEndVertex(v); + if (nullptr == v1) + { + ON_SUBD_ERROR("invalid subd topology."); + return false; // invalid topology + } + const double d = dir * e1->ControlNetDirectionFrom(v); + if ( + nullptr == best_candidate_edge + || Internal_CandidateTagIsBetterCreaseEnd(best_canditate_v1->m_vertex_tag,v1) + || (nullptr != best_candidate_edge && best_canditate_v1->m_vertex_tag == v1->m_vertex_tag && d > best_dot) + ) + { + best_candidate_edge = e1; + best_dot = d; + best_canditate_v1 = v1; + } + if (nullptr == sit.NextFace(ON_SubDSectorIterator::StopAt::AnyCrease)) + break; + } + + if (nullptr == other_crease) + { + ON_SUBD_ERROR("bug in nonmanifold mesh to subd code."); + return false; + } + if (other_crease != e) + return true; // this sector is a valid corner vertex sector. + + if (nullptr == best_candidate_edge) + { + ON_SUBD_ERROR("bug in nonmanifold mesh to subd code."); + return false; + } + + // make best_candidate_edge a crease so corner sector is valid + const_cast(best_candidate_edge)->m_edge_tag = ON_SubD::EdgeTag::Crease; + const ON_SubDVertexEdgeProperties best_ep = best_canditate_v1->EdgeProperties(); + + ON_SubD::VertexTag vtag; + if ( 1 == best_ep.m_crease_edge_count && 2 == best_ep.m_min_edge_face_count && 2 == best_ep.m_max_edge_face_count) + vtag = ON_SubD::VertexTag::Dart; + else if ( 2 == best_ep.m_crease_edge_count && best_ep.m_max_edge_face_count <= 2 ) + vtag = ON_SubD::VertexTag::Crease; + else + vtag = ON_SubD::VertexTag::Corner; + + if (false == Internal_CandidateTagIsBetterCreaseEnd(vtag, best_canditate_v1)) + const_cast(best_canditate_v1)->m_vertex_tag = vtag; + + return true; +} + +static void Internal_CreateFromMesh_ValidateNonmanifoldVertex( + const ON_SubDVertex* v +) +{ + if ( + nullptr == v + || ON_SubD::VertexTag::Corner != v->m_vertex_tag + ) + return; + + for (unsigned short vei = 0; vei < v->m_edge_count; ++vei) + { + const ON_SubDEdge* e = v->Edge(vei); + if ( + nullptr == e + || ON_SubD::EdgeTag::Crease != e->m_edge_tag + || e->m_face_count <= 2 + ) + continue; + // e is non manifold - verify every attached face has a valid corner sector + for (unsigned short efi = 0; efi < e->m_face_count; ++efi) + { + const ON_SubDFace* f = e->Face(efi); + if (nullptr == f) + continue; + ON_SubDSectorIterator sit; + sit.Initialize(f, 0, v); + if (e != sit.CurrentEdge(0)) + { + sit.Initialize(f, 1, v); + if (e != sit.CurrentEdge(0)) + { + ON_SUBD_ERROR("bug in nonmanifold mesh to subd code."); + continue; + } + } + Internal_CreateFromMesh_ValidateNonmanifoldVertexSector(v,e,sit); + // convert best_candidate to a crease to make this a valid corner sector; + } + } + + return; +} + ON_SubD* ON_SubD::CreateFromMesh( const class ON_Mesh* level_zero_mesh, - const class ON_SubDFromMeshOptions* from_mesh_options, + const class ON_ToSubDParameters* from_mesh_options, ON_SubD* subd ) { @@ -118,7 +275,7 @@ ON_SubD* ON_SubD::CreateFromMesh( ON_Workspace ws; if (nullptr == from_mesh_options) - from_mesh_options = &ON_SubDFromMeshOptions::Smooth; + from_mesh_options = &ON_ToSubDParameters::Smooth; ON_3dPointListRef mesh_points(level_zero_mesh); const unsigned int mesh_point_count = mesh_points.PointCount(); @@ -168,12 +325,12 @@ ON_SubD* ON_SubD::CreateFromMesh( double max_cos_crease_angle = ON_UNSET_VALUE; double min_crease_angle_radians = -ON_UNSET_VALUE; - ON_SubDFromMeshOptions::InteriorCreaseOption crease_test + ON_ToSubDParameters::InteriorCreaseOption crease_test = (nullptr != from_mesh_options) ? from_mesh_options->InteriorCreaseTest() - : ON_SubDFromMeshOptions::InteriorCreaseOption::None; + : ON_ToSubDParameters::InteriorCreaseOption::None; - if (ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease == crease_test && nullptr != pointNormal ) + if (ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease == crease_test && nullptr != pointNormal ) { double min_angle = from_mesh_options->MinimumCreaseAngleRadians(); if (min_angle >= 0.0 && min_angle < ON_PI) @@ -190,12 +347,12 @@ ON_SubD* ON_SubD::CreateFromMesh( } else { - crease_test = ON_SubDFromMeshOptions::InteriorCreaseOption::None; + crease_test = ON_ToSubDParameters::InteriorCreaseOption::None; } } - else if (ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge != crease_test) + else if (ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge != crease_test) { - crease_test = ON_SubDFromMeshOptions::InteriorCreaseOption::None; + crease_test = ON_ToSubDParameters::InteriorCreaseOption::None; } // Get sub-D edge list @@ -340,11 +497,16 @@ ON_SubD* ON_SubD::CreateFromMesh( : (uptr = std::move(std::unique_ptr< ON_SubD >(new ON_SubD()))).get(); // new ON_SubD on the heap managed by uptr - ignore CLang warning #pragma ON_PRAGMA_WARNING_POP + // Make sure the subdimple is created before adding components. + if (nullptr == new_subd->SubDimple(true)) + return nullptr; + bool bHasTaggedVertices = false; + bool bHasNonmanifoldCornerVertices = false; unsigned int* Nid = nullptr; unsigned int nextNid = 0; - if (ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease == crease_test) + if (ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease == crease_test) { Nid = (unsigned int*)ws.GetIntMemory(mesh_point_count); memset(Nid, 0, mesh_point_count*sizeof(Nid[0])); @@ -445,7 +607,7 @@ ON_SubD* ON_SubD::CreateFromMesh( { // set the normal ids from the ON_Mesh m_V[] indices struct ON_MeshNGonEdge& mesh_edge_ref = mesh_edges[i]; - if (ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge == crease_test) + if (ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge == crease_test) { // All coincident mesh vertices generate interior creases mesh_edge_ref.Ni = mesh_edge_ref.i; @@ -508,7 +670,7 @@ ON_SubD* ON_SubD::CreateFromMesh( { mesh_edge = mesh_edges[mesh_edge_map[i]]; subd_edge_index = (ON__UINT_PTR)mesh_edge.e; - // Later, some of the ON_SubD::EdgeTag::Smooth tags are changed to ON_SubD::EdgeTag::Crease or ON_SubD::EdgeTag::X. + // Later, some of the ON_SubD::EdgeTag::Smooth tags are changed to ON_SubD::EdgeTag::Crease or ON_SubD::EdgeTag::SmoothX. mesh_edge.e = (mesh_edge.i <= mesh_edge.j) ? new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorWeight, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorWeight) @@ -545,7 +707,7 @@ ON_SubD* ON_SubD::CreateFromMesh( } if (EP.UnsignedCount() >= 3) - new_subd->AddFace(EP.UnsignedCount(), EP.Array()); + new_subd->AddFace(EP.Array(), EP.UnsignedCount()); } // Apply "ON_SubD::EdgeTag::Crease" tag to boundary and non-manifold edges and their vertices. @@ -557,21 +719,37 @@ ON_SubD* ON_SubD::CreateFromMesh( bHasTaggedVertices = true; + const ON_SubD::VertexTag vtag + = (edge->m_face_count > 2) + ? ON_SubD::VertexTag::Corner + : ON_SubD::VertexTag::Crease; + const_cast(edge)->m_edge_tag = ON_SubD::EdgeTag::Crease; for (unsigned int j = 0; j < 2; j++) { const ON_SubDVertex* vertex = edge->m_vertex[j]; - const_cast(vertex)->m_vertex_tag = ON_SubD::VertexTag::Crease; + if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag) + { + const_cast(vertex)->m_vertex_tag = vtag; + if (ON_SubD::VertexTag::Corner == vtag && edge->m_face_count > 2) + bHasNonmanifoldCornerVertices = true; + } } if ( 2 == edge->m_face_count ) interior_crease_count++; } + if (bHasNonmanifoldCornerVertices) + { + // may need to crease more edges to get valid corners RH-49843 + for (const ON_SubDVertex* v = new_subd->FirstVertex(); nullptr != v; v = v->m_next_vertex) + Internal_CreateFromMesh_ValidateNonmanifoldVertex(v); + } + if (interior_crease_count > 0) { - // Any interior vertex that has exactly one creased edges must be - // tagged as a dart. + // Any interior vertex that has exactly one creased edges must be tagged as a dart. unsigned int k = 0; for (const ON_SubDEdge* edge = new_subd->FirstEdge(); nullptr != edge; edge = edge->m_next_edge) { @@ -587,40 +765,16 @@ ON_SubD* ON_SubD::CreateFromMesh( for (unsigned int j = 0; j < 2; j++) { const ON_SubDVertex* vertex = edge->m_vertex[j]; - bool bIsDart = false; - for (unsigned int vei = 0; vei < vertex->m_edge_count; vei++) + if (ON_SubD::VertexTag::Crease != vertex->m_vertex_tag) + continue; + const ON_SubDVertexEdgeProperties ep = vertex->EdgeProperties(); + + if ( 0 == ep.m_null_edge_count && 0 == ep.m_unset_edge_count ) { - const ON_SubDEdge* v_edge = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr); - if (v_edge == edge) + if (1 == ep.m_crease_edge_count && ep.m_smooth_edge_count >= 1 && 2 == ep.m_min_edge_face_count && 2 == ep.m_max_edge_face_count) { - bIsDart = true; - continue; - } - if (nullptr == v_edge) - { - bIsDart = false; - break; - } - if (2 != v_edge->m_face_count) - { - bIsDart = false; - break; - } - if (ON_SubD::EdgeTag::Crease == v_edge->m_edge_tag) - { - bIsDart = false; - break; - } - } - if (bIsDart) - { - dart_count++; - if ( 1 == dart_count ) dart_index = j; - else if ( 2 != dart_count ) - { - ON_SubDIncrementErrorCount(); - break; + ++dart_count; } } } @@ -661,7 +815,7 @@ ON_SubD* ON_SubD::CreateFromMesh( if (2 == edge->m_face_count) { // first subdivision will convert edge to smooth - const_cast(edge)->m_edge_tag = ON_SubD::EdgeTag::X; + const_cast(edge)->m_edge_tag = ON_SubD::EdgeTag::SmoothX; // sector weights will be calculated when facet type is set const_cast(edge)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; const_cast(edge)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; @@ -697,23 +851,13 @@ ON_SubD* ON_SubD::CreateFromMesh( } } - // Discard interior smooth vertices with 2 edges - const ON_SubDVertex* next_vertex = new_subd->FirstVertex(); - for (const ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex) - { - next_vertex = vertex->m_next_vertex; - if (2 == vertex->m_edge_count && 2 == vertex->m_face_count && vertex->IsSmoothOrDart()) - { - ON_SubDEdgePtr eptr0 = vertex->m_edges[0].Reversed(); - ON_SubDEdgePtr eptr1 = vertex->m_edges[1]; - if (ON_SubD::EdgesCanBeMerged(eptr0, eptr1)) - new_subd->MergeEdges(eptr0, eptr1); - } - } - - if (bMergeColinearEdges) - new_subd->MergeColinearEdges(1e-6, 0.01, sin(0.25*ON_PI)); + { + const bool bMergeBoundaryEdges = from_mesh_options->MergeColinearBoundaryEdges(); + const bool bMergeInteriorCreaseEdges = from_mesh_options->MergeColinearInteriorEdges(); + const bool bMergeInteriorSmoothEdges = from_mesh_options->MergeColinearInteriorEdges(); + new_subd->MergeColinearEdges(bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges, 1e-6, 0.01, sin(0.25*ON_PI)); + } // All interior vertices must have at least 2 faces and three edges @@ -721,38 +865,10 @@ ON_SubD* ON_SubD::CreateFromMesh( uptr.release(); // If the input mesh is not oriented, fix the subd so it is. - if ( false == new_subd->IsOriented(0) ) - new_subd->Orient(0); + if ( false == new_subd->IsOriented() ) + new_subd->Orient(); - new_subd->RepairInvalidSectors(0); - - ON_SubD::SubDType subd_type - = (nullptr != from_mesh_options) - ? from_mesh_options->SubDType() - : ON_SubD::DefaultSubDType(); - new_subd->SetSubDType(subd_type); - - /* - // Vid[] - // Vid[] has mesh_point_count values. - // Vid[i] = Vid[j] if and only if mesh->m_V[i] and mesh->m_V[j] are coincident. - // Values in Vid[] run from 0 to VidCount-1. - // There are VidCount unique locations. - // Vindex[] is a permutation of (0, ..., mesh_point_count-1) - // 0 == Vid[Vindex[0]] <= ... <= Vid[Vindex[mesh_point_count-1]] = VidCount-1. - - - //const bool bConcaveCornerTest - // = nullptr != crease_parameters - // && crease_parameters->ConcaveCornerTestIsEnabled(); - - //const double min_cos_concave_corner_angle - // = bConcaveCornerTest - // ? (crease_parameters->MaximumConcaveCornerAngleRadians() < ON_PI ? cos(crease_parameters->MaximumConcaveCornerAngleRadians()) : -2.0) - // : 2.0; - */ - - if (ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner == from_mesh_options->ConvexCornerTest()) + if (ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner == from_mesh_options->ConvexCornerTest()) { // Add corners ON_SubDVertexIterator vit(*new_subd); @@ -833,5 +949,510 @@ ON_SubD* ON_SubD::CreateFromMesh( new_subd->UpdateEdgeSectorCoefficients(false); + return new_subd; } + + +static ON_SubDVertex* IndexVertex( + ON_SimpleArray< ON_SubDVertex* >& vertex, + ON_ClassArray< ON_ClassArray< ON_SimpleArray < int > > >& vert_index, + int x, int y, int z + ) +{ + int vi = vert_index[x][y][z]; + if (vi < 0) + return nullptr; + if (vi >= vertex.Count()) + return nullptr; + return vertex[vi]; +} + + +ON_SubD* ON_SubD::CreateSubDBox( + const ON_3dPoint corners[8], + ON_SubD::EdgeTag edge_tag, + unsigned int facecount_x, + unsigned int facecount_y, + unsigned int facecount_z, + ON_SubD* subd) +{ + if (ON_SubD::EdgeTag::Crease != edge_tag) + edge_tag = ON_SubD::EdgeTag::Smooth; + + if (nullptr == subd) + subd = new ON_SubD; + + ON_3dVector xdir = corners[1] - corners[0]; + ON_3dVector ydir = corners[3] - corners[0]; + ON_3dVector zdir = corners[4] - corners[0]; + + double x_len = xdir.LengthAndUnitize(); + double y_len = ydir.LengthAndUnitize(); + double z_len = zdir.LengthAndUnitize(); + + double dx = x_len / (double)facecount_x; + double dy = y_len / (double)facecount_y; + double dz = z_len / (double)facecount_z; + + ON_ClassArray< ON_ClassArray< ON_SimpleArray < int > > > vert_index; + ON_SimpleArray< ON_SubDVertex* > vertex; + // Allocate index arrays + vert_index.Reserve(facecount_x + 1); + vert_index.SetCount(facecount_x + 1); + for (unsigned int ix = 0; ix <= facecount_x; ix++) + { + vert_index[ix].Reserve(facecount_y + 1); + vert_index[ix].SetCount(facecount_y + 1); + for (unsigned int iy = 0; iy <= facecount_y; iy++) + { + vert_index[ix][iy].Reserve(facecount_z + 1); + vert_index[ix][iy].SetCount(facecount_z + 1); + for (unsigned int iz = 0; iz <= facecount_z; iz++) + { + vert_index[ix][iy][iz] = -1; + } + } + } + + // Make interior vertexes and store 3d indexes + for (unsigned int ix = 0; ix <= facecount_x; ix++) + { + for (unsigned int iy = 0; iy <= facecount_y; iy++) + { + for (unsigned int iz = 0; iz <= facecount_z; iz++) + { + int ccnt = 0; + if (ix == 0 || ix == facecount_x) + ccnt++; + if (iy == 0 || iy == facecount_y) + ccnt++; + if (iz == 0 || iz == facecount_z) + ccnt++; + if (ccnt > 0) // On some face + { + ON_SubD::VertexTag vtag = ON_SubD::VertexTag::Smooth; + if (edge_tag == ON_SubD::EdgeTag::Crease) + { + if(ccnt == 2) // On some edge + vtag = ON_SubD::VertexTag::Crease; + else if(ccnt == 3) // On some corner + vtag = ON_SubD::VertexTag::Corner; + } + ON_3dPoint P(corners[0] + (xdir * (dx * ix)) + (ydir * (dy * iy)) + (zdir * (dz * iz))); + vert_index[ix][iy][iz] = vertex.Count(); + vertex.AppendNew() = subd->AddVertex(vtag, &P.x); + if (nullptr == vertex.Last()) + return nullptr; + } + } + } + } + + ON_ClassArray< ON_SubDEdgePtr > box_edges[12]; + ON_SubDEdge* e = nullptr; + + // 4 edge chains parallel to x + for (unsigned int ix = 0; ix < facecount_x; ix++) + { + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, ix, 0, 0), IndexVertex(vertex, vert_index, ix + 1, 0, 0)); + box_edges[0].Append(ON_SubDEdgePtr::Create(e, 0)); + + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, ix, facecount_y, 0), IndexVertex(vertex, vert_index, ix + 1, facecount_y, 0)); + box_edges[2].Append(ON_SubDEdgePtr::Create(e, 0)); + + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, ix, 0, facecount_z), IndexVertex(vertex, vert_index, ix + 1, 0, facecount_z)); + box_edges[8].Append(ON_SubDEdgePtr::Create(e, 0)); + + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, ix, facecount_y, facecount_z), IndexVertex(vertex, vert_index, ix + 1, facecount_y, facecount_z)); + box_edges[10].Append(ON_SubDEdgePtr::Create(e, 0)); + } + + // 4 edge chains parallel to y + for (unsigned int iy = 0; iy < facecount_y; iy++) + { + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, 0, iy, 0), IndexVertex(vertex, vert_index, 0, iy + 1, 0)); + box_edges[3].Append(ON_SubDEdgePtr::Create(e, 0)); + + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, facecount_x, iy, 0), IndexVertex(vertex, vert_index, facecount_x, iy + 1, 0)); + box_edges[1].Append(ON_SubDEdgePtr::Create(e, 0)); + + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, facecount_x, iy, facecount_z), IndexVertex(vertex, vert_index, facecount_x, iy + 1, facecount_z)); + box_edges[9].Append(ON_SubDEdgePtr::Create(e, 0)); + + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, 0, iy, facecount_z), IndexVertex(vertex, vert_index, 0, iy + 1, facecount_z)); + box_edges[11].Append(ON_SubDEdgePtr::Create(e, 0)); + } + + // 4 edge chains parallel to z + for (unsigned int iz = 0; iz < facecount_z; iz++) + { + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, 0, 0, iz), IndexVertex(vertex, vert_index, 0, 0, iz + 1)); + box_edges[4].Append(ON_SubDEdgePtr::Create(e, 0)); + + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, facecount_x, 0, iz), IndexVertex(vertex, vert_index, facecount_x, 0, iz + 1)); + box_edges[5].Append(ON_SubDEdgePtr::Create(e, 0)); + + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, facecount_x, facecount_y, iz), IndexVertex(vertex, vert_index, facecount_x, facecount_y, iz + 1)); + box_edges[6].Append(ON_SubDEdgePtr::Create(e, 0)); + + e = subd->AddEdge(edge_tag, IndexVertex(vertex, vert_index, 0, facecount_y, iz), IndexVertex(vertex, vert_index, 0, facecount_y, iz + 1)); + box_edges[7].Append(ON_SubDEdgePtr::Create(e, 0)); + } + + ON_ClassArray< ON_ClassArray< ON_SubDEdgePtr > > face_edges[2]; + + // Bottom face + { + for (unsigned int iy = 0; iy <= facecount_y; iy++) + { + if (iy == 0) + face_edges[0].Append(box_edges[0]); + else if (iy == facecount_y) + face_edges[0].Append(box_edges[2]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); + for (unsigned int ix = 0; ix < facecount_x; ix++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, 0), IndexVertex(vertex, vert_index, ix + 1, iy, 0)); + row.Append(ON_SubDEdgePtr::Create(e, 0)); + } + } + } + + for (unsigned int ix = 0; ix <= facecount_x; ix++) + { + if (ix == 0) + face_edges[1].Append(box_edges[3]); + else if (ix == facecount_x) + face_edges[1].Append(box_edges[1]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); + for (unsigned int iy = 0; iy < facecount_y; iy++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, 0), IndexVertex(vertex, vert_index, ix, iy + 1, 0)); + col.Append(ON_SubDEdgePtr::Create(e, 0)); + } + } + } + + for (unsigned int iy = 0; iy < facecount_y; iy++) + { + for (unsigned int ix = 0; ix < facecount_x; ix++) + { + ON_SubDEdgePtr edge_ptrs[4]; + edge_ptrs[0] = face_edges[1][ix][iy]; + edge_ptrs[1] = face_edges[0][iy + 1][ix]; + edge_ptrs[2] = face_edges[1][ix + 1][iy].Reversed(); + edge_ptrs[3] = face_edges[0][iy][ix].Reversed(); + ON_SubDFace* f0 = subd->AddFace(edge_ptrs, 4); + if (nullptr == f0) + return nullptr; + } + } + } + + // Top face + { + face_edges[0].Empty(); + face_edges[1].Empty(); + + for (unsigned int iy = 0; iy <= facecount_y; iy++) + { + if (iy == 0) + face_edges[0].Append(box_edges[8]); + else if (iy == facecount_y) + face_edges[0].Append(box_edges[10]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); + for (unsigned int ix = 0; ix < facecount_x; ix++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, facecount_z), IndexVertex(vertex, vert_index, ix + 1, iy, facecount_z)); + row.Append(ON_SubDEdgePtr::Create(e, 0)); + } + } + } + + for (unsigned int ix = 0; ix <= facecount_x; ix++) + { + if (ix == 0) + face_edges[1].Append(box_edges[11]); + else if (ix == facecount_x) + face_edges[1].Append(box_edges[9]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); + for (unsigned int iy = 0; iy < facecount_y; iy++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, iy, facecount_z), IndexVertex(vertex, vert_index, ix, iy + 1, facecount_z)); + col.Append(ON_SubDEdgePtr::Create(e, 0)); + } + } + } + + for (unsigned int iy = 0; iy < facecount_y; iy++) + { + for (unsigned int ix = 0; ix < facecount_x; ix++) + { + ON_SubDEdgePtr edge_ptrs[4]; + edge_ptrs[0] = face_edges[0][iy][ix]; + edge_ptrs[1] = face_edges[1][ix + 1][iy]; + edge_ptrs[2] = face_edges[0][iy + 1][ix].Reversed(); + edge_ptrs[3] = face_edges[1][ix][iy].Reversed(); + ON_SubDFace* f0 = subd->AddFace(edge_ptrs, 4); + if (nullptr == f0) + return nullptr; + } + } + } + + // Front face + { + face_edges[0].Empty(); + face_edges[1].Empty(); + + for (unsigned int iz = 0; iz <= facecount_z; iz++) + { + if (iz == 0) + face_edges[0].Append(box_edges[0]); + else if (iz == facecount_z) + face_edges[0].Append(box_edges[8]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); + for (unsigned int ix = 0; ix < facecount_x; ix++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, 0, iz), IndexVertex(vertex, vert_index, ix + 1, 0, iz)); + row.Append(ON_SubDEdgePtr::Create(e, 0)); + } + } + } + + for (unsigned int ix = 0; ix <= facecount_x; ix++) + { + if (ix == 0) + face_edges[1].Append(box_edges[4]); + else if (ix == facecount_x) + face_edges[1].Append(box_edges[5]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); + for (unsigned int iz = 0; iz < facecount_z; iz++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, 0, iz), IndexVertex(vertex, vert_index, ix, 0, iz + 1)); + col.Append(ON_SubDEdgePtr::Create(e, 0)); + } + } + } + + for (unsigned int iz = 0; iz < facecount_z; iz++) + { + for (unsigned int ix = 0; ix < facecount_x; ix++) + { + ON_SubDEdgePtr edge_ptrs[4]; + edge_ptrs[0] = face_edges[0][iz][ix]; + edge_ptrs[1] = face_edges[1][ix + 1][iz]; + edge_ptrs[2] = face_edges[0][iz + 1][ix].Reversed(); + edge_ptrs[3] = face_edges[1][ix][iz].Reversed(); + ON_SubDFace* f0 = subd->AddFace(edge_ptrs, 4); + if (nullptr == f0) + return nullptr; + } + } + } + + // Back face + { + face_edges[0].Empty(); + face_edges[1].Empty(); + + for (unsigned int iz = 0; iz <= facecount_z; iz++) + { + if (iz == 0) + face_edges[0].Append(box_edges[2]); + else if (iz == facecount_z) + face_edges[0].Append(box_edges[10]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); + for (unsigned int ix = 0; ix < facecount_x; ix++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, facecount_y, iz), IndexVertex(vertex, vert_index, ix + 1, facecount_y, iz)); + row.Append(ON_SubDEdgePtr::Create(e, 0)); + } + } + } + + for (unsigned int ix = 0; ix <= facecount_x; ix++) + { + if (ix == 0) + face_edges[1].Append(box_edges[7]); + else if (ix == facecount_x) + face_edges[1].Append(box_edges[6]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); + for (unsigned int iz = 0; iz < facecount_z; iz++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, ix, facecount_y, iz), IndexVertex(vertex, vert_index, ix, facecount_y, iz + 1)); + col.Append(ON_SubDEdgePtr::Create(e, 0)); + } + } + } + + for (unsigned int iz = 0; iz < facecount_z; iz++) + { + for (unsigned int ix = 0; ix < facecount_x; ix++) + { + ON_SubDEdgePtr edge_ptrs[4]; + edge_ptrs[0] = face_edges[1][ix][iz]; + edge_ptrs[1] = face_edges[0][iz + 1][ix]; + edge_ptrs[2] = face_edges[1][ix + 1][iz].Reversed(); + edge_ptrs[3] = face_edges[0][iz][ix].Reversed(); + ON_SubDFace* f0 = subd->AddFace(edge_ptrs, 4); + if (nullptr == f0) + return nullptr; + } + } + } + + // Left face + { + face_edges[0].Empty(); + face_edges[1].Empty(); + + for (unsigned int iz = 0; iz <= facecount_z; iz++) + { + if (iz == 0) + face_edges[0].Append(box_edges[3]); + else if (iz == facecount_z) + face_edges[0].Append(box_edges[11]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); + for (unsigned int iy = 0; iy < facecount_y; iy++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, 0, iy, iz), IndexVertex(vertex, vert_index, 0, iy + 1, iz)); + row.Append(ON_SubDEdgePtr::Create(e, 0)); + + // mac compile warning // ON_3dPoint p0 = row.Last()->RelativeVertex(0)->ControlNetPoint(); + // mac compile warning // ON_3dPoint p1 = row.Last()->RelativeVertex(1)->ControlNetPoint(); + // mac compile warning // iy = iy; + } + } + } + + for (unsigned int iy = 0; iy <= facecount_y; iy++) + { + if (iy == 0) + face_edges[1].Append(box_edges[4]); + else if (iy == facecount_y) + face_edges[1].Append(box_edges[7]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); + for (unsigned int iz = 0; iz < facecount_z; iz++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, 0, iy, iz), IndexVertex(vertex, vert_index, 0, iy, iz + 1)); + col.Append(ON_SubDEdgePtr::Create(e, 0)); + + // mac compile warning // ON_3dPoint p0 = col.Last()->RelativeVertex(0)->ControlNetPoint(); + // mac compile warning // ON_3dPoint p1 = col.Last()->RelativeVertex(1)->ControlNetPoint(); + // mac compile warning // iy = iy; + } + } + } + + for (unsigned int iz = 0; iz < facecount_z; iz++) + { + for (unsigned int iy = 0; iy < facecount_y; iy++) + { + ON_SubDEdgePtr edge_ptrs[4]; + edge_ptrs[0] = face_edges[1][iy][iz]; + edge_ptrs[1] = face_edges[0][iz + 1][iy]; + edge_ptrs[2] = face_edges[1][iy + 1][iz].Reversed(); + edge_ptrs[3] = face_edges[0][iz][iy].Reversed(); + + // mac compile warning // ON_3dPoint p00 = edge_ptrs[0].RelativeVertex(0)->ControlNetPoint(); + // mac compile warning // ON_3dPoint p01 = edge_ptrs[0].RelativeVertex(1)->ControlNetPoint(); + + // mac compile warning // ON_3dPoint p10 = edge_ptrs[1].RelativeVertex(0)->ControlNetPoint(); + // mac compile warning // ON_3dPoint p11 = edge_ptrs[1].RelativeVertex(1)->ControlNetPoint(); + + // mac compile warning // ON_3dPoint p20 = edge_ptrs[2].RelativeVertex(0)->ControlNetPoint(); + // mac compile warning // ON_3dPoint p21 = edge_ptrs[2].RelativeVertex(1)->ControlNetPoint(); + + // mac compile warning // ON_3dPoint p30 = edge_ptrs[3].RelativeVertex(0)->ControlNetPoint(); + // mac compile warning // ON_3dPoint p31 = edge_ptrs[3].RelativeVertex(1)->ControlNetPoint(); + + ON_SubDFace* f0 = subd->AddFace(edge_ptrs, 4); + if (nullptr == f0) + return nullptr; + } + } + } + + // Right face + { + face_edges[0].Empty(); + face_edges[1].Empty(); + + for (unsigned int iz = 0; iz <= facecount_z; iz++) + { + if (iz == 0) + face_edges[0].Append(box_edges[1]); + else if (iz == facecount_z) + face_edges[0].Append(box_edges[9]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& row = face_edges[0].AppendNew(); + for (unsigned int iy = 0; iy < facecount_y; iy++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, facecount_x, iy, iz), IndexVertex(vertex, vert_index, facecount_x, iy + 1, iz)); + row.Append(ON_SubDEdgePtr::Create(e, 0)); + } + } + } + + for (unsigned int iy = 0; iy <= facecount_y; iy++) + { + if (iy == 0) + face_edges[1].Append(box_edges[5]); + else if (iy == facecount_y) + face_edges[1].Append(box_edges[6]); + else + { + ON_ClassArray< ON_SubDEdgePtr >& col = face_edges[1].AppendNew(); + for (unsigned int iz = 0; iz < facecount_z; iz++) + { + e = subd->AddEdge(ON_SubD::EdgeTag::Smooth, IndexVertex(vertex, vert_index, facecount_x, iy, iz), IndexVertex(vertex, vert_index, facecount_x, iy, iz + 1)); + col.Append(ON_SubDEdgePtr::Create(e, 0)); + } + } + } + + for (unsigned int iz = 0; iz < facecount_z; iz++) + { + for (unsigned int iy = 0; iy < facecount_y; iy++) + { + ON_SubDEdgePtr edge_ptrs[4]; + edge_ptrs[0] = face_edges[0][iz][iy]; + edge_ptrs[1] = face_edges[1][iy + 1][iz]; + edge_ptrs[2] = face_edges[0][iz + 1][iy].Reversed(); + edge_ptrs[3] = face_edges[1][iy][iz].Reversed(); + ON_SubDFace* f0 = subd->AddFace(edge_ptrs, 4); + if (nullptr == f0) + return nullptr; + } + } + } + + subd->SubDModifiedNofification(); + //subd->UpdateAllTagsAndSectorCoefficients(true); + + return subd; +} + diff --git a/opennurbs_subd_heap.cpp b/opennurbs_subd_heap.cpp index 6f6ca300..c9149389 100644 --- a/opennurbs_subd_heap.cpp +++ b/opennurbs_subd_heap.cpp @@ -3,7 +3,7 @@ #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 +// 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 @@ -77,23 +77,32 @@ void ON_SubD_FixedSizeHeap::Destroy() m_e_capacity = 0; m_f_capacity = 0; m_p_capacity = 0; - void* p[4] = { m_v, m_e, m_f, m_p }; + m_h_capacity = 0; + m_h_count = 0; + void* p[6] = { m_v, m_e, m_f, m_p, m_hash_table, m_hash_elements }; m_v = nullptr; m_e = nullptr; m_f = nullptr; m_p = nullptr; + m_hash_table = nullptr; + m_hash_elements = nullptr; ON_SubD__Free(p[0]); ON_SubD__Free(p[1]); ON_SubD__Free(p[2]); ON_SubD__Free(p[3]); + ON_SubD__Free(p[4]); + ON_SubD__Free(p[5]); } void ON_SubD_FixedSizeHeap::Reset() { + if (m_h_capacity > 0) + memset(m_hash_table, 0, m_h_capacity * sizeof(*m_hash_table)); m_v_index = 0; m_e_index = 0; m_f_index = 0; m_p_index = 0; + m_h_count = 0; } bool ON_SubD_FixedSizeHeap::InUse() const @@ -101,29 +110,79 @@ bool ON_SubD_FixedSizeHeap::InUse() const return (m_v_index > 0 || m_e_index > 0 || m_f_index>0 || m_p_index>0); } +class ON_SubD_FixedSizeHeap_ComponentPairHashElement +{ +public: + //static const ON_SubD_FixedSizeHeap_ComponentPairHashElement Empty; + ON_SubDComponentPtrPair m_pair; + ON_SubD_FixedSizeHeap_ComponentPairHashElement* m_next; -bool ON_SubD_FixedSizeHeap::ReserveSubDWorkspace( +}; + +bool ON_SubD_FixedSizeHeap::Internal_ReserveSubDWorkspace_HashTable() +{ + const unsigned int hash_capacity = (m_v_capacity > 0) ? (m_v_capacity / 4 + 1) : 0; + m_h_count = 0; + if (hash_capacity > m_h_capacity) + { + m_h_capacity = 0; + if (nullptr != m_hash_elements) + { + ON_SubD__Free(m_hash_elements); + m_hash_elements = nullptr; + } + if (nullptr != m_hash_table) + { + ON_SubD__Free(m_hash_table); + m_hash_table = nullptr; + } + m_hash_table = (ON_SubD_FixedSizeHeap_ComponentPairHashElement**)ON_SubD__Allocate(hash_capacity * sizeof(*m_hash_table)); + if (nullptr == m_hash_table) + return false; + m_hash_elements = (ON_SubD_FixedSizeHeap_ComponentPairHashElement*)ON_SubD__Allocate(m_v_capacity * sizeof(*m_hash_elements)); + if (nullptr == m_hash_elements) + { + ON_SubD__Free(m_hash_table); + m_hash_table = nullptr; + return false; + } + m_h_capacity = hash_capacity; + } + if ( m_h_capacity > 0 && nullptr != m_hash_table) + memset(m_hash_table, 0, m_h_capacity * sizeof(*m_hash_table)); + return true; +} + +bool ON_SubD_FixedSizeHeap::Internal_ReserveSubDWorkspace( size_t vertex_capacity, - size_t edge_capacity, size_t face_capacity, - size_t array_capacity + size_t array_capacity, + bool bEnableHash ) { - if (0 == vertex_capacity || (0 == edge_capacity && 0 == face_capacity && 0 == array_capacity)) + if ( vertex_capacity <= 0 || face_capacity <= 0 || array_capacity <= 0) { Destroy(); - return true; + return ON_SUBD_RETURN_ERROR(false); } - - if (m_v_capacity >= vertex_capacity && m_e_capacity >= edge_capacity && m_f_capacity >= face_capacity && m_p_capacity >= array_capacity) + const size_t edge_capacity = vertex_capacity + face_capacity - 1; // Euler formula + if (m_v_capacity >= vertex_capacity + && m_e_capacity >= edge_capacity + && m_f_capacity >= face_capacity + && m_p_capacity >= array_capacity + ) { Reset(); + if (bEnableHash) + Internal_ReserveSubDWorkspace_HashTable(); + else + m_h_count = ON_SubD_FixedSizeHeap::DisabledHashCount; return true; } Destroy(); - size_t max_capacity = 0xFFFFFFF0U; + size_t max_capacity = 0xFFFFFFU; if (vertex_capacity > max_capacity || edge_capacity > max_capacity || face_capacity > max_capacity || array_capacity > max_capacity) return ON_SUBD_RETURN_ERROR(false); @@ -146,6 +205,11 @@ bool ON_SubD_FixedSizeHeap::ReserveSubDWorkspace( m_e_capacity = (unsigned int)edge_capacity; m_f_capacity = (unsigned int)face_capacity; m_p_capacity = (unsigned int)array_capacity; + + if (bEnableHash) + Internal_ReserveSubDWorkspace_HashTable(); + else + m_h_count = ON_SubD_FixedSizeHeap::DisabledHashCount; return true; } @@ -154,62 +218,186 @@ bool ON_SubD_FixedSizeHeap::ReserveSubDWorkspace( return ON_SUBD_RETURN_ERROR(false); } - bool ON_SubD_FixedSizeHeap::ReserveSubDWorkspace( - ON_SubD::SubDType subdivision_type, - unsigned int extraordinary_valence + unsigned int sector_edge_count ) { - if (0 == extraordinary_valence) + if (0 == sector_edge_count) { Destroy(); return true; } - // capacity depends on extraordinary_valence and subdivision_type + const unsigned int k = (sector_edge_count <= 4) ? 0 : (sector_edge_count - 4); + const unsigned int v_capacity = 16 + 2 * k; + const unsigned int f_capacity = 9 + k; + const unsigned int p_capacity = 8*v_capacity + 2 * k; - bool bTri = (ON_SubD::SubDType::TriLoopWarren == subdivision_type); - - unsigned int ordinary_valence = bTri ? 6 : 4; - - if (extraordinary_valence < ordinary_valence) - extraordinary_valence = ordinary_valence; - - // When ON_SubD::FacetType::Unset == facet_type, - // the maximum of quad or tri capcacity is used. - // For all but the face array, tri capacity < quad capacity. - - const unsigned int v_capacity - = bTri - ? (extraordinary_valence + 6) - : (2 * extraordinary_valence + 8); // quads - - const unsigned int e_capacity - = bTri - ? (extraordinary_valence + 14) - : (3 * extraordinary_valence + 12); // quads or unset - - // const unsigned int f_capacity = extraordinary_valence + 7 for tris - // const unsigned int f_capacity = extraordinary_valence + 5 for quads - // 7 is alwasy used to accomodate unset as well - const unsigned int f_capacity = extraordinary_valence + 7; - - const unsigned int p_capacity = 2*(ordinary_valence*v_capacity + (extraordinary_valence - ordinary_valence)); - - return ReserveSubDWorkspace(v_capacity, e_capacity, f_capacity, p_capacity); + return Internal_ReserveSubDWorkspace(v_capacity, f_capacity, p_capacity, false); } +static unsigned int Internal_AtLeast4(unsigned int n) +{ + return (n > 4U) ? n : 4U; +} + +bool ON_SubD_FixedSizeHeap::ReserveSubDWorkspace( + const ON_SubDFace* center_face0 + ) +{ + unsigned int v_capacity = 0; + unsigned int f_capacity = 0; + unsigned int a_capacity = 0; + + for (;;) + { + if (nullptr == center_face0) + break; + + const unsigned int N = center_face0->m_edge_count; + if (N <= 2) + break; + + unsigned int S = 0; // Set S = sum of the number of edges attached to each vertex of center_face0. + unsigned int T = Internal_AtLeast4(N); // Set T = capacity required for vertex edge arrays on face subdivision vertices + unsigned int X = 0; + bool bValenceTwoVertices = false; // bValenceTwoVertices = true if center_face0 has a valence 2 vertex and we need the hash table + { + const ON_SubDEdgePtr* edges = center_face0->m_edge4; + ON__UINT_PTR edge_ptr; + const ON_SubDEdge* edge; + const ON_SubDVertex* vertex; + const ON_SubDFace* vertex_face; + unsigned int fei; + edge = center_face0->Edge(N - 1); + if (nullptr == edge) + break; + bool bEdgeIsHardCrease[2] = { false, edge->IsHardCrease() }; + for (fei = 0; fei < N; fei++, edges++) + { + if (4 == fei) + { + edges = center_face0->m_edgex; + if (nullptr == edges) + break; + } + edge_ptr = edges->m_ptr; + edge = ON_SUBD_EDGE_POINTER(edge_ptr); + if (nullptr == edge) + break; + bEdgeIsHardCrease[0] = bEdgeIsHardCrease[1]; + bEdgeIsHardCrease[1] = edge->IsHardCrease(); + vertex = edge->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)]; + if (nullptr == vertex) + break; + if (vertex->m_edge_count < 2) + break; + if (vertex->m_edge_count < vertex->m_face_count) + break; + S += vertex->m_edge_count; + X += Internal_AtLeast4(vertex->m_edge_count); + if ( bEdgeIsHardCrease[0] && bEdgeIsHardCrease[1] && vertex->IsCreaseOrCorner() ) + { + // If this vertex has multiple sectors, the other sectors are isolated from center_face0 by hard creases. + continue; + } + if (2 == vertex->m_edge_count) + { + // ring face has valence 2 vertex and the subdivision point for vertex_face + // may be reference by 2 different edges from center_face0 + bValenceTwoVertices = true; + } + for (unsigned short vfi = 0; vfi < vertex->m_face_count; ++vfi) + { + vertex_face = vertex->m_faces[vfi]; + if (nullptr == vertex_face || center_face0 == vertex_face) + continue; + T += Internal_AtLeast4(vertex_face->m_edge_count); + } + } + if (fei != N) + break; + } + + // NOTE: S >= 2*N + v_capacity = 2*(S - N) + 1; // maximum possible and occurs when all face0 edges are distinct and smooth + f_capacity = S; // maximum possible and occurs when all face0 edges are distinct and smooth + + // T = capacity required for vertex edge arrays on face subdivision vertices + // 4*(S-N) = capacity required for vertex edge arrays on edge subdivision vertices + // X = capacity required for vertex edge arrays on vertex subdivision vertices + // + a_capacity = 2*( X + T + 4 * (S - N) ); // Twice the number of edges from all subdivision vertices. + + return Internal_ReserveSubDWorkspace( + v_capacity, + f_capacity, + a_capacity, + (0U == center_face0->SubdivisionLevel()) || bValenceTwoVertices + ); + } + + Destroy(); + if (nullptr == center_face0 ) + return true; + + return ON_SUBD_RETURN_ERROR(false); +} + +bool ON_SubD_FixedSizeHeap::Internal_HashEnabled() const +{ + return (ON_SubD_FixedSizeHeap::DisabledHashCount != m_h_count && m_h_capacity > 0); +} + +unsigned int ON_SubD_FixedSizeHeap::Internal_Hash(ON_SubDComponentPtr component0) +{ + return Internal_HashEnabled() ? (((unsigned int)component0.Hash16FromTypeAndId()) % m_h_capacity) : 0U; +} + +ON_SubDVertex* ON_SubD_FixedSizeHeap::Internal_HashFindVertex1(unsigned int hash, ON_SubDComponentPtr component0) +{ + if (Internal_HashEnabled()) + { + for (ON_SubD_FixedSizeHeap_ComponentPairHashElement* e = m_hash_table[hash]; nullptr != e; e = e->m_next) + { + if (component0.m_ptr == e->m_pair.m_pair[0].m_ptr) + return e->m_pair.m_pair[1].Vertex(); + } + } + return nullptr; +} + +void ON_SubD_FixedSizeHeap::Internal_HashAddPair(unsigned int hash, ON_SubDComponentPtr component0, class ON_SubDVertex* vertex1) +{ + if (Internal_HashEnabled()) + { + if (vertex1->m_id == m_v_index) + { + ON_SubD_FixedSizeHeap_ComponentPairHashElement* e = &m_hash_elements[vertex1->m_id - 1]; + e->m_pair.m_pair[0] = component0; + e->m_pair.m_pair[1] = ON_SubDComponentPtr::Create(vertex1); + e->m_next = m_hash_table[hash]; + m_hash_table[hash] = e; + ++m_h_count; + } + else + { + ON_SUBD_ERROR("unexpected has table state"); + } + } +} ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( const double vertexP[3], - unsigned int edge_capacity, - unsigned int face_capacity + unsigned int edge_capacity ) { if (nullptr == m_v || m_v_index >= m_v_capacity) return ON_SUBD_RETURN_ERROR(nullptr); - if (edge_capacity + face_capacity + m_p_index >= m_p_capacity ) + const unsigned int face_capacity = edge_capacity; + + if (edge_capacity + face_capacity + m_p_index > m_p_capacity ) return ON_SUBD_RETURN_ERROR(nullptr); ON__UINT_PTR* a = nullptr; @@ -228,7 +416,7 @@ ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( memset(v, 0, sizeof(*v)); if (m_v_index > 0) { - // code in ON_SubDFaceNeighborhood.Subdivide() relies on + // code in ON_SubDFaceNeighborhood.Subdivide() relies on // m_next_vertex being set this way. m_v[m_v_index - 1].m_next_vertex = v; v->m_prev_vertex = &m_v[m_v_index - 1]; @@ -260,114 +448,178 @@ ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( const ON_SubDVertex* vertex0, - ON_SubD::SubDType subdivision_type, - bool bUseSavedSubdivisionPoint, - unsigned int edge_capacity, - unsigned int face_capacity + unsigned int edge_capacity ) { if ( nullptr == vertex0) return ON_SUBD_RETURN_ERROR(nullptr); - + double subdP[3]; - if (false == vertex0->GetSubdivisionPoint(subdivision_type,bUseSavedSubdivisionPoint,subdP)) + if (false == vertex0->GetSubdivisionPoint(subdP)) return ON_SUBD_RETURN_ERROR(nullptr); - ON_SubDVertex* v1 = AllocateVertex(subdP,edge_capacity,face_capacity); - - if ( nullptr == v1) + ON_SubDVertex* v1 = AllocateVertex(subdP, edge_capacity); + + if (nullptr == v1) return ON_SUBD_RETURN_ERROR(nullptr); - v1->m_level = vertex0->m_level+1; - + v1->SetSubdivisionLevel( vertex0->SubdivisionLevel() + 1 ); + v1->m_vertex_tag = vertex0->m_vertex_tag; - //if ( ON_SubD::SubDType::QuadCatmullClark == subdivision_type) - // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad; - //else if ( ON_SubD::SubDType::TriLoopWarren == subdivision_type) - // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Tri; - - if (bUseSavedSubdivisionPoint && subdivision_type == vertex0->SavedLimitPointType()) + if (vertex0->SurfacePointIsSet()) { - ON_SubDSectorLimitPoint limit_point; - if (vertex0->GetLimitPoint(subdivision_type, vertex0->m_faces[0], bUseSavedSubdivisionPoint, limit_point)) + // copy any cached limit point from vertex0 to v1. + ON_SubDSectorSurfacePoint limit_point; + if (vertex0->GetSurfacePoint(vertex0->m_faces[0], limit_point)) { if (nullptr == limit_point.m_sector_face) { - limit_point.m_next_sector_limit_point = (const ON_SubDSectorLimitPoint*)1; - v1->SetSavedLimitPoint(subdivision_type, limit_point); + limit_point.m_next_sector_limit_point = (const ON_SubDSectorSurfacePoint*)1; + v1->SetSavedSurfacePoint(true, limit_point); } } } - + + return v1; +} + +ON_SubDVertex * ON_SubD_FixedSizeHeap::FindOrAllocateVertex(const ON_SubDEdge * edge0) +{ + if ( nullptr == edge0) + return ON_SUBD_RETURN_ERROR(nullptr); + + const ON_SubDComponentPtr component0 = ON_SubDComponentPtr::Create(edge0); + const unsigned int hash = Internal_Hash(component0); + ON_SubDVertex* v1 = Internal_HashFindVertex1(hash, component0); + + if (nullptr != v1) + { + // found the previously allocated vertex + if (((unsigned int)v1->m_edge_capacity) < 4) + { + ON_SUBD_ERROR("edge capacity was too small when vertex was created."); + } + return v1; + } + + v1 = AllocateVertex(edge0); + if (nullptr == v1) + return ON_SUBD_RETURN_ERROR(nullptr); + Internal_HashAddPair(hash, component0, v1); + return v1; } ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( - const ON_SubDEdge* edge0, - ON_SubD::SubDType subdivision_type, - bool bUseSavedSubdivisionPoint, - unsigned int edge_capacity, - unsigned int face_capacity + const ON_SubDEdge* edge0 ) { if ( nullptr == edge0) return ON_SUBD_RETURN_ERROR(nullptr); - + double subdP[3]; - if (false == edge0->GetSubdivisionPoint(subdivision_type,bUseSavedSubdivisionPoint,subdP)) - return ON_SUBD_RETURN_ERROR(nullptr); - - ON_SubDVertex* v1 = AllocateVertex(subdP,edge_capacity,face_capacity); - if ( nullptr == v1) + if (false == edge0->GetSubdivisionPoint(subdP)) return ON_SUBD_RETURN_ERROR(nullptr); - v1->m_level = edge0->m_level+1; - - if (ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag || ON_SubD::EdgeTag::X == edge0->m_edge_tag) + const unsigned int edge_capacity = 4; + ON_SubDVertex* v1 = AllocateVertex(subdP, edge_capacity); + if (nullptr == v1) + return ON_SUBD_RETURN_ERROR(nullptr); + + v1->SetSubdivisionLevel( edge0->SubdivisionLevel() + 1 ); + + if (ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag || ON_SubD::EdgeTag::SmoothX == edge0->m_edge_tag) v1->m_vertex_tag = ON_SubD::VertexTag::Smooth; else if (ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) v1->m_vertex_tag = ON_SubD::VertexTag::Crease; - //if ( ON_SubD::SubDType::QuadCatmullClark == subdivision_type) - // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad; - //else if ( ON_SubD::SubDType::TriLoopWarren == subdivision_type) - // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Tri; - return v1; } -ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( - const ON_SubDFace* face0, - ON_SubD::SubDType subdivision_type, - bool bUseSavedSubdivisionPoint, - unsigned int edge_capacity, - unsigned int face_capacity - ) +ON_SubDVertex * ON_SubD_FixedSizeHeap::FindOrAllocateVertex(const ON_SubDFace * face0) { - if ( nullptr == face0) + const unsigned int face0_edge_count = (nullptr != face0) ? ((unsigned int)face0->m_edge_count) : 0U; + if (face0_edge_count < 3) return ON_SUBD_RETURN_ERROR(nullptr); + const ON_SubDComponentPtr component0 = ON_SubDComponentPtr::Create(face0); + const unsigned int hash = Internal_Hash(component0); + ON_SubDVertex* v1 = Internal_HashFindVertex1(hash, component0); + + if (nullptr != v1) + { + // found the previously allocated vertex + if (((unsigned int)v1->m_edge_capacity) < face0->m_edge_count) + { + ON_SUBD_ERROR("edge capacity was too small when vertex was created."); + } + return v1; + } + double subdP[3]; - if (false == face0->GetSubdivisionPoint(subdivision_type,bUseSavedSubdivisionPoint,subdP)) + if (false == face0->GetSubdivisionPoint(subdP)) return ON_SUBD_RETURN_ERROR(nullptr); - ON_SubDVertex* v1 = AllocateVertex(subdP,edge_capacity,face_capacity); - if ( nullptr == v1) + v1 = AllocateVertex(subdP, face0_edge_count ); + if (nullptr == v1) return ON_SUBD_RETURN_ERROR(nullptr); - v1->m_level = face0->m_level+1; - + v1->SetSubdivisionLevel( face0->SubdivisionLevel() + 1 ); v1->m_vertex_tag = ON_SubD::VertexTag::Smooth; - //if ( ON_SubD::SubDType::QuadCatmullClark == subdivision_type) - // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad; - //else if ( ON_SubD::SubDType::TriLoopWarren == subdivision_type) - // v1->m_vertex_facet_type = ON_SubD::VertexFacetType::Tri; + Internal_HashAddPair(hash, component0, v1); return v1; } -ON_SubDEdge* ON_SubD_FixedSizeHeap::AllocateEdge( + +ON_SubDVertex * ON_SubD_FixedSizeHeap::AllocateSectorFaceVertex(const ON_SubDFace * sector_face0) +{ + if (nullptr == sector_face0) + return ON_SUBD_RETURN_ERROR(nullptr); + double subdP[3]; + if (false == sector_face0->GetSubdivisionPoint(subdP)) + return ON_SUBD_RETURN_ERROR(nullptr); + + ON_SubDVertex* v1 = AllocateVertex(subdP, 3 ); + if (nullptr == v1) + return ON_SUBD_RETURN_ERROR(nullptr); + + v1->SetSubdivisionLevel( sector_face0->SubdivisionLevel() + 1 ); + v1->m_vertex_tag = ON_SubD::VertexTag::Smooth; + return v1; +} + + +const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::FindOrAllocateEdge(ON_SubDVertex * v0, double v0_sector_weight, ON_SubDVertex * v1, double v1_sector_weight) +{ + if ( nullptr == v0 || nullptr == v0->m_edges) + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + if ( nullptr == v1 || nullptr == v1->m_edges) + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); + for (unsigned short v0ei = 0; v0ei < v0->m_edge_count; ++v0ei) + { + const ON_SubDEdgePtr ep = v0->m_edges[v0ei]; + if (v0 == ep.RelativeVertex(0)) + { + if (v1 == ep.RelativeVertex(1)) + return ep; + } + else if (v0 == ep.RelativeVertex(1)) + { + if (v1 == ep.RelativeVertex(0)) + return ep.Reversed(); + } + else + { + ON_SUBD_RETURN_ERROR("Invalid ON_SubDEdgePtr in vertex->m_edge[] array"); + } + } + + return AllocateEdge(v0, v0_sector_weight, v1, v1_sector_weight); +} + +const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge( ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, @@ -375,17 +627,17 @@ ON_SubDEdge* ON_SubD_FixedSizeHeap::AllocateEdge( ) { if ( nullptr != v0 && nullptr == v0->m_edges) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); if ( nullptr != v1 && nullptr == v1->m_edges) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); if (nullptr == m_e || m_e_index >= m_e_capacity) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); bool bTaggedVertex[2]; if (nullptr != v0) { if (nullptr == v0->m_edges || v0->m_edge_count >= v0->m_edge_capacity) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); if (ON_SubD::VertexTag::Smooth == v0->m_vertex_tag) { bTaggedVertex[0] = false; @@ -402,7 +654,7 @@ ON_SubDEdge* ON_SubD_FixedSizeHeap::AllocateEdge( if (nullptr != v1) { if (nullptr == v1->m_edges || v1->m_edge_count >= v1->m_edge_capacity) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); if (ON_SubD::VertexTag::Smooth == v1->m_vertex_tag) { bTaggedVertex[1] = false; @@ -423,10 +675,10 @@ ON_SubDEdge* ON_SubD_FixedSizeHeap::AllocateEdge( bTaggedVertex[1] = false; if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v0_sector_weight, true)) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v1_sector_weight, true)) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); ON_SubDEdge* e = m_e + m_e_index; memset(e, 0, sizeof(*e)); @@ -438,13 +690,13 @@ ON_SubDEdge* ON_SubD_FixedSizeHeap::AllocateEdge( } e->m_id = ++m_e_index; - + if (nullptr != v0) { e->m_vertex[0] = v0; v0->m_edges[v0->m_edge_count++] = ON_SubDEdgePtr::Create(e,0); //v0->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; - e->m_level = v0->m_level; + e->SetSubdivisionLevel(v0->SubdivisionLevel()); } if (nullptr != v1) @@ -452,18 +704,18 @@ ON_SubDEdge* ON_SubD_FixedSizeHeap::AllocateEdge( e->m_vertex[1] = v1; v1->m_edges[v1->m_edge_count++] = ON_SubDEdgePtr::Create(e,1); //v1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; - if ( e->m_level < v1->m_level) - e->m_level = v1->m_level; + if ( e->SubdivisionLevel() < v1->SubdivisionLevel()) + e->SetSubdivisionLevel(v1->SubdivisionLevel()); } e->m_sector_coefficient[0] = v0_sector_weight; e->m_sector_coefficient[1] = v1_sector_weight; e->m_edge_tag = (bTaggedVertex[0] && bTaggedVertex[1]) ? ON_SubD::EdgeTag::Crease : ON_SubD::EdgeTag::Smooth; - return e; + return ON_SubDEdgePtr::Create(e,0); } -ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateFace( +ON_SubDFace* ON_SubD_FixedSizeHeap::Internal_AllocateFace( unsigned int zero_face_id, unsigned int parent_face_id ) @@ -474,7 +726,7 @@ ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateFace( memset(f, 0, sizeof(*f)); if (m_f_index > 0) { - // code in ON_SubDFaceNeighborhood.Subdivide() relies on + // code in ON_SubDFaceNeighborhood.Subdivide() relies on // m_next_face being set this way. m_f[m_f_index-1].m_next_face = f; f->m_prev_face = &m_f[m_f_index-1]; @@ -487,6 +739,20 @@ ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateFace( return f; } + +ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateQuad( + unsigned int zero_face_id, + unsigned int parent_face_id, + ON_SubDEdgePtr e0, + ON_SubDEdgePtr e1, + ON_SubDEdgePtr e2, + ON_SubDEdgePtr e3 +) +{ + const ON_SubDEdgePtr eptrs[4] = { e0,e1,e2,e3 }; + return AllocateQuad(zero_face_id, parent_face_id, eptrs); +} + ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateQuad( unsigned int zero_face_id, unsigned int parent_face_id, @@ -531,8 +797,8 @@ ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateQuad( return ON_SUBD_RETURN_ERROR(nullptr); if (nullptr == vertices[3] || nullptr == vertices[3]->m_faces || vertices[3]->m_face_count >= vertices[3]->m_face_capacity || vertices[3] != edges[2]->m_vertex[1-edgedirs[2]]) return ON_SUBD_RETURN_ERROR(nullptr); - - ON_SubDFace* f = AllocateFace(zero_face_id,parent_face_id); + + ON_SubDFace* f = Internal_AllocateFace(zero_face_id,parent_face_id); if (nullptr == f) return ON_SUBD_RETURN_ERROR(nullptr); @@ -556,74 +822,11 @@ ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateQuad( vertices[3]->m_faces[vertices[3]->m_face_count++] = f; //vertices[3]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; - f->m_level = edges[0]->m_level; + f->SetSubdivisionLevel( edges[0]->SubdivisionLevel() ); return f; } - -ON_SubDFace* ON_SubD_FixedSizeHeap::AllocateTri( - unsigned int zero_face_id, - unsigned int parent_face_id, - const ON_SubDEdgePtr eptrs[3] - ) -{ - if (nullptr == eptrs) - return ON_SUBD_RETURN_ERROR(nullptr); - - ON_SubDEdge* edges[3] = { - ON_SUBD_EDGE_POINTER(eptrs[0].m_ptr), - ON_SUBD_EDGE_POINTER(eptrs[1].m_ptr), - ON_SUBD_EDGE_POINTER(eptrs[2].m_ptr)}; - - if (nullptr == edges[0] || edges[0]->m_face_count > 1) - return ON_SUBD_RETURN_ERROR(nullptr); - if (nullptr == edges[1] || edges[1]->m_face_count > 1) - return ON_SUBD_RETURN_ERROR(nullptr); - if (nullptr == edges[2] || edges[2]->m_face_count > 1) - return ON_SUBD_RETURN_ERROR(nullptr); - - ON__UINT_PTR edgedirs[3] = { - ON_SUBD_EDGE_DIRECTION(eptrs[0].m_ptr), - ON_SUBD_EDGE_DIRECTION(eptrs[1].m_ptr), - ON_SUBD_EDGE_DIRECTION(eptrs[2].m_ptr)}; - - ON_SubDVertex* vertices[3] = { - const_cast(edges[0]->m_vertex[edgedirs[0]]), - const_cast(edges[1]->m_vertex[edgedirs[1]]), - const_cast(edges[2]->m_vertex[edgedirs[2]])}; - - if (nullptr == vertices[0] || nullptr == vertices[0]->m_faces || vertices[0]->m_face_count >= vertices[0]->m_face_capacity || vertices[0] != edges[2]->m_vertex[1-edgedirs[2]]) - return ON_SUBD_RETURN_ERROR(nullptr); - if (nullptr == vertices[1] || nullptr == vertices[1]->m_faces || vertices[1]->m_face_count >= vertices[1]->m_face_capacity || vertices[1] != edges[0]->m_vertex[1-edgedirs[0]]) - return ON_SUBD_RETURN_ERROR(nullptr); - if (nullptr == vertices[2] || nullptr == vertices[2]->m_faces || vertices[2]->m_face_count >= vertices[2]->m_face_capacity || vertices[2] != edges[1]->m_vertex[1-edgedirs[1]]) - return ON_SUBD_RETURN_ERROR(nullptr); - - ON_SubDFace* f = AllocateFace(zero_face_id,parent_face_id); - if (nullptr == f) - return ON_SUBD_RETURN_ERROR(nullptr); - - f->m_edge_count = 3; - f->m_edge4[0] = eptrs[0]; - f->m_edge4[1] = eptrs[1]; - f->m_edge4[2] = eptrs[2]; - - edges[0]->m_face2[edges[0]->m_face_count++] = ON_SubDFacePtr::Create(f,edgedirs[0]); - edges[1]->m_face2[edges[1]->m_face_count++] = ON_SubDFacePtr::Create(f,edgedirs[1]); - edges[2]->m_face2[edges[2]->m_face_count++] = ON_SubDFacePtr::Create(f,edgedirs[2]); - - vertices[0]->m_faces[vertices[0]->m_face_count++] = f; - //vertices[0]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; - vertices[1]->m_faces[vertices[1]->m_face_count++] = f; - //vertices[1]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; - vertices[2]->m_faces[vertices[2]->m_face_count++] = f; - //vertices[2]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; - - f->m_level = edges[0]->m_level; - - return f; -} ON__UINT_PTR* ON_SubD_FixedSizeHeap::AllocatePtrArray( unsigned int capacity, bool bZeroMemory @@ -632,7 +835,7 @@ ON__UINT_PTR* ON_SubD_FixedSizeHeap::AllocatePtrArray( if (0 == capacity) return nullptr; - if (nullptr == m_p || capacity + m_p_index >= m_p_capacity) + if (nullptr == m_p || capacity + m_p_index > m_p_capacity) return ON_SUBD_RETURN_ERROR(nullptr); ON__UINT_PTR* p = m_p + m_p_index; @@ -651,8 +854,8 @@ ON__UINT_PTR* ON_SubD_FixedSizeHeap::AllocatePtrArray( } bool ON_SubD_FixedSizeHeap::ReturnPtrArray( - unsigned int capacity, - void* p + void* p, + unsigned int capacity ) { if (nullptr != m_p && capacity <= m_p_index && p == m_p + (m_p_index - capacity)) @@ -703,7 +906,7 @@ class ON_SubDVertex* ON_SubDHeap::AllocateVertexAndSetId(unsigned int& max_verte // In order for m_fspv.ElementFromId() to work, it is critical that // once a vertex is allocated from m_fspv, the value of m_id never // changes. This is imporant because the value of m_id must persist - // in binary archives in order for ON_COMPONENT_INDEX values to + // in binary archives in order for ON_COMPONENT_INDEX values to // persist in binary archives. ON_SubDVertex* v; if (m_unused_vertex) @@ -737,6 +940,7 @@ void ON_SubDHeap::ReturnVertex(class ON_SubDVertex* v) { ReturnVertexEdgeAndFaceArrays(v); (&v->m_id)[1] = ON_UNSET_UINT_INDEX; // m_archive_id == ON_UNSET_UINT_INDEX marks the fixed size pool element as unused + v->m_status = ON_ComponentStatus::Deleted; v->m_next_vertex = m_unused_vertex; m_unused_vertex = v; // NO! // m_fspv.ReturnElement(v); @@ -749,7 +953,7 @@ class ON_SubDEdge* ON_SubDHeap::AllocateEdgeAndSetId(unsigned int& max_edge_id) // In order for m_fspe.ElementFromId() to work, it is critical that // once a edge is allocated from m_fspe, the value of m_id never // changes. This is imporant because the value of m_id must persist - // in binary archives in order for ON_COMPONENT_INDEX values to + // in binary archives in order for ON_COMPONENT_INDEX values to // persist in binary archives. ON_SubDEdge* e; if (m_unused_edge) @@ -784,6 +988,7 @@ void ON_SubDHeap::ReturnEdge(class ON_SubDEdge* e) if (nullptr != e->m_facex) ReturnArray(e->m_facex_capacity,(ON__UINT_PTR*)e->m_facex); (&e->m_id)[1] = ON_UNSET_UINT_INDEX; // m_archive_id == ON_UNSET_UINT_INDEX marks the fixed size pool element as unused + e->m_status = ON_ComponentStatus::Deleted; e->m_next_edge = m_unused_edge; m_unused_edge = e; // NO! // m_fspe.ReturnElement(e); @@ -792,17 +997,38 @@ void ON_SubDHeap::ReturnEdge(class ON_SubDEdge* e) } class ON_SubDFace* ON_SubDHeap::AllocateFaceAndSetId(unsigned int& max_face_id) +{ + return AllocateFaceAndSetId(nullptr, max_face_id); +} + +class ON_SubDFace* ON_SubDHeap::AllocateFaceAndSetId(const ON_SubDFace* candidate_face, unsigned int& max_face_id) { // In order for m_fspf.ElementFromId() to work, it is critical that // once a face is allocated from m_fspf, the value of m_id never // changes. This is imporant because the value of m_id must persist - // in binary archives in order for ON_COMPONENT_INDEX values to + // in binary archives in order for ON_COMPONENT_INDEX values to // persist in binary archives. ON_SubDFace* f; if (m_unused_face) { - f = m_unused_face; - m_unused_face = (ON_SubDFace*)(m_unused_face->m_next_face); + f = nullptr; + if (nullptr != candidate_face && candidate_face != m_unused_face) + { + for (f = m_unused_face; nullptr != f; f = const_cast(f->m_next_face)) + { + if (candidate_face == f->m_next_face) + { + f->m_next_face = f->m_next_face->m_next_face; + f = const_cast(candidate_face); + break; + } + } + } + if (nullptr == f) + { + f = m_unused_face; + m_unused_face = const_cast(m_unused_face->m_next_face); + } const unsigned int id = f->m_id; if (ON_UNSET_UINT_INDEX == (&f->m_id)[1]) { @@ -830,6 +1056,7 @@ void ON_SubDHeap::ReturnFace(class ON_SubDFace* f) { ReturnArray(f->m_edgex_capacity,(ON__UINT_PTR*)f->m_edgex); (&f->m_id)[1] = ON_UNSET_UINT_INDEX; // m_archive_id == ON_UNSET_UINT_INDEX marks the fixed size pool element as unused + f->m_status = ON_ComponentStatus::Deleted; f->m_next_face = m_unused_face; m_unused_face = f; // NO! // m_fspf.ReturnElement(f); @@ -853,6 +1080,11 @@ void ON_SubDHeap::Clear() m_fsp5.ReturnAll(); m_fsp9.ReturnAll(); m_fsp17.ReturnAll(); + m_limit_block_pool.ReturnAll(); + + m_unused_full_fragments = nullptr; + m_unused_half_fragments = nullptr; + m_unused_limit_curves = nullptr; m_unused_vertex = nullptr; m_unused_edge = nullptr; @@ -953,8 +1185,8 @@ unsigned int ON_SubDHeap::MaximumFaceId() const bool ON_SubDHeap::IsValid() const { - return m_fspv.ElementIdIsIncreasing(ON_SubDHeap::m_offset_vertex_id) - && m_fspe.ElementIdIsIncreasing(ON_SubDHeap::m_offset_edge_id) + return m_fspv.ElementIdIsIncreasing(ON_SubDHeap::m_offset_vertex_id) + && m_fspe.ElementIdIsIncreasing(ON_SubDHeap::m_offset_edge_id) && m_fspf.ElementIdIsIncreasing(ON_SubDHeap::m_offset_face_id); } @@ -1002,8 +1234,13 @@ void ON_SubDHeap::ReturnOversizedElement( class tagWSItem* p = ((class tagWSItem*)(a - 1)) - 1; if (p == m_ws) { - m_ws = p->m_next; - p->m_next->m_prev = 0; + if (nullptr != p->m_next) + { + m_ws = p->m_next; + p->m_next->m_prev = 0; + } + else + m_ws = nullptr; } else { @@ -1389,3 +1626,266 @@ void ON_SubDHeap::ReturnArray( return; } +bool ON_SubDHeap::Internal_InitializeLimitBlockPool() +{ + if (0 == m_limit_block_pool.SizeofElement()) + { + m_sizeof_full_fragment = ON_SubDMeshFragment::SizeofFragment(ON_SubDDisplayParameters::DefaultDensity); + m_sizeof_half_fragment = ON_SubDMeshFragment::SizeofFragment(ON_SubDDisplayParameters::DefaultDensity-1); + m_sizeof_limit_curve = sizeof(ON_SubDEdgeSurfaceCurve); + size_t sz = m_sizeof_full_fragment; + if (sz < 4 * m_sizeof_half_fragment) + sz = 4 * m_sizeof_half_fragment; + + ON_SleepLockGuard guard(m_limit_block_pool); + m_limit_block_pool.Create(sz,0,0); + // check size again in case another thread beat this call + if (0 == m_limit_block_pool.SizeofElement()) + m_limit_block_pool.Create(sz, 0, 0); + } + return (m_limit_block_pool.SizeofElement() > 0); +} + +ON_SubDMeshFragment* ON_SubDHeap::AllocateMeshFragment( + const ON_SubDMeshFragment& src_fragment +) +{ + // When 4 == ON_SubDDisplayParameters::DefaultDensity (setting used in February 2019) + // quads get a single fragment with a 16x16 face grid + // N-gons with N != 4 get N 8x8 grids. + const unsigned int density = (src_fragment.m_face_fragment_count > 1) + ? (ON_SubDDisplayParameters::DefaultDensity-1) + : ((1==src_fragment.m_face_fragment_count) ? ON_SubDDisplayParameters::DefaultDensity : 0) + ; + if (0 == density) + return ON_SUBD_RETURN_ERROR(nullptr); + + const unsigned short side_seg_count = (unsigned short)ON_SubDMeshFragment::SideSegmentCountFromDisplayDensity(density); + const unsigned short vertex_capacity = (side_seg_count + 1)*(side_seg_count + 1); + if ( src_fragment.VertexCount() > 0 && src_fragment.VertexCount() < ((unsigned)vertex_capacity) ) + return ON_SUBD_RETURN_ERROR(nullptr); + + if (0 == m_limit_block_pool.SizeofElement()) + Internal_InitializeLimitBlockPool(); + + ON_SubDMeshFragment* fragment; + { + char* p = nullptr; + char* p1 = nullptr; + ON_SleepLockGuard guard(m_limit_block_pool); + if (ON_SubDDisplayParameters::DefaultDensity == density) + { + if (nullptr == m_unused_full_fragments) + { + p = (char*)m_limit_block_pool.AllocateDirtyElement(); + if (nullptr == p) + return ON_SUBD_RETURN_ERROR(nullptr); + p1 = p + m_limit_block_pool.SizeofElement(); + m_unused_full_fragments = (ON_FixedSizePoolElement*)p; + m_unused_full_fragments->m_next = nullptr; + p += m_sizeof_full_fragment; + while (p + m_sizeof_full_fragment < p1) + { + ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)p; + ele->m_next = m_unused_full_fragments; + m_unused_full_fragments = ele; + p += m_sizeof_full_fragment; + } + } + fragment = (ON_SubDMeshFragment*)m_unused_full_fragments; + m_unused_full_fragments = m_unused_full_fragments->m_next; + } + else + { + if (nullptr == m_unused_half_fragments) + { + p = (char*)m_limit_block_pool.AllocateDirtyElement(); + if (nullptr == p) + return ON_SUBD_RETURN_ERROR(nullptr); + p1 = p + m_limit_block_pool.SizeofElement(); + m_unused_half_fragments = (ON_FixedSizePoolElement*)p; + m_unused_half_fragments->m_next = nullptr; + p += m_sizeof_half_fragment; + while (p + m_sizeof_half_fragment < p1) + { + ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)p; + ele->m_next = m_unused_half_fragments; + m_unused_half_fragments = ele; + p += m_sizeof_half_fragment; + } + } + fragment = (ON_SubDMeshFragment*)m_unused_half_fragments; + m_unused_half_fragments = m_unused_half_fragments->m_next; + } + if (nullptr != p) + { + while (p + m_sizeof_limit_curve < p1) + { + ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)p; + ele->m_next = m_unused_limit_curves; + m_unused_limit_curves = ele; + p += m_sizeof_limit_curve; + } + } + } + + *fragment = src_fragment; + fragment->m_prev_fragment = nullptr; + fragment->m_next_fragment = nullptr; + double* a = (double*)(fragment + 1); + fragment->SetUnmanagedVertexCapacity(vertex_capacity); + fragment->SetVertexCount(0); + fragment->m_P = a; + fragment->m_P_stride = 3; + fragment->m_N = a + (vertex_capacity * 3); + fragment->m_N_stride = 3; + fragment->m_T = a + (vertex_capacity * 6); + fragment->m_T_stride = 3; + + if (src_fragment.VertexCount() > 0) + fragment->CopyFrom(src_fragment,density); + + return fragment; +} + +bool ON_SubDHeap::ReturnMeshFragment(ON_SubDMeshFragment * fragment) +{ + if (nullptr == fragment) + return false; + + ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)fragment; + if (17 * 17 == fragment->VertexCapacity()) + { + ON_SleepLockGuard guard(m_limit_block_pool); + ((unsigned int*)ele)[5] = 0; // zero m_vertex_count_etc and m_vertex_capacity_etc + ele->m_next = m_unused_full_fragments; + m_unused_full_fragments = ele; + } + else if (9 * 9 == fragment->VertexCapacity()) + { + ON_SleepLockGuard guard(m_limit_block_pool); + ((unsigned int*)ele)[5] = 0; // zero m_vertex_count_etc and m_vertex_capacity_etc + ele->m_next = m_unused_half_fragments; + m_unused_half_fragments = ele; + } + else + return ON_SUBD_RETURN_ERROR(false); + + return true; +} + +bool ON_SubDHeap::ReturnMeshFragments(const ON_SubDFace * face) +{ + if (nullptr != face) + { + face->Internal_ClearSurfacePointFlag(); + face->Internal_ClearControlNetFragmentFlag(); + ON_SubDMeshFragment* fragment = face->m_mesh_fragments; + face->m_mesh_fragments = nullptr; + while (nullptr != fragment) + { + if (face != fragment->m_face) + return ON_SUBD_RETURN_ERROR(false); + ON_SubDMeshFragment* next_fragment = fragment->m_next_fragment; + if (false == ReturnMeshFragment(fragment)) + return false; + fragment = next_fragment; + } + } + return true; +} + +class ON_SubDEdgeSurfaceCurve* ON_SubDHeap::AllocateEdgeSurfaceCurve( + unsigned int cv_capacity +) +{ + if (cv_capacity < 1 || cv_capacity > ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity) + return ON_SUBD_RETURN_ERROR(nullptr); + if (0 == m_limit_block_pool.SizeofElement()) + Internal_InitializeLimitBlockPool(); + + ON_SubDEdgeSurfaceCurve* limit_curve; + double* cvx = nullptr; + + { + ON_SleepLockGuard guard(m_limit_block_pool); + if ( + nullptr == m_unused_limit_curves + || ( cv_capacity > ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity && nullptr == m_unused_limit_curves->m_next) + ) + { + char* p = (char*)m_limit_block_pool.AllocateDirtyElement(); + if (nullptr == p) + return ON_SUBD_RETURN_ERROR(nullptr); + char* p1 = p + m_limit_block_pool.SizeofElement(); + while (p + m_sizeof_limit_curve < p1) + { + ON_FixedSizePoolElement* ele = (ON_FixedSizePoolElement*)p; + ele->m_next = m_unused_limit_curves; + m_unused_limit_curves = ele; + p += m_sizeof_limit_curve; + } + } + + limit_curve = (ON_SubDEdgeSurfaceCurve*)m_unused_limit_curves; + m_unused_limit_curves = m_unused_limit_curves->m_next; + if (cv_capacity > ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity) + { + cvx = (double*)m_unused_limit_curves; + m_unused_limit_curves = m_unused_limit_curves->m_next; + } + } + + memset(limit_curve, 0, sizeof(*limit_curve)); + limit_curve->m_cv_capacity = ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity; + if (nullptr != cvx) + { + // increase capacity + limit_curve->m_cv_capacity = ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity; + limit_curve->m_cvx = cvx; + double* p1 = cvx + 3 * (ON_SubDEdgeSurfaceCurve::MaximumControlPointCapacity-ON_SubDEdgeSurfaceCurve::MinimumControlPointCapacity); + while (cvx < p1) + *cvx++ = ON_DBL_QNAN; + } + + return limit_curve; +} + +bool ON_SubDHeap::ReturnEdgeSurfaceCurve( + class ON_SubDEdgeSurfaceCurve* limit_curve +) +{ + if (nullptr != limit_curve) + { + limit_curve->m_cv_count = 0; + ON_FixedSizePoolElement* ele0 = (ON_FixedSizePoolElement*)limit_curve; + ON_FixedSizePoolElement* ele1 = (ON_FixedSizePoolElement*)limit_curve->m_cvx; + if (nullptr != ele1) + { + ((unsigned int*)ele1)[2] = 0; // zero cv_count and cv_capacity - to limit crashes caused by rogue references + ele0->m_next = ele1; + } + else + ele1 = ele0; + ((unsigned int*)ele0)[2] = 0; // zero cv_count and cv_capacity - to limit crashes caused by rogue references + ON_SleepLockGuard guard(m_limit_block_pool); + ele1->m_next = m_unused_limit_curves; + m_unused_limit_curves = ele0; + } + return true; +} + +bool ON_SubDHeap::ReturnEdgeSurfaceCurve( + const class ON_SubDEdge* edge +) +{ + bool rc = true; + ON_SubDEdgeSurfaceCurve* limit_curve = (nullptr != edge) ? edge->m_limit_curve : nullptr; + if (nullptr != limit_curve) + { + edge->Internal_ClearSurfacePointFlag(); + edge->m_limit_curve = nullptr; + rc = ReturnEdgeSurfaceCurve(limit_curve); + } + return rc; +} diff --git a/opennurbs_subd_iter.cpp b/opennurbs_subd_iter.cpp index f5b094fa..44437cdc 100644 --- a/opennurbs_subd_iter.cpp +++ b/opennurbs_subd_iter.cpp @@ -168,6 +168,261 @@ ON_SubDFaceArray& ON_SubDFaceArray::operator=(ON_SubDFaceArray&& src) } #endif +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentIdIterator +// + +void ON_SubDHeap::InitializeComponentIdIterator( + ON_SubDComponentPtr::Type ctype, + class ON_SubDComponentIdIterator& cidit +) const +{ + switch (ctype) + { + case ON_SubDComponentPtr::Type::Vertex: + cidit.m_component_type = ctype; + cidit.ON_FixedSizePoolIterator::Create(&m_fspv); + break; + + case ON_SubDComponentPtr::Type::Edge: + cidit.m_component_type = ctype; + cidit.ON_FixedSizePoolIterator::Create(&m_fspe); + break; + + case ON_SubDComponentPtr::Type::Face: + cidit.m_component_type = ctype; + cidit.ON_FixedSizePoolIterator::Create(&m_fspf); + break; + + default: + ON_SUBD_ERROR("Invalid ctype"); + break; + } +} + +void ON_SubDimple::InitializeComponentIdIterator( + ON_SubDComponentPtr::Type ctype, + ON_SubDComponentIdIterator & cidit +) const +{ + m_heap.InitializeComponentIdIterator(ctype, cidit); +} + +const ON_SubDComponentBase* ON_SubDComponentIdIterator::FirstComponent() +{ + for (const ON_SubDComponentBase* c = (const ON_SubDComponentBase*)FirstElement(); nullptr != c; c = (const ON_SubDComponentBase*)NextElement()) + { + if (ON_UNSET_UINT_INDEX != (&c->m_id)[1]) + return c; + } + return nullptr; +} + +const ON_SubDComponentBase* ON_SubDComponentIdIterator::NextComponent() +{ + for (const ON_SubDComponentBase* c = (const ON_SubDComponentBase*)NextElement(); nullptr != c; c = (const ON_SubDComponentBase*)NextElement()) + { + if (ON_UNSET_UINT_INDEX != (&c->m_id)[1]) + return c; + } + return nullptr; +} + +const ON_SubDComponentBase* ON_SubDComponentIdIterator::CurrentComponent() const +{ + return (const ON_SubDComponentBase*)CurrentElement(); +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDVertexIterator +// + +void ON_SubDHeap::InitializeVertexIdIterator( + class ON_SubDVertexIdIterator& vidit +) const +{ + vidit.ON_FixedSizePoolIterator::Create(&m_fspv); +} + +void ON_SubDimple::InitializeVertexIdIterator( + class ON_SubDVertexIdIterator& vidit +) const +{ + m_heap.InitializeVertexIdIterator(vidit); +} + +void ON_SubDVertexIdIterator::Internal_Init() +{ + const ON_SubDimple* subdimple = m_subd_ref.SubD().SubDimple(); + if (nullptr != subdimple) + subdimple->InitializeVertexIdIterator(*this); +} + +ON_SubDVertexIdIterator::ON_SubDVertexIdIterator(const ON_SubDRef& subd_ref) + : m_subd_ref(subd_ref) +{ + Internal_Init(); +} + +ON_SubDVertexIdIterator::ON_SubDVertexIdIterator(const ON_SubD& subd) + : m_subd_ref(ON_SubDRef::CreateReferenceForExperts(subd)) +{ + Internal_Init(); +} + +const ON_SubDVertex* ON_SubDVertexIdIterator::FirstVertex() +{ + for (const ON_SubDVertex* v = (const ON_SubDVertex*)FirstElement(); nullptr != v; v = (const ON_SubDVertex*)NextElement()) + { + if (ON_UNSET_UINT_INDEX != (&v->m_id)[1]) + return v; + } + return nullptr; +} + +const ON_SubDVertex* ON_SubDVertexIdIterator::NextVertex() +{ + for (const ON_SubDVertex* v = (const ON_SubDVertex*)NextElement(); nullptr != v; v = (const ON_SubDVertex*)NextElement()) + { + if (ON_UNSET_UINT_INDEX != (&v->m_id)[1]) + return v; + } + return nullptr; +} + +const ON_SubDVertex* ON_SubDVertexIdIterator::CurrentVertex() const +{ + return (const ON_SubDVertex*)CurrentElement(); +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDEdgeIterator +// + +void ON_SubDHeap::InitializeEdgeIdIterator( + class ON_SubDEdgeIdIterator& vidit +) const +{ + vidit.ON_FixedSizePoolIterator::Create(&m_fspe); +} + +void ON_SubDimple::InitializeEdgeIdIterator( + class ON_SubDEdgeIdIterator& vidit +) const +{ + m_heap.InitializeEdgeIdIterator(vidit); +} + +void ON_SubDEdgeIdIterator::Internal_Init() +{ + const ON_SubDimple* subdimple = m_subd_ref.SubD().SubDimple(); + if (nullptr != subdimple) + subdimple->InitializeEdgeIdIterator(*this); +} + +ON_SubDEdgeIdIterator::ON_SubDEdgeIdIterator(const ON_SubDRef& subd_ref) + : m_subd_ref(subd_ref) +{ + Internal_Init(); +} + +ON_SubDEdgeIdIterator::ON_SubDEdgeIdIterator(const ON_SubD& subd) + : m_subd_ref(ON_SubDRef::CreateReferenceForExperts(subd)) +{ + Internal_Init(); +} + +const ON_SubDEdge* ON_SubDEdgeIdIterator::FirstEdge() +{ + for (const ON_SubDEdge* e = (const ON_SubDEdge*)FirstElement(); nullptr != e; e = (const ON_SubDEdge*)NextElement()) + { + if (ON_UNSET_UINT_INDEX != (&e->m_id)[1]) + return e; + } + return nullptr; +} + +const ON_SubDEdge* ON_SubDEdgeIdIterator::NextEdge() +{ + for (const ON_SubDEdge* e = (const ON_SubDEdge*)NextElement(); nullptr != e; e = (const ON_SubDEdge*)NextElement()) + { + if (ON_UNSET_UINT_INDEX != (&e->m_id)[1]) + return e; + } + return nullptr; +} + +const ON_SubDEdge* ON_SubDEdgeIdIterator::CurrentEdge() const +{ + return (const ON_SubDEdge*)CurrentElement(); +}; + +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFaceIterator +// + +void ON_SubDHeap::InitializeFaceIdIterator( + class ON_SubDFaceIdIterator& vidit +) const +{ + vidit.ON_FixedSizePoolIterator::Create(&m_fspf); +} + +void ON_SubDimple::InitializeFaceIdIterator( + class ON_SubDFaceIdIterator& vidit +) const +{ + m_heap.InitializeFaceIdIterator(vidit); +} + +void ON_SubDFaceIdIterator::Internal_Init() +{ + const ON_SubDimple* subdimple = m_subd_ref.SubD().SubDimple(); + if (nullptr != subdimple) + subdimple->InitializeFaceIdIterator(*this); +} + +ON_SubDFaceIdIterator::ON_SubDFaceIdIterator(const ON_SubDRef& subd_ref) + : m_subd_ref(subd_ref) +{ + Internal_Init(); +} + +ON_SubDFaceIdIterator::ON_SubDFaceIdIterator(const ON_SubD& subd) + : m_subd_ref(ON_SubDRef::CreateReferenceForExperts(subd)) +{ + Internal_Init(); +} + +const ON_SubDFace* ON_SubDFaceIdIterator::FirstFace() +{ + for (const ON_SubDFace* f = (const ON_SubDFace*)FirstElement(); nullptr != f; f = (const ON_SubDFace*)NextElement()) + { + if (ON_UNSET_UINT_INDEX != (&f->m_id)[1]) + return f; + } + return nullptr; +} + +const ON_SubDFace* ON_SubDFaceIdIterator::NextFace() +{ + for (const ON_SubDFace* f = (const ON_SubDFace*)NextElement(); nullptr != f; f = (const ON_SubDFace*)NextElement()) + { + if (ON_UNSET_UINT_INDEX != (&f->m_id)[1]) + return f; + } + return nullptr; +} + +const ON_SubDFace* ON_SubDFaceIdIterator::CurrentFace() const +{ + return (const ON_SubDFace*)CurrentElement(); +}; + ////////////////////////////////////////////////////////////////////////// // // ON_SubDVertexIterator @@ -780,6 +1035,33 @@ const ON_SubDEdge* ON_SubDFaceEdgeIterator::PrevEdge() return nullptr; } + +const ON_SubDEdge* ON_SubDFaceEdgeIterator::NextEdge(bool bReturnNullAtFirstEdge) +{ + if (m_edge_count > 0) + { + const unsigned int next_edge_index = (m_edge_index + 1) % m_edge_count; + if (bReturnNullAtFirstEdge && next_edge_index == m_edge_index0) + return nullptr; + m_edge_index = next_edge_index; + return CurrentEdge(); + } + return nullptr; +} + +const ON_SubDEdge* ON_SubDFaceEdgeIterator::PrevEdge(bool bReturnNullAtFirstEdge) +{ + if (m_edge_count > 0) + { + const unsigned int prev_edge_index = (m_edge_index + (m_edge_count - 1)) % m_edge_count; + if (bReturnNullAtFirstEdge && prev_edge_index == m_edge_index0) + return nullptr; + m_edge_index = prev_edge_index; + return CurrentEdge(); + } + return nullptr; +} + unsigned int ON_SubDFaceEdgeIterator::FirstEdgeIndex() const { return m_edge_index0; diff --git a/opennurbs_subd_limit.cpp b/opennurbs_subd_limit.cpp index f8ed8107..c4d86a50 100644 --- a/opennurbs_subd_limit.cpp +++ b/opennurbs_subd_limit.cpp @@ -10,9 +10,6 @@ #include "opennurbs_subd_data.h" -#if defined(OPENNURBS_SUBD_WIP) - - bool ON_SubDQuadNeighborhood::IsValid() const { unsigned int count = 0; @@ -163,7 +160,7 @@ bool ON_SubDQuadNeighborhood::IsValid() const if (m_bBoundaryCrease[fi]) { - // A tag of ON_SubD::EdgeTag::X is an error here + // A tag of ON_SubD::EdgeTag::SmoothX is an error here if ( false == quad_edge[fi]->IsCrease() ) return ON_SUBD_RETURN_ERROR(false); @@ -186,7 +183,7 @@ bool ON_SubDQuadNeighborhood::IsValid() const if (nullptr == side_face[fi]) return ON_SUBD_RETURN_ERROR(false); - // A tag of ON_SubD::EdgeTag::X is permitted here + // A tag of ON_SubD::EdgeTag::SmoothX is permitted here if ( 2 != quad_edge[fi]->m_face_count) return ON_SUBD_RETURN_ERROR(false); @@ -395,10 +392,10 @@ void ON_SubDQuadNeighborhood::Internal_Destroy(bool bReinitialize) m_bCenterEdgeLimitPoint[2] = false; m_bCenterEdgeLimitPoint[3] = false; - m_center_edge_limit_point[0] = ON_SubDSectorLimitPoint::Nan; - m_center_edge_limit_point[1] = ON_SubDSectorLimitPoint::Nan; - m_center_edge_limit_point[2] = ON_SubDSectorLimitPoint::Nan; - m_center_edge_limit_point[3] = ON_SubDSectorLimitPoint::Nan; + m_center_edge_limit_point[0] = ON_SubDSectorSurfacePoint::Nan; + m_center_edge_limit_point[1] = ON_SubDSectorSurfacePoint::Nan; + m_center_edge_limit_point[2] = ON_SubDSectorSurfacePoint::Nan; + m_center_edge_limit_point[3] = ON_SubDSectorSurfacePoint::Nan; for (unsigned int i = 0; i < 4; i++) { @@ -673,7 +670,7 @@ void ON_SubDQuadNeighborhood::SetPatchStatus( { if ( nullptr != m_center_edges[i] - && ON_SubD::EdgeTag::X != m_center_edges[i]->m_edge_tag + && ON_SubD::EdgeTag::SmoothX != m_center_edges[i]->m_edge_tag && (bCenterEdgeIsSmooth[i] != bCenterEdgeIsCrease[i]) ) { @@ -1220,7 +1217,7 @@ bool ON_SubDQuadNeighborhood::GetSubdivisionPoint( if (nullptr==vertex) break; double Q[3]; - if (!vertex->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,true,Q)) + if (!vertex->GetSubdivisionPoint(Q)) break; subdivision_point[0] = Q[0]; subdivision_point[1] = Q[1]; @@ -1244,7 +1241,7 @@ bool ON_SubDQuadNeighborhood::GetSubdivisionPoint( if (nullptr==edge) break; double Q[3]; - if (!edge->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,true,Q)) + if (!edge->GetSubdivisionPoint(Q)) break; subdivision_point[0] = Q[0]; subdivision_point[1] = Q[1]; @@ -1269,7 +1266,7 @@ bool ON_SubDQuadNeighborhood::GetSubdivisionPoint( if (nullptr==face) break; double Q[3]; - if (!face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,true,Q)) + if (!face->GetSubdivisionPoint(Q)) break; subdivision_point[0] = Q[0]; subdivision_point[1] = Q[1]; @@ -1288,8 +1285,6 @@ unsigned int ON_SubDQuadNeighborhood::SetLimitSubSurfaceExactCVs( if (nullptr == m_face_grid[1][1] || quadrant_index > 4) return ON_SUBD_RETURN_ERROR(0); - const bool bUseSavedSubdivisionPoint = true; - ON_2dex dex; ON_2dex deltadex; const ON_SubDFace* face; @@ -1311,7 +1306,7 @@ unsigned int ON_SubDQuadNeighborhood::SetLimitSubSurfaceExactCVs( // is calculated here because if face->GetSubdivisionPoint() fails, // nothing below should succeed. The value of Q[2] is assigned to m_srf_cv1[2][2] // after the 8 ring points are successfully calculated. - if (!face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[2])) + if (!face->GetSubdivisionPoint( Q[2])) return ON_SUBD_RETURN_ERROR(0); // faces_vertices[] face, in counterclockwise order. @@ -1326,11 +1321,11 @@ unsigned int ON_SubDQuadNeighborhood::SetLimitSubSurfaceExactCVs( { if (nullptr == faces_vertices[fei]) return ON_SUBD_RETURN_ERROR(0); - if (!faces_vertices[fei]->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[0])) + if (!faces_vertices[fei]->GetSubdivisionPoint( Q[0])) return ON_SUBD_RETURN_ERROR(0); if (nullptr == m_center_edges[fei]) return ON_SUBD_RETURN_ERROR(0); - if (!m_center_edges[fei]->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[1])) + if (!m_center_edges[fei]->GetSubdivisionPoint( Q[1])) return ON_SUBD_RETURN_ERROR(0); dex = srf_cv_dex[2 * fei]; @@ -1390,20 +1385,20 @@ unsigned int ON_SubDQuadNeighborhood::SetLimitSubSurfaceExactCVs( const ON_SubDEdge* edge = m_edge_grid[side_fvi][0]; if (nullptr == edge) return ON_SUBD_RETURN_ERROR(0); - if (!edge->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[0])) + if (!edge->GetSubdivisionPoint(Q[0])) return ON_SUBD_RETURN_ERROR(0); const ON_2dex fdex = ON_SubDQuadNeighborhood::GridDex(3, side_fvi, 1, 0); face = m_face_grid[fdex.i][fdex.j]; if (nullptr == face) return ON_SUBD_RETURN_ERROR(0); - if (!face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[1])) + if (!face->GetSubdivisionPoint(Q[1])) return ON_SUBD_RETURN_ERROR(0); edge = m_edge_grid[side_fvi][1]; if (nullptr == edge) return ON_SUBD_RETURN_ERROR(0); - if (!edge->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[2])) + if (!edge->GetSubdivisionPoint(Q[2])) return ON_SUBD_RETURN_ERROR(0); } @@ -1481,7 +1476,7 @@ unsigned int ON_SubDQuadNeighborhood::SetLimitSubSurfaceExactCVs( const ON_SubDFace* face_ij = m_face_grid[dex.i][dex.j]; if (nullptr == face_ij) return ON_SUBD_RETURN_ERROR(0); - if (!face_ij->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, Q[0])) + if (!face_ij->GetSubdivisionPoint(Q[0])) return ON_SUBD_RETURN_ERROR(0); P1[0] = Q[0][0]; P1[1] = Q[0][1]; P1[2] = Q[0][2]; } @@ -1669,7 +1664,6 @@ bool ON_SubDQuadNeighborhood::Internal_GetApproximateCV( } bool bHaveApproximateCV; - const bool bUseSavedSubdivisionPoint = true; if (nullptr != e) { const int extraordinary_vertex_index = ExtraordinaryCenterVertexIndex(ON_SubD::VertexTag::Crease, 4); @@ -1687,11 +1681,11 @@ bool ON_SubDQuadNeighborhood::Internal_GetApproximateCV( } else { - bHaveApproximateCV = e->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, bUseSavedSubdivisionPoint, approximate_cv); + bHaveApproximateCV = e->GetSubdivisionPoint(approximate_cv); } } else if ( nullptr != f && 4 == f->m_edge_count ) - bHaveApproximateCV = f->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,bUseSavedSubdivisionPoint,approximate_cv); + bHaveApproximateCV = f->GetSubdivisionPoint(approximate_cv); else { bHaveApproximateCV = false; @@ -1710,7 +1704,7 @@ static double ON_SubDQuadFaceTopology_CopySectorWeight( if (nullptr == e0 || nullptr == e0v) return ON_SUBD_RETURN_ERROR(false); - if (ON_SubD::EdgeTag::Smooth != e0->m_edge_tag && ON_SubD::EdgeTag::X != e0->m_edge_tag ) + if (ON_SubD::EdgeTag::Smooth != e0->m_edge_tag && ON_SubD::EdgeTag::SmoothX != e0->m_edge_tag ) return ON_SubDSectorType::IgnoredSectorWeight; if (e0v == e0->m_vertex[0]) @@ -1722,7 +1716,7 @@ static double ON_SubDQuadFaceTopology_CopySectorWeight( return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); } -static ON_SubDEdge* ON_SubDQuadFaceTopology_SubdivideEdge( +static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge( ON_SubD_FixedSizeHeap& fsh, ON_SubDVertex* qv1, const ON_SubDVertex* qv0, @@ -1730,25 +1724,25 @@ static ON_SubDEdge* ON_SubDQuadFaceTopology_SubdivideEdge( ) { if (nullptr == qv1 || nullptr == e0) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); - ON_SubDVertex* v1 = fsh.AllocateVertex(e0,ON_SubD::SubDType::QuadCatmullClark,true,4,4); + ON_SubDVertex* v1 = fsh.AllocateVertex(e0); if (nullptr == v1) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); double v0_weight; if ( e0->IsSmooth() && nullptr != qv0) { // qv1 is the subdivision point of qv0. if ( qv1->m_vertex_tag != qv0->m_vertex_tag ) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); if ( ON_SubD::VertexTag::Smooth == qv0->m_vertex_tag) v0_weight = ON_SubDSectorType::IgnoredSectorWeight; else { v0_weight = ON_SubDQuadFaceTopology_CopySectorWeight(e0, qv0); if (false == ON_SubDSectorType::IsValidSectorWeightValue(v0_weight,false)) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); } } else @@ -1756,48 +1750,49 @@ static ON_SubDEdge* ON_SubDQuadFaceTopology_SubdivideEdge( const double v1_weight = ON_SubDSectorType::IgnoredSectorWeight; - ON_SubDEdge* e1 = fsh.AllocateEdge(qv1,v0_weight,v1,v1_weight); + ON_SubDEdgePtr ep1 = fsh.AllocateEdge(qv1,v0_weight,v1,v1_weight); + ON_SubDEdge* e1 = ep1.Edge(); if (nullptr == e1) - return ON_SUBD_RETURN_ERROR(nullptr); + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); if (e1->m_edge_tag != e0->m_edge_tag) { // On the first subdivision step, - // e0 with tag ON_SubD::EdgeTag::X turns into + // e0 with tag ON_SubD::EdgeTag::SmoothX turns into // e1 with tag ON_SubD::EdgeTag::Smooth. - if ( ON_SubD::EdgeTag::Smooth != e1->m_edge_tag || ON_SubD::EdgeTag::X != e0->m_edge_tag) - return ON_SUBD_RETURN_ERROR(nullptr); + if ( ON_SubD::EdgeTag::Smooth != e1->m_edge_tag || ON_SubD::EdgeTag::SmoothX != e0->m_edge_tag) + return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); } - return e1; + return ep1; } static ON_SubDFace* ON_SubDQuadFaceTopology_SubdivideFace( ON_SubD_FixedSizeHeap& fsh, const ON_SubDFace* f0, - ON_SubDEdge* e1[2], + ON_SubDEdgePtr e1[2], double at_crease_weight ) { ON_SubDVertex* v[4]; - if (nullptr == f0 || nullptr == e1[0] || nullptr == e1[1]) + if (nullptr == f0 || nullptr == e1[0].Edge() || nullptr == e1[1].Edge()) return ON_SUBD_RETURN_ERROR(nullptr); - v[0] = const_cast(e1[0]->m_vertex[0]); - if (nullptr == v[0] || v[0] != e1[1]->m_vertex[0]) + v[0] = const_cast(e1[0].RelativeVertex(0)); + if (nullptr == v[0] || v[0] != e1[1].RelativeVertex(0)) return ON_SUBD_RETURN_ERROR(nullptr); - v[1] = const_cast(e1[0]->m_vertex[1]); + v[1] = const_cast(e1[0].RelativeVertex(1)); if (nullptr == v[1] ) return ON_SUBD_RETURN_ERROR(nullptr); - v[3] = const_cast(e1[1]->m_vertex[1]); + v[3] = const_cast(e1[1].RelativeVertex(1)); if (nullptr == v[3] ) return ON_SUBD_RETURN_ERROR(nullptr); if (v[1] == v[0] || v[3] == v[0] || v[1] == v[3]) return ON_SUBD_RETURN_ERROR(nullptr); - v[2] = fsh.AllocateVertex(f0,ON_SubD::SubDType::QuadCatmullClark,true,4,4); + v[2] = fsh.FindOrAllocateVertex(f0); if ( nullptr == v[2]) return ON_SUBD_RETURN_ERROR(nullptr); @@ -1805,20 +1800,15 @@ static ON_SubDFace* ON_SubDQuadFaceTopology_SubdivideFace( const double v2_weight = ON_SubDSectorType::IgnoredSectorWeight; const double v3_weight = (ON_SubD::VertexTag::Crease == v[3]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorWeight; - ON_SubDEdge* e12 = fsh.AllocateEdge(v[1],v1_weight,v[2],v2_weight); - if ( nullptr == e12) + ON_SubDEdgePtr e12 = fsh.AllocateEdge(v[1],v1_weight,v[2],v2_weight); + if ( nullptr == e12.Edge()) return ON_SUBD_RETURN_ERROR(nullptr); - ON_SubDEdge* e23 = fsh.AllocateEdge(v[2],v2_weight,v[3],v3_weight); - if ( nullptr == e23) + ON_SubDEdgePtr e23 = fsh.AllocateEdge(v[2],v2_weight,v[3],v3_weight); + if ( nullptr == e23.Edge()) return ON_SUBD_RETURN_ERROR(nullptr); - ON_SubDEdgePtr f1_epts[4] = { - ON_SubDEdgePtr::Create(e1[0], 0), - ON_SubDEdgePtr::Create(e12, 0), - ON_SubDEdgePtr::Create(e23, 0), - ON_SubDEdgePtr::Create(e1[1], 1) - }; + ON_SubDEdgePtr f1_epts[4] = { e1[0], e12, e23, e1[1].Reversed() }; ON_SubDFace* f1 = fsh.AllocateQuad(f0->m_zero_face_id, f0->m_id, f1_epts); if ( nullptr == f1) @@ -1839,13 +1829,11 @@ bool ON_SubDQuadNeighborhood::Subdivide( if ( m_fsh == &fsh ) return ON_SUBD_RETURN_ERROR(false); - const ON_SubD::SubDType subd_type = ON_SubD::SubDType::QuadCatmullClark; - // When a smooth subdivision edge ends at a vertex that is a subdivision point // of a creased original edge, this is the value to assign to the new // edge's m_vertex_weight. The "2" is there because there would be 2 // sector faces if the subdivision was complete. - const double at_crease_weight = ON_SubDSectorType::CreaseSectorWeight(subd_type,2); + const double at_crease_weight = ON_SubDSectorType::CreaseSectorWeight(2); if ( m_fsh == q1ft->m_fsh) q1ft->m_fsh = nullptr; @@ -1883,9 +1871,23 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); const ON_SubDVertex* qv0 = qf0_vertices[0]; - if ( nullptr == qv0 || qv0->m_face_count > qv0->m_edge_count) + if ( nullptr == qv0 ) return ON_SUBD_RETURN_ERROR(false); + if (nullptr == qv0 || qv0->m_face_count > qv0->m_edge_count) + { + if (ON_SubD::VertexTag::Corner == qv0->m_vertex_tag) + { + // nonmanifold case can have face_count > edge_count + if ( qv0->m_edge_count < 3 ) + return ON_SUBD_RETURN_ERROR(false); + } + else + { + return ON_SUBD_RETURN_ERROR(false); + } + } + const unsigned int N = qv0->m_edge_count; if (N < ON_SubDSectorType::MinimumSectorEdgeCount(qv0->m_vertex_tag)) return ON_SUBD_RETURN_ERROR(false); @@ -1911,10 +1913,10 @@ bool ON_SubDQuadNeighborhood::Subdivide( : ON_SubDSectorIterator::StopAt::AnyCrease ; - if ( false == fsh.ReserveSubDWorkspace( subd_type, N) ) + if ( false == fsh.ReserveSubDWorkspace(qf0) ) return ON_SUBD_RETURN_ERROR(false); - ON_SubDVertex* qv1 = fsh.AllocateVertex(qv0,subd_type,true,N,qv0->m_face_count); + ON_SubDVertex* qv1 = fsh.AllocateVertex(qv0,N); if ( nullptr == qv1 ) return ON_SUBD_RETURN_ERROR(false); //qv1->m_vertex_facet_type = ON_SubD::VertexFacetType::Quad; @@ -1926,17 +1928,17 @@ bool ON_SubDQuadNeighborhood::Subdivide( ON_SubDFace* face_grid1[3][3] = {}; ON_SubDVertex* vertex_grid1[4][4] = {}; - ON_SubDEdge* edge_grid1[4][2] = {}; + ON_SubDEdgePtr edge_grid1[4][2] = {}; // edge1 = new edge from qv1 to edge0 subdivision point - ON_SubDEdge* edge1 = ON_SubDQuadFaceTopology_SubdivideEdge(fsh,qv1,qv0,edge0); - if (nullptr == edge1) + ON_SubDEdgePtr edgep1 = ON_SubDQuadFaceTopology_SubdivideEdge(fsh,qv1,qv0,edge0); + if (edgep1.IsNull()) return ON_SUBD_RETURN_ERROR(false); // prepare to rotate coutnerclockwise around qv0, // subdividing edges and adding new faces const ON_SubDEdge* e0[2] = {nullptr, edge0}; - ON_SubDEdge* e1[2] = {nullptr, edge1}; + ON_SubDEdgePtr e1[2] = {ON_SubDEdgePtr::Null, edgep1}; const ON_SubDFace* f0 = qf0; ON_SubDFace* f1 = nullptr; bool bAtBoundaryCrease = false; @@ -1951,10 +1953,10 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); e1[0] = e1[1]; if (edge0 == e0[1]) - e1[1] = edge1; // back to where we started + e1[1] = edgep1; // back to where we started else e1[1] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, qv1, qv0, e0[1]); - if (nullptr == e1[1]) + if (e1[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); f1 = ON_SubDQuadFaceTopology_SubdivideFace(fsh, f0, e1, at_crease_weight); @@ -1966,8 +1968,8 @@ bool ON_SubDQuadNeighborhood::Subdivide( face_grid1[0][1] = f1; vertex_grid1[0][1] = const_cast(f1->Vertex(3)); vertex_grid1[0][2] = const_cast(f1->Vertex(2)); - edge_grid1[3][0] = const_cast(f1->Edge(1)); - edge_grid1[3][1] = const_cast(f1->Edge(3)); + edge_grid1[3][0] = f1->EdgePtr(1); + edge_grid1[3][1] = f1->EdgePtr(3); } if (0 == i) @@ -1995,7 +1997,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( if (bAtBoundaryCrease) { - e1[1]->m_edge_tag = ON_SubD::EdgeTag::Crease; + e1[1].Edge()->m_edge_tag = ON_SubD::EdgeTag::Crease; break; } @@ -2055,8 +2057,8 @@ bool ON_SubDQuadNeighborhood::Subdivide( e0[0] = edge0; e0[1] = nullptr; - e1[0] = edge1; - e1[1] = nullptr; + e1[0] = edgep1; + e1[1] = ON_SubDEdgePtr::Null; f1 = nullptr; for (unsigned int i = 0; i < N - edge_mark; i++) { @@ -2066,7 +2068,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); e1[1] = e1[0]; e1[0] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, qv1, qv0, e0[0]); - if (nullptr == e1[0]) + if (e1[0].IsNull()) return ON_SUBD_RETURN_ERROR(false); f1 = ON_SubDQuadFaceTopology_SubdivideFace(fsh, f0, e1, at_crease_weight); @@ -2082,7 +2084,6 @@ bool ON_SubDQuadNeighborhood::Subdivide( bFinished = (nullptr == f0 && ON_SubD::EdgeTag::Crease == e0[0]->m_edge_tag && qv1->m_face_count+1 == qv1->m_edge_count); if (false == bFinished) return ON_SUBD_RETURN_ERROR(false); - //qv1->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; // edges no longer radially sorted break; } } @@ -2099,42 +2100,41 @@ bool ON_SubDQuadNeighborhood::Subdivide( face_grid1[1][0] = f1; vertex_grid1[1][0] = const_cast(f1->Vertex(1)); vertex_grid1[2][0] = const_cast(f1->Vertex(2)); - edge_grid1[0][0] = const_cast(f1->Edge(0)); - edge_grid1[0][1] = const_cast(f1->Edge(2)); + edge_grid1[0][0] = f1->EdgePtr(0); + edge_grid1[0][1] = f1->EdgePtr(2); } // Add the 7 remaining elements to vertex_grid1[][] if (false == bBoundaryCrease1[0]) { - vertex_grid1[3][0] = fsh.AllocateVertex(m_edge_grid[q0fvi][1],ON_SubD::SubDType::QuadCatmullClark,true,2,1); + vertex_grid1[3][0] = fsh.AllocateVertex(m_edge_grid[q0fvi][1]); if ( nullptr == vertex_grid1[3][0]) return ON_SUBD_RETURN_ERROR(false); } - vertex_grid1[3][1] = fsh.AllocateVertex(qf0_vertices[1],ON_SubD::SubDType::QuadCatmullClark,true,3,2); + vertex_grid1[3][1] = fsh.AllocateVertex(qf0_vertices[1],3); if ( nullptr == vertex_grid1[3][1]) return ON_SUBD_RETURN_ERROR(false); - - vertex_grid1[3][2] = fsh.AllocateVertex(qf0_edges[1],ON_SubD::SubDType::QuadCatmullClark,true,3,2); + vertex_grid1[3][2] = fsh.AllocateVertex(qf0_edges[1]); if ( nullptr == vertex_grid1[3][2]) return ON_SUBD_RETURN_ERROR(false); - vertex_grid1[3][3] = fsh.AllocateVertex(qf0_vertices[2],ON_SubD::SubDType::QuadCatmullClark,true,2,1); + vertex_grid1[3][3] = fsh.AllocateVertex(qf0_vertices[2],2); if ( nullptr == vertex_grid1[3][3]) return ON_SUBD_RETURN_ERROR(false); - vertex_grid1[2][3] = fsh.AllocateVertex(qf0_edges[2],ON_SubD::SubDType::QuadCatmullClark,true,3,2); + vertex_grid1[2][3] = fsh.AllocateVertex(qf0_edges[2]); if ( nullptr == vertex_grid1[2][3]) return ON_SUBD_RETURN_ERROR(false); - vertex_grid1[1][3] = fsh.AllocateVertex(qf0_vertices[3],ON_SubD::SubDType::QuadCatmullClark,true,3,2); + vertex_grid1[1][3] = fsh.AllocateVertex(qf0_vertices[3],3); if ( nullptr == vertex_grid1[1][3]) return ON_SUBD_RETURN_ERROR(false); if (false == bBoundaryCrease1[3]) { - vertex_grid1[0][3] = fsh.AllocateVertex(m_edge_grid[(q0fvi+3)%4][0],ON_SubD::SubDType::QuadCatmullClark,true,2,1); + vertex_grid1[0][3] = fsh.AllocateVertex(m_edge_grid[(q0fvi+3)%4][0]); if ( nullptr == vertex_grid1[0][3]) return ON_SUBD_RETURN_ERROR(false); } @@ -2145,7 +2145,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[3][1], ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[0], qf0_vertices[1]) ); - if ( nullptr == edge_grid1[1][0]) + if ( edge_grid1[1][0].IsNull()) return ON_SUBD_RETURN_ERROR(false); edge_grid1[1][1] = fsh.AllocateEdge( @@ -2154,7 +2154,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[3][2], at_crease_weight // ignored unless vertex_grid1[3][2] is tagged as a crease ); - if ( nullptr == edge_grid1[1][1]) + if (edge_grid1[1][1].IsNull()) return ON_SUBD_RETURN_ERROR(false); edge_grid1[2][0] = fsh.AllocateEdge( @@ -2163,7 +2163,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[2][3], at_crease_weight // ignored unless vertex_grid1[2][3] is tagged as a crease ); - if ( nullptr == edge_grid1[2][0]) + if (edge_grid1[2][0].IsNull()) return ON_SUBD_RETURN_ERROR(false); edge_grid1[2][1] = fsh.AllocateEdge( @@ -2172,20 +2172,19 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[1][3], ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[3], qf0_vertices[3]) ); - if ( nullptr == edge_grid1[2][1]) + if (edge_grid1[2][1].IsNull()) return ON_SUBD_RETURN_ERROR(false); // Add the 5 remaining elements to face_grid1[][] - ON_SubDEdge* fedges[4]; - ON_SubDEdgePtr feptrs[4]; + ON_SubDEdgePtr fedges[4]; if (false == bBoundaryCrease1[0]) { - fedges[0] = nullptr; - fedges[1] = nullptr; + fedges[0] = ON_SubDEdgePtr::Null; + fedges[1] = ON_SubDEdgePtr::Null; fedges[2] = edge_grid1[1][0]; fedges[3] = edge_grid1[0][1]; - if (nullptr == fedges[2] || nullptr == fedges[3] ) + if (fedges[2].IsNull() || fedges[3].IsNull() ) return ON_SUBD_RETURN_ERROR(false); fedges[0] = fsh.AllocateEdge( @@ -2194,7 +2193,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[3][0], at_crease_weight // ignored unless vertex_grid1[3][0] is tagged as a crease ); - if (nullptr == fedges[0]) + if (fedges[0].IsNull()) return ON_SUBD_RETURN_ERROR(false); // m_edge_grid[q0fvi][1] fedges[1] = fsh.AllocateEdge( @@ -2203,26 +2202,26 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[3][1], ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[q0fvi][1], qf0_vertices[1]) ); - if (nullptr == fedges[1]) + if (fedges[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); - feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0); - feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0); - feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],1); - feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1); - - face_grid1[2][0] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs); + face_grid1[2][0] = fsh.AllocateQuad(zero_face_id, parent_face_id, + fedges[0], + fedges[1], + fedges[2].Reversed(), + fedges[3].Reversed() + ); if (nullptr == face_grid1[2][0]) return ON_SUBD_RETURN_ERROR(false); } // face_grid1[2][1] fedges[0] = edge_grid1[1][0]; - fedges[1] = nullptr; + fedges[1] = ON_SubDEdgePtr::Null; fedges[2] = edge_grid1[1][1]; - fedges[3] = ON_SUBD_EDGE_POINTER(face_grid1[1][1]->m_edge4[1].m_ptr); + fedges[3] = face_grid1[1][1]->m_edge4[1]; - if (nullptr == fedges[0] || nullptr == fedges[2] || nullptr == fedges[3] ) + if ( fedges[0].IsNull() || fedges[2].IsNull() || fedges[3].IsNull() ) return ON_SUBD_RETURN_ERROR(false); fedges[1] = fsh.AllocateEdge( @@ -2231,25 +2230,28 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[3][2], ON_SubDSectorType::IgnoredSectorWeight ); - if (nullptr == fedges[1]) + if (fedges[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); - feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0); - feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0); - feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],1); - feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1); - face_grid1[2][1] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs); + face_grid1[2][1] = fsh.AllocateQuad( + zero_face_id, + parent_face_id, + fedges[0], + fedges[1], + fedges[2].Reversed(), + fedges[3].Reversed() + ); if (nullptr == face_grid1[2][1]) return ON_SUBD_RETURN_ERROR(false); // face_grid1[2][2] fedges[0] = edge_grid1[1][1]; - fedges[1] = nullptr; - fedges[2] = nullptr; + fedges[1] = ON_SubDEdgePtr::Null; + fedges[2] = ON_SubDEdgePtr::Null; fedges[3] = edge_grid1[2][0]; - if (nullptr == fedges[0] || nullptr == fedges[3] ) + if ( fedges[0].IsNull() || fedges[3].IsNull() ) return ON_SUBD_RETURN_ERROR(false); fedges[1] = fsh.AllocateEdge( @@ -2258,7 +2260,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[3][3], ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[2]) ); - if (nullptr == fedges[1]) + if (fedges[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); fedges[2] = fsh.AllocateEdge( @@ -2267,25 +2269,27 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[2][3], ON_SubDSectorType::IgnoredSectorWeight ); - if (nullptr == fedges[2]) + if (fedges[2].IsNull()) return ON_SUBD_RETURN_ERROR(false); - feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0); - feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0); - feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],0); - feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1); - - face_grid1[2][2] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs); + face_grid1[2][2] = fsh.AllocateQuad( + zero_face_id, + parent_face_id, + fedges[0], + fedges[1], + fedges[2], + fedges[3].Reversed() + ); if (nullptr == face_grid1[2][2]) return ON_SUBD_RETURN_ERROR(false); // face_grid1[1][2] - fedges[0] = ON_SUBD_EDGE_POINTER(face_grid1[1][1]->m_edge4[2].m_ptr); + fedges[0] = face_grid1[1][1]->m_edge4[2]; fedges[1] = edge_grid1[2][0]; - fedges[2] = nullptr; + fedges[2] = ON_SubDEdgePtr::Null; fedges[3] = edge_grid1[2][1]; - if (nullptr == fedges[0] || nullptr == fedges[1] || nullptr == fedges[3] ) + if (fedges[0].IsNull() || fedges[1].IsNull() ||fedges[3].IsNull() ) return ON_SUBD_RETURN_ERROR(false); fedges[2] = fsh.AllocateEdge( @@ -2294,26 +2298,27 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[1][3], ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[3]) ); - if (nullptr == fedges[2]) + if (fedges[2].IsNull()) return ON_SUBD_RETURN_ERROR(false); - feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],1); - feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0); - feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],0); - feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1); - - face_grid1[1][2] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs); + face_grid1[1][2] = fsh.AllocateQuad( + zero_face_id, parent_face_id, + fedges[0].Reversed(), + fedges[1], + fedges[2], + fedges[3].Reversed() + ); if (nullptr == face_grid1[1][2]) return ON_SUBD_RETURN_ERROR(false); if (false == bBoundaryCrease1[3]) { fedges[0] = edge_grid1[2][1]; - fedges[1] = nullptr; - fedges[2] = nullptr; + fedges[1] = ON_SubDEdgePtr::Null; + fedges[2] = ON_SubDEdgePtr::Null; fedges[3] = edge_grid1[3][0]; - if (nullptr == fedges[0] || nullptr == fedges[3] ) + if ( fedges[0].IsNull() || fedges[3].IsNull() ) return ON_SUBD_RETURN_ERROR(false); fedges[1] = fsh.AllocateEdge( @@ -2322,7 +2327,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[0][3], ON_SubDSectorType::IgnoredSectorWeight ); - if (nullptr == fedges[1]) + if ( fedges[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); fedges[2] = fsh.AllocateEdge( @@ -2331,15 +2336,16 @@ bool ON_SubDQuadNeighborhood::Subdivide( vertex_grid1[0][2], ON_SubDSectorType::IgnoredSectorWeight ); - if (nullptr == fedges[2]) + if ( fedges[2].IsNull()) return ON_SUBD_RETURN_ERROR(false); - feptrs[0] = ON_SubDEdgePtr::Create(fedges[0],0); - feptrs[1] = ON_SubDEdgePtr::Create(fedges[1],0); - feptrs[2] = ON_SubDEdgePtr::Create(fedges[2],0); - feptrs[3] = ON_SubDEdgePtr::Create(fedges[3],1); - - face_grid1[0][2] = fsh.AllocateQuad(zero_face_id, parent_face_id, feptrs); + face_grid1[0][2] = fsh.AllocateQuad( + zero_face_id, parent_face_id, + fedges[0], + fedges[1], + fedges[2], + fedges[3].Reversed() + ); if (nullptr == face_grid1[0][2]) return ON_SUBD_RETURN_ERROR(false); } @@ -2364,8 +2370,8 @@ bool ON_SubDQuadNeighborhood::Subdivide( { unsigned int j = (q0fvi + i) % 4; q1ft->m_bBoundaryCrease[j] = bBoundaryCrease1[i]; - q1ft->m_edge_grid[j][0] = edge_grid1[i][0]; - q1ft->m_edge_grid[j][1] = edge_grid1[i][1]; + q1ft->m_edge_grid[j][0] = edge_grid1[i][0].Edge(); + q1ft->m_edge_grid[j][1] = edge_grid1[i][1].Edge(); q1ft->m_center_edges[j] = face_grid1[1][1]->Edge(i); dex = ON_SubDQuadNeighborhood::GridDex(4,q0fvi,i,0); @@ -2382,23 +2388,26 @@ bool ON_SubDQuadNeighborhood::Subdivide( if ( m_bExtraordinaryCornerVertex[q0fvi] ) { // evaluate extraordinary limit point as soon as possible and then save it for future use. - ON_SubDSectorLimitPoint limit_point; - const ON_SubD::SubDType subd_type_local_scope = ON_SubD::SubDType::QuadCatmullClark; + ON_SubDSectorSurfacePoint limit_point; const ON_SubDFace* qv0_sector_face = this->m_face_grid[1][1]; const ON_SubDFace* qv1_sector_face = q1ft->m_face_grid[1][1]; - if (subd_type_local_scope == qv0->SavedLimitPointType() && qv0->GetLimitPoint(subd_type_local_scope,qv0_sector_face,true,limit_point) && limit_point.IsSet()) + if ( + qv0->SurfacePointIsSet() + && qv0->GetSurfacePoint(qv0_sector_face,limit_point) + && limit_point.IsSet(false) + ) { limit_point.m_sector_face = qv1_sector_face; // qv1's sector face limit_point.m_next_sector_limit_point = nullptr; // sector checks are required to get "first" face - qv1->SetSavedLimitPoint(subd_type_local_scope,limit_point); + qv1->SetSavedSurfacePoint(false,limit_point); } - else if (qv1->GetLimitPoint(subd_type_local_scope, qv1_sector_face, true, limit_point)) + else if (qv1->GetSurfacePoint( qv1_sector_face, limit_point)) { limit_point.m_sector_face = qv0_sector_face; limit_point.m_next_sector_limit_point = nullptr; // sector checks are required to get "first" face - qv0->SetSavedLimitPoint(subd_type_local_scope,limit_point); + qv0->SetSavedSurfacePoint(false,limit_point); } } @@ -2473,10 +2482,10 @@ ON_2dex ON_SubDQuadNeighborhood::DeltaDex( -bool ON_SubDFace::GetQuadLimitSurface( +bool ON_SubDFace::GetQuadSurface( + double* limit_surface_cv, size_t limit_surface_cv_stride0, - size_t limit_surface_cv_stride1, - double* limit_surface_cv + size_t limit_surface_cv_stride1 ) const { double srf_cv[4][4][3]; @@ -2511,7 +2520,7 @@ bool ON_SubDFace::GetQuadLimitSurface( return true; } -bool ON_SubDFace::GetQuadLimitSurface( +bool ON_SubDFace::GetQuadSurface( class ON_NurbsSurface& nurbs_surface ) const { @@ -2524,7 +2533,7 @@ class ON_NurbsSurface& nurbs_surface { return false; } - if (false == GetQuadLimitSurface(nurbs_surface.m_cv_stride[0], nurbs_surface.m_cv_stride[1], nurbs_surface.m_cv)) + if (false == GetQuadSurface(nurbs_surface.m_cv, nurbs_surface.m_cv_stride[0], nurbs_surface.m_cv_stride[1])) return false; // evaluation domain will be [0,1] x [0,1] @@ -2538,7 +2547,7 @@ class ON_NurbsSurface& nurbs_surface return true; } -bool ON_SubDFace::GetQuadLimitSurface( +bool ON_SubDFace::GetQuadSurface( class ON_BezierSurface& bezier_surface ) const { @@ -2567,7 +2576,7 @@ class ON_BezierSurface& bezier_surface nurbs_surface.m_knot_capacity[0] = 0; nurbs_surface.m_knot_capacity[1] = 0; - if (false == GetQuadLimitSurface(nurbs_surface)) + if (false == GetQuadSurface(nurbs_surface)) return false; if (false == nurbs_surface.ClampEnd(0, 2)) @@ -2583,259 +2592,67 @@ class ON_BezierSurface& bezier_surface ON_SubDFaceNeighborhood::~ON_SubDFaceNeighborhood() { - ReserveCapacity(ON_SubD::SubDType::Unset,nullptr); + ReserveCapacity(nullptr); } bool ON_SubDFaceNeighborhood::ReserveCapacity( - ON_SubD::SubDType subd_type, const ON_SubDFace* face ) { m_face0 = nullptr; - m_subd_type = ON_SubD::SubDType::Unset; m_center_vertex1 = nullptr; m_face1_count = 0; m_face1 = nullptr; - unsigned int v_capacity = 0; - unsigned int e_capacity = 0; - unsigned int f_capacity = 0; - unsigned int a_capacity = 0; - - for (;;) - { - if (nullptr == face) - break; - - const unsigned int N = face->m_edge_count; - if (N <= 2) - break; - - // ordinary valence - //const unsigned short V0 - // = (ON_SubD::FacetType::Quad == ON_SubD::FacetTypeFromSubDType(subd_type)) - // ? 4 - // : 6; - // set E = extraordinary vertex valence overhead - unsigned int S = 0; - //unsigned int E = 0; - const ON_SubDEdgePtr* edges = face->m_edge4; - ON__UINT_PTR edge_ptr; - const ON_SubDEdge* edge; - const ON_SubDVertex* vertex; - unsigned int i; - for (i = 0; i < N; i++, edges++) - { - if (4 == i) - { - if (nullptr == face->m_edgex) - return ON_SUBD_RETURN_ERROR(false); - edges = face->m_edgex; - } - edge_ptr = edges->m_ptr; - edge = ON_SUBD_EDGE_POINTER(edge_ptr); - if (nullptr == edge) - break; - vertex = edge->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)]; - if (nullptr == vertex) - break; - if (vertex->m_edge_count < 2 ) - break; - if (vertex->m_edge_count < vertex->m_face_count ) - break; - S += vertex->m_edge_count; - //if (vertex->m_edge_count > V0 ) - // E += (vertex->m_edge_count - V0); - } - if (i != N) - break; - - if (ON_SubD::SubDType::QuadCatmullClark == subd_type) - { - f_capacity = S; - e_capacity = 3*S - N; - v_capacity = 2*(S - N) + 1; - a_capacity = 4*f_capacity + 2*e_capacity + 2*v_capacity; - } - else if (ON_SubD::SubDType::TriLoopWarren == subd_type) - { - // todo -- add tri support - // (remember to add 4 to a_capacity for the ON_SubDFaceNeighborhood.m_face1[] array - // when N = 3). - break; - } - else - { - break; - } - - return m_fsh.ReserveSubDWorkspace(v_capacity, e_capacity, f_capacity, a_capacity); - } - - m_fsh.ReserveSubDWorkspace(0,0,0,0); - if (ON_SubD::SubDType::Unset == subd_type && nullptr == face ) - return true; - return ON_SUBD_RETURN_ERROR(false); + // reserve enough room to store the subdivided face and all neighboring components. + return m_fsh.ReserveSubDWorkspace(face); } bool ON_SubDFaceNeighborhood::Subdivide( - ON_SubD::SubDType subd_type, const ON_SubDFace* face ) { - if (false == ReserveCapacity(subd_type, face)) + if (false == ReserveCapacity(face)) return ON_SUBD_RETURN_ERROR(false); - - if (ON_SubD::SubDType::QuadCatmullClark == subd_type) - return ON_SubDFaceNeighborhood::QuadSubdivideHelper(face); - - if (ON_SubD::SubDType::TriLoopWarren == subd_type) - return ON_SubDFaceNeighborhood::TriSubdivideHelper(face); - - return ON_SUBD_RETURN_ERROR(false); + return ON_SubDFaceNeighborhood::QuadSubdivideHelper(face); } - -bool ON_SubDFaceNeighborhood::TriSubdivideHelper( - const ON_SubDFace* face - ) -{ - // todo - add tri support - return ON_SUBD_RETURN_ERROR(false); -} - -////static bool RadialSort( -//// unsigned int vertex1_sort_mark, -//// ON_SubDVertex* vertex -//// ) -////{ -//// if (nullptr == vertex || vertex->m_face_count > vertex->m_edge_count) -//// return ON_SUBD_RETURN_ERROR(false); -//// -//// if ( 0 == vertex1_sort_mark) -//// return true; -//// -//// unsigned int e0_count = vertex1_sort_mark+1; -//// unsigned int f0_count= vertex1_sort_mark; -//// unsigned int e_count = vertex->m_edge_count; -//// unsigned int f_count = vertex->m_face_count; -//// if (e0_count > e_count || f0_count > f_count) -//// return ON_SUBD_RETURN_ERROR(false); -//// -//// ON__UINT_PTR stack_buffer[40]; -//// ON__UINT_PTR* buffer = 0; -//// if (e0_count <= sizeof(stack_buffer) / sizeof(stack_buffer[0])) -//// buffer = stack_buffer; -//// else -//// { -//// buffer = new (std::nothrow) ON__UINT_PTR[e0_count]; -//// if ( nullptr == buffer) -//// return ON_SUBD_RETURN_ERROR(false); -//// } -//// -//// unsigned int i, j; -//// ON__UINT_PTR* a; -//// -//// a = (ON__UINT_PTR*)vertex->m_edges; -//// for (i = 0; i < e0_count; i++) -//// { -//// buffer[i] = a[i]; -//// } -//// j = 0; -//// for (i = e0_count; i < e_count; i++) -//// { -//// a[j++] = a[i]; -//// } -//// for (i = 0; i < e0_count; i++) -//// { -//// a[j++] = buffer[i]; -//// } -//// -//// a = (ON__UINT_PTR*)vertex->m_faces; -//// for (i = 0; i < f0_count; i++) -//// { -//// buffer[i] = a[i]; -//// } -//// j = 0; -//// for (i = f0_count; i < f_count; i++) -//// { -//// a[j++] = a[i]; -//// } -//// for (i = 0; i < f0_count; i++) -//// { -//// a[j++] = buffer[i]; -//// } -//// -//// if ( buffer != stack_buffer) -//// delete[] buffer; -//// vertex->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial; -//// return true; -////} - - -#if defined(ON_DEBUG) - -//// debugging tool. If a call to this function is left in release code, the compile will fail (on purpose). -//static void ON_DEBUG_SubDBreakPoint(const ON_SubDFace* face) -//{ -// for (;;) -// { -// if (nullptr == face) -// break; -// if (0 != face->m_level) -// break; -// if (34 != face->m_id) -// break; -// ON_TextLog::Null.Print("Breakpoint here."); // does nothing prevents warning as error -// break; -// } -//#if !defined(ON_DEBUG) -//#error Do not use ON_DEBUG_SubDBreakPoint() in release builds. -//#endif -//} - -#endif - bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( const ON_SubDFace* face ) { // input face is valid and space is reserved. //ON_DEBUG_SubDBreakPoint(face); - - - const ON_SubD::SubDType subd_type = ON_SubD::SubDType::QuadCatmullClark; // When a smooth subdivision edge ends at a vertex that is a subdivision point // of a creased original edge, this is the value to assign to the new // edge's m_vertex_weight. The "2" is there because there would be 2 // sector faces if the subdivision was complete. - const double at_crease2_weight = ON_SubDSectorType::CreaseSectorWeight(subd_type,2); + const double at_crease2_weight = ON_SubDSectorType::CreaseSectorWeight(2); const unsigned int N = face->m_edge_count; const unsigned int zero_face_id = face->m_zero_face_id; const unsigned int parent_face_id = face->m_parent_face_id; - const bool bUseSavedSubdivisionPoint = true; - - ON_SubDSectorLimitPoint limit_point; + ON_SubDSectorSurfacePoint limit_point; // create central vertex - ON_SubDVertex* center_vertex1 = m_fsh.AllocateVertex(face,subd_type,bUseSavedSubdivisionPoint,N,N); + ON_SubDVertex* center_vertex1 = m_fsh.FindOrAllocateVertex(face); if ( nullptr == center_vertex1) return ON_SUBD_RETURN_ERROR(false); + if ( 1 != center_vertex1->m_id) + return ON_SUBD_RETURN_ERROR(false); const ON_SubDVertex* vertex0 = face->Vertex(0); if ( nullptr == vertex0 ) return ON_SUBD_RETURN_ERROR(false); - const bool bFirstVertexHasValence2 = (2 == vertex0->m_edge_count && 2 == vertex0->m_face_count && vertex0->IsSmoothOrDart()); const ON_SubDEdge* edge0 = nullptr; ON__UINT_PTR edge0_dir = 0; ON_SubDVertex* vertex1 = nullptr; - ON_SubDEdge* edge1 = nullptr; + ON_SubDEdgePtr edge1 = ON_SubDEdgePtr::Null; // Calculate 2*N subdivison vertices on the boundary of input "face" // and N subdivision edge that radiate from center_vertex1 to the input face edge's subdivison points. @@ -2860,29 +2677,33 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( if (vertex0->m_edge_count < 2) return ON_SUBD_RETURN_ERROR(false); - // One of the main reasons for creating a ON_SubDFaceNeighborhood is to calcualtethe + // One of the main reasons for creating a ON_SubDFaceNeighborhood is to calcualte the // limit mesh and limit cubic surfaces when the original face is not a quad. // For these calculations, we need to calculate and save the limit point for extraordinary // verticies while enough information is available to calculate it. Doing the calculation // before calling m_fsh.AllocateVertex(), insures the information will be copied to vertex1. - if ( false == vertex0->GetLimitPoint(subd_type,face,true,limit_point) ) + if ( false == vertex0->GetSurfacePoint(face,limit_point) ) return ON_SUBD_RETURN_ERROR(false); - vertex1 = m_fsh.AllocateVertex(vertex0,subd_type,bUseSavedSubdivisionPoint,vertex0->m_edge_count,vertex0->m_edge_count); + vertex1 = m_fsh.AllocateVertex(vertex0,vertex0->m_edge_count); if ( nullptr == vertex1) return ON_SUBD_RETURN_ERROR(false); - if (nullptr != limit_point.m_sector_face || subd_type != vertex1->SavedLimitPointType() ) + if ( 2*i+2 != vertex1->m_id ) + return ON_SUBD_RETURN_ERROR(false); + if (nullptr != limit_point.m_sector_face || false == vertex1->SurfacePointIsSet() ) { // While there may be multiple sectors around vertex0, the only limit point // that matters is this local subdivision is the one for vertex0 in the sector containing the input face. - limit_point.m_next_sector_limit_point = (ON_SubDSectorLimitPoint*)1; // causes unnecessary test to be skipped + limit_point.m_next_sector_limit_point = (ON_SubDSectorSurfacePoint*)1; // causes unnecessary test to be skipped limit_point.m_sector_face = nullptr; - vertex1->SetSavedLimitPoint(subd_type, limit_point); + vertex1->SetSavedSurfacePoint( false, limit_point); } // This vertex is either a crease (3 edges, 2 faces) or a smooth ordinary vertex (4 edges, 4 faces). - vertex1 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,4,4); + vertex1 = m_fsh.AllocateVertex(edge0); if ( nullptr == vertex1) return ON_SUBD_RETURN_ERROR(false); + if ( 2*i+3 != vertex1->m_id ) + return ON_SUBD_RETURN_ERROR(false); edge1 = m_fsh.AllocateEdge( center_vertex1, @@ -2890,7 +2711,11 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( vertex1, at_crease2_weight // ingored unless vertex1 is tagged as a crease ); - if ( nullptr == edge1) + if ( edge1.IsNull() ) + return ON_SUBD_RETURN_ERROR(false); + if ( 0 != edge1.EdgeDirection() ) + return ON_SUBD_RETURN_ERROR(false); + if ( i+1 != edge1.EdgeId() ) return ON_SUBD_RETURN_ERROR(false); } @@ -2902,12 +2727,11 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( // ring_vertex1[1] = subdivision vertex on input face->Vertex(i) // ring_vertex1[2] = subdivision vertex on input face->Edge(i) ON_SubDVertex* ring_vertex1[4] = { nullptr, nullptr, nullptr, vertex1 }; - - + // Calculate the 2*N subdivison edges on the boundary of input "face" // and N subdivision faces. ON_SubDFace* face1 = nullptr; - ON_SubDEdgePtr face1_eptrs[4] = {ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Create(edge1, 1)}; + ON_SubDEdgePtr face1_eptrs[4] = {ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null,ON_SubDEdgePtr::Null,edge1.Reversed()}; face_m_edges = face->m_edge4; for (unsigned int i = 0; i < N; i++, face_m_edges++) { @@ -2946,9 +2770,9 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( ring_vertex1[1], ON_SubDQuadFaceTopology_CopySectorWeight(prev_edge0, vertex0) ); - if ( nullptr == edge1 ) + if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); - face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1, 0); + face1_eptrs[1] = edge1; edge1 = m_fsh.AllocateEdge( @@ -2957,9 +2781,9 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( ring_vertex1[2], ON_SubDSectorType::IgnoredSectorWeight ); - if ( nullptr == edge1 ) + if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); - face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1, 0); + face1_eptrs[2] = edge1; face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs); if ( nullptr == face1) @@ -3029,9 +2853,6 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( ring_vertex1[2]->m_edges[1] = face1_eptrs[1]; ring_vertex1[2]->m_edges[2] = face1_eptrs[2]; - if (bFirstVertexHasValence2) - continue; - const ON_SubDFace* neighbor_face0 = edge0->NeighborFacePtr(face, false).Face(); if (nullptr == neighbor_face0) { @@ -3042,100 +2863,20 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( } continue; } - vertex1 = nullptr; - if (i > 0 && 2 == vertex0->m_edge_count && 2 == vertex0->m_face_count && 4 == ring_vertex1[0]->m_edge_count) - { - // uncommon valence 2 case that can be handled here - edge1 = ON_SUBD_EDGE_POINTER(ring_vertex1[0]->m_edges[3].m_ptr); - if ( nullptr == edge1 ) - return ON_SUBD_RETURN_ERROR(false); - vertex1 = const_cast(edge1->m_vertex[1]); - if (nullptr == vertex1) - return ON_SUBD_RETURN_ERROR(false); - } + // when the vertex0 has two faces and two edges, the 2nd edge will get the vertex1 created + // the previous time through this loop because the hash table lookup will find it. + vertex1 = m_fsh.FindOrAllocateVertex(neighbor_face0); if (nullptr == vertex1) - { - vertex1 = m_fsh.AllocateVertex(neighbor_face0, subd_type, bUseSavedSubdivisionPoint, 4, 2); - if (nullptr == vertex1) - return ON_SUBD_RETURN_ERROR(false); - } + return ON_SUBD_RETURN_ERROR(false); edge1 = m_fsh.AllocateEdge( ring_vertex1[2], at_crease2_weight, // ingored unless ring_vertex1[0] is tagged as a crease vertex1, ON_SubDSectorType::IgnoredSectorWeight ); - if (nullptr == edge1) + if ( edge1.IsNull()) return ON_SUBD_RETURN_ERROR(false); - } - - if (bFirstVertexHasValence2) - { - // extremely uncommon case that requires this mess ... - ON_SimpleArray neighbors(N); - face_m_edges = face->m_edge4; - for (unsigned int i = 0; i < N; i++, face_m_edges++) - { - if (4 == i) - face_m_edges = face->m_edgex; - const ON__UINT_PTR edge0_ptr = face_m_edges->m_ptr; - edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr); - neighbors.Append(edge0->NeighborFacePtr(face, edge0->IsHardCrease()).Face()); - } - - ON_SimpleArray neighbors_vertex1(N); - face_m_edges = face->m_edge4; - for (unsigned int i = 0; i < N; i++, face_m_edges++) - { - if (4 == i) - face_m_edges = face->m_edgex; - const ON__UINT_PTR edge0_ptr = face_m_edges->m_ptr; - edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr); - if (0 == i) - { - ring_vertex1[0] = ring_vertex1[3]; - ring_vertex1[1] = const_cast(center_vertex1->m_next_vertex); - ring_vertex1[2] = const_cast(ring_vertex1[1]->m_next_vertex); - } - else - { - ring_vertex1[0] = ring_vertex1[2]; - ring_vertex1[1] = const_cast(ring_vertex1[0]->m_next_vertex); - ring_vertex1[2] = const_cast(ring_vertex1[1]->m_next_vertex); - } - - - const ON_SubDFace* neighbor_face0 = edge0->NeighborFacePtr(face, edge0->IsHardCrease()).Face(); - if (nullptr == neighbor_face0) - { - neighbors_vertex1.Append(nullptr); - continue; - } - for (unsigned int j = 0; j < i; j++) - { - if (neighbor_face0 == neighbors[j]) - { - vertex1 = neighbors_vertex1[j]; - break; - } - } - if (nullptr == vertex1) - { - vertex1 = m_fsh.AllocateVertex(neighbor_face0, subd_type, bUseSavedSubdivisionPoint, 4, 2); - if (nullptr == vertex1) - return ON_SUBD_RETURN_ERROR(false); - } - neighbors_vertex1.Append(vertex1); - edge1 = m_fsh.AllocateEdge( - ring_vertex1[2], - at_crease2_weight, // ingored unless ring_vertex1[0] is tagged as a crease - vertex1, - ON_SubDSectorType::IgnoredSectorWeight - ); - if (nullptr == edge1) - return ON_SUBD_RETURN_ERROR(false); - } - } + } ///////////////////////////////////////////////////////////////////////////////// // @@ -3160,14 +2901,13 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( // vertex0 = face->Vertex(i) vertex0 = edge0_duo[1]->m_vertex[ON_SUBD_EDGE_DIRECTION(face_m_edges->m_ptr)]; - //const bool bCornerVertexIsDart = vertex0->IsDart(); - //const bool bCornerVertexIsCreaseOrCorner = bCornerVertexIsDart ? false : vertex0->IsCreaseOrCorner(); + if ( nullptr == vertex0 || vertex0->m_edge_count < 2) + return ON_SUBD_RETURN_ERROR(false); edge0_duo_bIsHardCrease[0] = edge0_duo_bIsHardCrease[1]; edge0_duo_bIsDartCrease[0] = edge0_duo_bIsDartCrease[1]; edge0_duo_bIsHardCrease[1] = edge0_duo[1]->IsHardCrease(); edge0_duo_bIsDartCrease[1] = edge0_duo_bIsHardCrease[1] ? false : edge0_duo[1]->IsDartCrease(); - //const bool bDartCreaseSituation = edge0_duo_bIsDartCrease[0] || edge0_duo_bIsDartCrease[1]; // neighbor0_duo[0] = original face neighbor across edge0_duo[0] // neighbor0_duo[1] = original face neighbor across edge0_duo[1] @@ -3219,6 +2959,13 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( continue; } + if (nullptr == neighbor0_duo[0] && nullptr == neighbor0_duo[1]) + { + // This is an error condition that we can tolerate and still get a partial result. + ON_SubDIncrementErrorCount(); + continue; + } + if (nullptr == neighbor0_duo[0] || nullptr == edge1_quartet[0]) { if (false == edge0_duo_bIsHardCrease[0]) @@ -3239,25 +2986,27 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( } } - if (neighbor0_duo[0] == neighbor0_duo[1]) + if ( neighbor0_duo[0] == neighbor0_duo[1] + && 2 == vertex0->m_edge_count + && 2 == vertex0->m_face_count + ) { - // special case - if ( nullptr == neighbor0_duo[0] || 2 == vertex0->m_edge_count) - { - // This is an error condition that we can tolerate and still get a partial result. - ON_SubDIncrementErrorCount(); - continue; - } - if (nullptr == edge1_quartet[0] || nullptr == edge1_quartet[3] || edge1_quartet[0]->m_vertex[0] != edge1_quartet[3]->m_vertex[0] ) - { - // This is an error condition that we can tolerate and still get a partial result. - ON_SubDIncrementErrorCount(); - continue; - } + // vertex0 is a valence 2 vertex + face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1_quartet[1],1); + face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1_quartet[0],0); + face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1_quartet[3],1); + face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[2],1); + face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs); + if ( nullptr == face1) + return ON_SUBD_RETURN_ERROR(false); continue; } - if (vertex0->m_face_count <= 1 || vertex0->m_edge_count <= 2) + if ( + neighbor0_duo[0] == neighbor0_duo[1] + || vertex0->m_face_count <= 1 + || vertex0->m_edge_count <= 2 + ) { // This is an error condition that we can tolerate and still get a partial result. ON_SubDIncrementErrorCount(); @@ -3268,19 +3017,6 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( // // There is at least one outer subdivision quads around the inner corner subd quad at face->Vertex(i) - if (2 == vertex0->m_edge_count && 2 == vertex0->m_face_count) - { - // valance 2 special case - face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1_quartet[2],1); - face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1_quartet[3],0); - face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1_quartet[0],1); - face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[1],1); - face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs); - if ( nullptr == face1) - return ON_SUBD_RETURN_ERROR(false); - continue; - } - if ( vertex0 != sit.Initialize(face,0,i) ) return ON_SUBD_RETURN_ERROR(false); @@ -3294,7 +3030,6 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( return ON_SUBD_RETURN_ERROR(false); } - const unsigned int vertex_face_count = vertex0->m_face_count; const ON_SubDFace* face0 = nullptr; @@ -3317,19 +3052,19 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( const bool b3FaceCase = (neighbor0_duo[0] == edge0->NeighborFace(face0, false)); - face1_corners[1] = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,3,2); + face1_corners[1] = m_fsh.FindOrAllocateVertex(edge0); face1_corners[2] = const_cast(edge1_quartet[3]->m_vertex[1]); face1_corners[3] = ring_vertex1[2]; - edge1 = m_fsh.AllocateEdge(face1_corners[0], ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), face1_corners[1], at_crease2_weight); - if ( nullptr == edge1 ) + edge1 = m_fsh.FindOrAllocateEdge(face1_corners[0], ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), face1_corners[1], at_crease2_weight); + if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); - face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1, 0); + face1_eptrs[0] = edge1; - edge1 = m_fsh.AllocateEdge(face1_corners[1], at_crease2_weight, face1_corners[2], ON_SubDSectorType::IgnoredSectorWeight); - if ( nullptr == edge1 ) + edge1 = m_fsh.FindOrAllocateEdge(face1_corners[1], at_crease2_weight, face1_corners[2], ON_SubDSectorType::IgnoredSectorWeight); + if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); - face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1, 0); + face1_eptrs[1] = edge1; face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1_quartet[3],1); face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1_quartet[2],1); @@ -3351,9 +3086,9 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( else { edge0 = sit.CurrentEdge(1); - if ( nullptr == edge1 ) + if ( nullptr == edge0 ) return ON_SUBD_RETURN_ERROR(false); - vertex1 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,3,2); + vertex1 = m_fsh.FindOrAllocateVertex(edge0); } if ( nullptr == vertex1 ) return ON_SUBD_RETURN_ERROR(false); @@ -3367,18 +3102,18 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( } else { - edge1 = m_fsh.AllocateEdge(face1_corners[0], ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), face1_corners[3], at_crease2_weight ); - if ( nullptr == edge1 ) + edge1 = m_fsh.FindOrAllocateEdge(face1_corners[0], ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), face1_corners[3], at_crease2_weight ); + if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); - face1_eptrs[3] = ON_SubDEdgePtr::Create(edge1, 1); + face1_eptrs[3] = edge1.Reversed(); } face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1_quartet[1], 1); face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1_quartet[0], 0); - edge1 = m_fsh.AllocateEdge(face1_corners[2], ON_SubDSectorType::IgnoredSectorWeight, face1_corners[3], at_crease2_weight); - if ( nullptr == edge1 ) + edge1 = m_fsh.FindOrAllocateEdge(face1_corners[2], ON_SubDSectorType::IgnoredSectorWeight, face1_corners[3], at_crease2_weight); + if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); - face1_eptrs[2] = ON_SubDEdgePtr::Create(edge1, 0); + face1_eptrs[2] = edge1; face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs); if ( nullptr == face1 ) @@ -3537,18 +3272,18 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( edge0 = sit.CurrentEdge(0); if (nullptr==edge0) return ON_SUBD_RETURN_ERROR(false); - vertex1 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,3,2); + vertex1 = m_fsh.FindOrAllocateVertex(edge0); } break; case 2: - vertex1 = m_fsh.AllocateVertex(face0,subd_type,bUseSavedSubdivisionPoint,3,2); + vertex1 = m_fsh.FindOrAllocateVertex(face0); break; case 3: { edge0 = sit.CurrentEdge(1); if (nullptr == edge0) return ON_SUBD_RETURN_ERROR(false); - vertex1 = m_fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,3,2); + vertex1 = m_fsh.FindOrAllocateVertex(edge0); } break; } @@ -3561,66 +3296,67 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( for (int j = 0; j < 4; j++) { - edge1 = ON_SUBD_EDGE_POINTER(face1_eptrs[j].m_ptr); - if (nullptr == edge1) + if (false == face1_eptrs[j].IsNull()) + continue; + + edge1 = ON_SubDEdgePtr::Null; + switch (j) { - switch (j) + case 0: { - case 0: - { - edge0 = sit.CurrentEdge(0); - if (nullptr == edge0) - return ON_SUBD_RETURN_ERROR(false); - edge1 = m_fsh.AllocateEdge( - face1_corners[0], - ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), - face1_corners[1], - at_crease2_weight - ); - if ( 0 == sit_limit && edge0 == sit_first_edge0 ) - sit_first_eptr1 = ON_SubDEdgePtr::Create(edge1, 0); - } - break; - case 1: - edge1 = m_fsh.AllocateEdge( + edge0 = sit.CurrentEdge(0); + if (nullptr == edge0) + return ON_SUBD_RETURN_ERROR(false); + edge1 = m_fsh.FindOrAllocateEdge( + face1_corners[0], + ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), face1_corners[1], - at_crease2_weight, - face1_corners[2], - ON_SubDSectorType::IgnoredSectorWeight - ); - break; - case 2: - edge1 = m_fsh.AllocateEdge( - face1_corners[2], - ON_SubDSectorType::IgnoredSectorWeight, - face1_corners[3], at_crease2_weight ); - break; - case 3: - { - edge0 = sit.CurrentEdge(1); - if (nullptr == edge0) - return ON_SUBD_RETURN_ERROR(false); - edge1 = m_fsh.AllocateEdge( - face1_corners[3], - at_crease2_weight, - face1_corners[0], - ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0) - ); - } - break; + if ( 0 == sit_limit && edge0 == sit_first_edge0 ) + sit_first_eptr1 = edge1; } - if ( nullptr == edge1 ) - return ON_SUBD_RETURN_ERROR(false); - face1_eptrs[j] = ON_SubDEdgePtr::Create(edge1, 0); + break; + case 1: + edge1 = m_fsh.FindOrAllocateEdge( + face1_corners[1], + at_crease2_weight, + face1_corners[2], + ON_SubDSectorType::IgnoredSectorWeight + ); + break; + case 2: + edge1 = m_fsh.FindOrAllocateEdge( + face1_corners[2], + ON_SubDSectorType::IgnoredSectorWeight, + face1_corners[3], + at_crease2_weight + ); + break; + case 3: + { + edge0 = sit.CurrentEdge(1); + if (nullptr == edge0) + return ON_SUBD_RETURN_ERROR(false); + edge1 = m_fsh.FindOrAllocateEdge( + face1_corners[3], + at_crease2_weight, + face1_corners[0], + ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0) + ); + } + break; } + if ( edge1.IsNull() ) + return ON_SUBD_RETURN_ERROR(false); + face1_eptrs[j] = edge1; } face1 = m_fsh.AllocateQuad(zero_face_id, parent_face_id, face1_eptrs); if ( nullptr == face1 ) return ON_SUBD_RETURN_ERROR(false); } + for(;;) { if (bFinishedCorner) @@ -3635,7 +3371,6 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( } m_center_vertex1 = center_vertex1; - m_subd_type = subd_type; m_face0 = face; m_face1 = m_center_vertex1->m_faces; m_face1_count = m_center_vertex1->m_face_count; @@ -3643,4 +3378,92 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( return true; } -#endif +unsigned int ON_SubDVertexSurfacePointCoefficient::SurfacePointVertexId() const +{ + return (nullptr != m_limit_point_vertex) ? m_limit_point_vertex->m_id : 0U; +} + +unsigned int ON_SubDVertexSurfacePointCoefficient::RingVertexId() const +{ + return (nullptr != m_ring_vertex) ? m_ring_vertex->m_id : 0U; +} + +double ON_SubDVertexSurfacePointCoefficient::Coefficient() const +{ + return m_c; +} + +//#if 0 +//static void DebugAt(const ON_SubDVertexSurfacePointCoefficient& c) +//{ +// if (856 == c.LimitPointVertexId() && 857==c.RingVertexId()) +// ON_TextLog::Null.Print("breakpoint here"); +//} +//#endif +const ON_SubDVertexSurfacePointCoefficient ON_SubDVertexSurfacePointCoefficient::Create( + const ON_SubDVertex* limit_point_vertex, + const ON_SubDVertex* ring_vertex, + double x +) +{ + ON_SubDVertexSurfacePointCoefficient c; + c.m_limit_point_vertex = limit_point_vertex; + c.m_ring_vertex = ring_vertex; + c.m_c = x; + //DebugAt(c) + return c; +} + +int ON_SubDVertexSurfacePointCoefficient::CompareSurfacePointVertexId( + const ON_SubDVertexSurfacePointCoefficient* lhs, + const ON_SubDVertexSurfacePointCoefficient* rhs +) +{ + const ON_SubDVertex* lhs_v = (nullptr == lhs) ? nullptr : lhs->m_limit_point_vertex; + const ON_SubDVertex* rhs_v = (nullptr == rhs) ? nullptr : rhs->m_limit_point_vertex; + return ON_SubDComponentBase::CompareId(lhs_v, rhs_v); +} + +int ON_SubDVertexSurfacePointCoefficient::CompareRingVertexId( + const ON_SubDVertexSurfacePointCoefficient* lhs, + const ON_SubDVertexSurfacePointCoefficient* rhs +) +{ + const ON_SubDVertex* lhs_v = (nullptr == lhs) ? nullptr : lhs->m_ring_vertex; + const ON_SubDVertex* rhs_v = (nullptr == rhs) ? nullptr : rhs->m_ring_vertex; + return ON_SubDComponentBase::CompareId(lhs_v, rhs_v); +} + +int ON_SubDVertexSurfacePointCoefficient::CompareSurfacePointAndRingVertexId( + const ON_SubDVertexSurfacePointCoefficient* lhs, + const ON_SubDVertexSurfacePointCoefficient* rhs +) +{ + const ON_SubDVertex* lhs_v = (nullptr == lhs) ? nullptr : lhs->m_limit_point_vertex; + const ON_SubDVertex* rhs_v = (nullptr == rhs) ? nullptr : rhs->m_limit_point_vertex; + int rc = ON_SubDComponentBase::CompareId(lhs_v, rhs_v); + if (0 == rc) + { + lhs_v = (nullptr == lhs) ? nullptr : lhs->m_ring_vertex; + rhs_v = (nullptr == rhs) ? nullptr : rhs->m_ring_vertex; + rc = ON_SubDComponentBase::CompareId(lhs_v, rhs_v); + } + return rc; +} + +int ON_SubDVertexSurfacePointCoefficient::CompareRingAndSurfacePointVertexId( + const ON_SubDVertexSurfacePointCoefficient* lhs, + const ON_SubDVertexSurfacePointCoefficient* rhs +) +{ + const ON_SubDVertex* lhs_v = (nullptr == lhs) ? nullptr : lhs->m_ring_vertex; + const ON_SubDVertex* rhs_v = (nullptr == rhs) ? nullptr : rhs->m_ring_vertex; + int rc = ON_SubDComponentBase::CompareId(lhs_v, rhs_v); + if (0 == rc) + { + lhs_v = (nullptr == lhs) ? nullptr : lhs->m_limit_point_vertex; + rhs_v = (nullptr == rhs) ? nullptr : rhs->m_limit_point_vertex; + rc = ON_SubDComponentBase::CompareId(lhs_v, rhs_v); + } + return rc; +} diff --git a/opennurbs_subd_matrix.cpp b/opennurbs_subd_matrix.cpp index a4140132..56bad4df 100644 --- a/opennurbs_subd_matrix.cpp +++ b/opennurbs_subd_matrix.cpp @@ -26,8 +26,6 @@ //////////////////////////////////////////////////////////////// */ -#if defined(OPENNURBS_SUBD_WIP) - ///////////////////////////////////////////////////////// // // Built-in quad subdivision @@ -542,8 +540,8 @@ static unsigned int GetQuadCreaseEigenvalues( } unsigned int ON_SubDSectorType::GetAllEigenvalues( - size_t eigenvalues_capacity, - double* eigenvalues + double* eigenvalues, + size_t eigenvalues_capacity ) { if ( 0 == eigenvalues_capacity) @@ -564,7 +562,6 @@ unsigned int ON_SubDSectorType::GetAllEigenvalues( if ( 0 == R || (nullptr != eigenvalues && eigenvalues_capacity < R)) return ON_SUBD_RETURN_ERROR(0); - const ON_SubD::SubDType subdivision_type = SubDType(); const ON_SubD::VertexTag vertex_tag = VertexTag(); const unsigned int N = EdgeCount(); @@ -573,56 +570,50 @@ unsigned int ON_SubDSectorType::GetAllEigenvalues( if (ON_SubD::VertexTag::Smooth == vertex_tag) { - if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) + if (nullptr == eigenvalues) + { + // caller is asking if eigenvalues are available + return R; + } + eigenvalues[0] = 1.0; + + double a = (3 * N - 7); + double b = (N >= 6) ? ((5 * N - 30)*N + 49) : ((5 * N*N + 49) - 30 * N); + double c = sqrt(b); + double d = 0.125; + unsigned int D = N; + while (D > 0 && 0 == (D % 2)) + { + D /= 2; + d *= 0.5; + } + d /= ((double)D); + + eigenvalues[1] = d*(a + c); + eigenvalues[2] = d*(a - c); + + for (unsigned int k = 1; k < N; k++) + { + double e2[2]; + GetQuadSubdivisionMatrix_eigenvalue_pair(k, N, e2); + eigenvalues[2 * k + 1] = e2[0]; + eigenvalues[2 * k + 2] = e2[1]; + } + + // return sorted in decreasing order + ON_SortDoubleArrayDecreasing(eigenvalues + 1, R - 1); + } + else if (ON_SubD::VertexTag::Crease == vertex_tag) + { + if (N <= 20) { if (nullptr == eigenvalues) { // caller is asking if eigenvalues are available return R; } - eigenvalues[0] = 1.0; - - double a = (3 * N - 7); - double b = (N >= 6) ? ((5 * N - 30)*N + 49) : ((5 * N*N + 49) - 30 * N); - double c = sqrt(b); - double d = 0.125; - unsigned int D = N; - while (D > 0 && 0 == (D % 2)) - { - D /= 2; - d *= 0.5; - } - d /= ((double)D); - - eigenvalues[1] = d*(a + c); - eigenvalues[2] = d*(a - c); - - for (unsigned int k = 1; k < N; k++) - { - double e2[2]; - GetQuadSubdivisionMatrix_eigenvalue_pair(k, N, e2); - eigenvalues[2 * k + 1] = e2[0]; - eigenvalues[2 * k + 2] = e2[1]; - } - - // return sorted in decreasing order - ON_SortDoubleArrayDecreasing(eigenvalues + 1, R - 1); - } - } - else if (ON_SubD::VertexTag::Crease == vertex_tag) - { - if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type) - { - if (N <= 20) - { - if (nullptr == eigenvalues) - { - // caller is asking if eigenvalues are available - return R; - } - if (R != GetQuadCreaseEigenvalues(N, R, eigenvalues) || !(1.0 == eigenvalues[0])) - return ON_SUBD_RETURN_ERROR(0); - } + if (R != GetQuadCreaseEigenvalues(N, R, eigenvalues) || !(1.0 == eigenvalues[0])) + return ON_SUBD_RETURN_ERROR(0); } } @@ -757,8 +748,8 @@ bool ON_SubDMatrix::EvaluateCosAndSin( unsigned int ON_SubDSectorType::GetSubdivisionMatrix( - size_t S_capacity, - double** S + double** S, + size_t S_capacity ) const { if ( S_capacity < 3 || nullptr == S ) @@ -780,16 +771,16 @@ unsigned int ON_SubDSectorType::GetSubdivisionMatrix( return ON_SUBD_RETURN_ERROR(0); } - const ON_SubD::SubDType subd_type = SubDType(); - //const ON_SubD::VertexTag vertex_tag = VertexTag(); const unsigned int N = EdgeCount(); - if (ON_SubD::SubDType::QuadCatmullClark != subd_type) - return ON_SUBD_RETURN_ERROR(0); // todo -- add tri support if (this->IsSmoothSector() || this->IsDartSector()) { - if (N < 3 || R < 7) + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth and Dart + // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() + // for more details on how this case is handled. + // Valence 2 has R = 5, N = 5 + if (N < 2 || R < 5) return ON_SUBD_RETURN_ERROR(0); /* @@ -817,69 +808,153 @@ unsigned int ON_SubDSectorType::GetSubdivisionMatrix( double* y; double* x1; - // first row - x = S[0]; - *x++ = a; - x1 = x + R; - while (x < x1) + if (5 == R) { - *x++ = b; - *x++ = c; + if (this->IsDartSector()) + { + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart + // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() + // for more details on how this case is handled. + // + // Dart Valence 2 subdivision matrix special case. + S[0][0] = 1.0 / 8.0; + S[0][1] = 3.0 / 8.0; + S[0][2] = 1.0 / 16.0; + S[0][3] = 3.0 / 8.0; + S[0][4] = 1.0 / 16.0; + + // creased edge subdivision + S[1][0] = 1.0 / 2.0; + S[1][1] = 1.0 / 2.0; + S[1][2] = 0.0; + S[1][3] = 0.0; + S[1][4] = 0.0; + + S[2][0] = 1.0 / 4.0; + S[2][1] = 1.0 / 4.0; + S[2][2] = 1.0 / 4.0; + S[2][3] = 1.0 / 4.0; + S[2][4] = 0.0; + + // smooth edge subdivision + // k = 2, theta = 2pi/2 = pi, cos(theta) = cos(pi) = -1 + S[3][0] = 1.0 / 8.0; // = 3/4 + cos(theta)/4 + S[3][1] = 1.0 / 8.0; // deviates from general case because P0 is a corner of both quads attached to edge(C,P1) + S[3][2] = 1.0 / 16.0; + S[3][3] = 5.0 / 8.0; // = 3/4 - cos(theta)/4 + S[3][4] = 1.0 / 16.0; + + S[4][0] = 1.0 / 4.0; + S[4][1] = 1.0 / 4.0; + S[4][2] = 0.0; + S[4][3] = 1.0 / 4.0; + S[4][4] = 1.0 / 4.0; + } + else + { + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth + // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() + // for more details on how this case is handled. + // + // Smooth Valence 2 subdivision matrix special case. + S[0][0] = 1.0 / 8.0; + S[0][1] = 3.0 / 8.0; + S[0][2] = 1.0 / 16.0; + S[0][3] = 3.0 / 8.0; + S[0][4] = 1.0 / 16.0; + + S[1][0] = 3.0 / 8.0; + S[1][1] = 3.0 / 8.0; + S[1][2] = 1.0 / 16.0; + S[1][3] = 1.0 / 8.0; + S[1][4] = 1.0 / 16.0; + + S[2][0] = 1.0 / 4.0; + S[2][1] = 1.0 / 4.0; + S[2][2] = 1.0 / 4.0; + S[2][3] = 1.0 / 4.0; + S[2][4] = 0.0; + + S[3][0] = 3.0 / 8.0; + S[3][1] = 1.0 / 8.0; + S[3][2] = 1.0 / 16.0; + S[3][3] = 3.0 / 8.0; + S[3][4] = 1.0 / 16.0; + + S[4][0] = 1.0 / 4.0; + S[4][1] = 1.0 / 4.0; + S[4][2] = 0.0; + S[4][3] = 1.0 / 4.0; + S[4][4] = 1.0 / 4.0; + } } - - // second row - x = S[1]; - x1 = x + R - 2; - *x++ = d_center; - const double* E = x; - *x++ = d_ring; - *x++ = e; - *x++ = e; - while (x < x1) - *x++ = 0.0; - *x++ = e; - *x++ = e; - - // third row - x = S[2]; - x1 = x + R; - *x++ = f; - const double* F = x; - *x++ = f; - *x++ = f; - *x++ = f; - while (x < x1) - *x++ = 0.0; - - unsigned int k = 0; - const unsigned int kmax = R - 1; - - for (unsigned int i = 3; i < R; i += 2) + else { - x = S[i]; - y = S[i + 1]; - k = (k + (kmax - 2)) % kmax; - *x++ = d_center; - *y++ = f; - x1 = x + kmax; + // first row + x = S[0]; + *x++ = a; + x1 = x + R; while (x < x1) { - *x++ = E[k]; - *y++ = F[k]; - k = (k + 1) % kmax; + *x++ = b; + *x++ = c; + } + + // second row + x = S[1]; + x1 = x + R - 2; + *x++ = d_center; + const double* E = x; + *x++ = d_ring; + *x++ = e; + *x++ = e; + while (x < x1) + *x++ = 0.0; + *x++ = e; + *x++ = e; + + // third row + x = S[2]; + x1 = x + R; + *x++ = f; + const double* F = x; + *x++ = f; + *x++ = f; + *x++ = f; + while (x < x1) + *x++ = 0.0; + + unsigned int k = 0; + const unsigned int kmax = R - 1; + + for (unsigned int i = 3; i < R; i += 2) + { + x = S[i]; + y = S[i + 1]; + k = (k + (kmax - 2)) % kmax; + *x++ = d_center; + *y++ = f; + x1 = x + kmax; + while (x < x1) + { + *x++ = E[k]; + *y++ = F[k]; + k = (k + 1) % kmax; + } + } + + if (this->IsDartSector()) + { + // fix creased edge row + x = S[1]; + x1 = x + R; + *x++ = 0.5; // center vertex + *x++ = 0.5; // crease edge vertex + while (x < x1) + *x++ = 0.0; } } - if (this->IsDartSector()) - { - // fix creased edge row - x = S[1]; - x1 = x + R; - *x++ = 0.5; // center vertex - *x++ = 0.5; // crease edge vertex - while (x < x1) - *x++ = 0.0; - } return R; } @@ -1007,8 +1082,8 @@ unsigned int ON_SubDSectorType::GetSubdivisionMatrix( } unsigned int ON_SubDSectorType::GetSubdivisionMatrix( - size_t S_capacity, - double* S + double* S, + size_t S_capacity ) const { if ( S_capacity < 9 || nullptr == S ) @@ -1030,7 +1105,7 @@ unsigned int ON_SubDSectorType::GetSubdivisionMatrix( Srows[i] = Srows[i-1] + R; } - const unsigned int rc = GetSubdivisionMatrix(R,Srows); + const unsigned int rc = GetSubdivisionMatrix(Srows, R); delete[] Srows; @@ -1048,36 +1123,54 @@ double ON_SubDSectorType::SubdominantEigenvalue() const if (F < 1) return ON_SUBD_RETURN_ERROR(rc_error); - if (ON_SubD::SubDType::QuadCatmullClark == SubDType()) + switch (VertexTag()) { - switch (VertexTag()) + case ON_SubD::VertexTag::Unset: + break; + + case ON_SubD::VertexTag::Smooth: + if (1 == (R % 2)) { - case ON_SubD::VertexTag::Unset: - break; + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth + // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() + // for more details on how this case is handled. + // + // R = 5 is the valence 2 case. + // In this case the evaluation matrix is 5x5 and the eigenvalues are + // In this case the evaluation matrix is 5x5 and the eigenvalues are + // 1, 1/4, 1/4, -1/4, 1/8. + // The multiplicity of 1/4 is two, but -1/4 is also a subdominant eigenvalue. + // This function returns 1/4 when R = 5 and tag = Smooth. - case ON_SubD::VertexTag::Smooth: - if (1 == (R % 2)) - { - double cos_2pi_over_F, cos_pi_over_F, lambda; - ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos_2pi_over_F, &lambda); - ON_SubDMatrix::EvaluateCosAndSin(1, F, &cos_pi_over_F, &lambda); - lambda = (5.0 + cos_2pi_over_F + 3.0*cos_pi_over_F*sqrt(2.0*(1.0 + cos_2pi_over_F/9.0)))/16.0; - return lambda; - } - break; - - case ON_SubD::VertexTag::Dart: - if (1 == (R % 2)) - return 0.5; - break; - - - case ON_SubD::VertexTag::Corner: - case ON_SubD::VertexTag::Crease: - if (0 == (R % 2)) - return 0.5; - break; + double cos_2pi_over_F, cos_pi_over_F, lambda; + ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos_2pi_over_F, &lambda); + ON_SubDMatrix::EvaluateCosAndSin(1, F, &cos_pi_over_F, &lambda); + lambda = (5.0 + cos_2pi_over_F + 3.0*cos_pi_over_F*sqrt(2.0*(1.0 + cos_2pi_over_F/9.0)))/16.0; + return lambda; } + break; + + case ON_SubD::VertexTag::Dart: + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart + // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() + // for more details on how this case is handled. + // + // R = 5 is the valence 2 case. + // In this case the evaluation matrix is 5x5 and the eigenvalues are + // 1, 1/2, 1/4, sqrt(2)/8, -sqrt(2)/8. + // All eigenvalues have multiplicity 1, including the subdominant 1/2. + // This function returns 1/2 when R = 5 and tag = Dart. + + if (1 == (R % 2)) + return 0.5; + break; + + + case ON_SubD::VertexTag::Corner: + case ON_SubD::VertexTag::Crease: + if (0 == (R % 2)) + return 0.5; + break; } return ON_SUBD_RETURN_ERROR(rc_error); @@ -1089,22 +1182,69 @@ unsigned int ON_SubDSectorType::SubdominantEigenvalueMulitiplicity() const if (false == IsValid() ) return 0; - if (ON_SubD::SubDType::QuadCatmullClark == m_subd_type) + // Catmull-Clark quad subdivision special cases + if (ON_SubD::VertexTag::Crease == m_vertex_tag) { - // Catmull-Clark quad subdivision special case - if (ON_SubD::VertexTag::Crease == m_vertex_tag && 1 == m_sector_face_count) - return 1; + if (0 == m_sector_face_count) + { + // "wire" edges (no faces) + // In this case the evaluation matrix is 3x3 and the eigenvalues are + // 1, 1/2, 1/4. + // All eigenvalues have multiplicity 1, including the subdominant 1/2. + return 1; // for the 1/2 + } + if (1 == m_sector_face_count) + { + // single face + // In this case the evaluation matrix is 4x4 and the eigenvalues are + // 1, 1/2, 1/4, 1/4. + // The Jordan block for 1/4, 1/4 had a 1 on the super diagonal. + return 1; // for the 1/2 + } + } + else if (2 == m_sector_face_count) + { + // valance 2 special cases + if (ON_SubD::VertexTag::Smooth == m_vertex_tag) + { + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth + // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() + // for more details on how this case is handled. + // + // In this case the evaluation matrix is 5x5 and the eigenvalues are + // 1, 1/4, 1/4, -1/4, 1/8. + // The multiplicity of 1/4 is two, but -1/4 is also a subdominant eigenvalue. + // For valence >= 3 the Smooth eigenvalues are 1, 1/4, 1/4, smaller values ... + return 2; // for the 1/4, 1/4 pair + } + + if (ON_SubD::VertexTag::Dart == m_vertex_tag) + { + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart + // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients() + // for more details on how this case is handled. + // + // In this case the evaluation matrix is 5x5 and the eigenvalues are + // 1, 1/2, 1/4, sqrt(2)/8, -sqrt(2)/8. + // All eigenvalues have multiplicity 1, including the subdominant 1/2. + return 1; // for the 1/2 + } + } + // For a Corner vertex with valence = 0 (wire edges), the matrix is 3x3 and the eigenvalues are 1, 1/2, 1/2. + // For a Corner vertex with valence = 1 (single face), the matrix is 4x4 and the eigenvalues are 1, 1/2, 1/2, 1/4. + // For a Smooth vertex with valence >= 3, the eigenvalues are 1, 1/4, 1/4, smaller values ... + // For a Dart vertex with valence >= 3, the eigenvalues are 1, 1/2, 1/2, smaller values ... return 2; } double ON_SubDSectorType::GetSubdominantEigenvectors( - size_t E1_capacity, - double* E1, - size_t E2_capacity, - double* E2 - ) const + double* E1, + size_t E1_capacity, + double* E2, + size_t E2_capacity +) const { const double rc_error = ON_UNSET_VALUE; const double lambda = SubdominantEigenvalue(); @@ -1144,100 +1284,150 @@ double ON_SubDSectorType::GetSubdominantEigenvectors( double y, cos0, cos1, sin0, sin1; - if (ON_SubD::SubDType::QuadCatmullClark == SubDType()) + switch (VertexTag()) { - switch (VertexTag()) + case ON_SubD::VertexTag::Unset: + break; + + case ON_SubD::VertexTag::Smooth: + if (1 == (R % 2)) { - case ON_SubD::VertexTag::Unset: - break; - - case ON_SubD::VertexTag::Smooth: - if (1 == (R % 2)) + if (nullptr != E1) { - if (nullptr != E1) + // cos0 = cos(2pi * 0/F) + // sin0 = sin(2pi * 0/F) + // cos1 = cos(2pi * 1/F) + // sin1 = sin(2pi * 1/F) + cos0 = 1.0; + sin0 = 0.0; + ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos1, &sin1); + //y = 0.5*(3.0 * sqrt((1.0 + cos1 / 9.0) / (1.0 + cos1)) - 1.0); + y = 2.0*(4.0*lambda - 1.0) / (1.0 + cos1) - 1; + E1[0] = 0.0; + E2[0] = 0.0; + E1[1] = sin0; + E2[1] = cos0; + unsigned int i = 2; + for (;;) { - // cos0 = cos(2pi * 0/F) - // sin0 = sin(2pi * 0/F) - // cos1 = cos(2pi * 1/F) - // sin1 = sin(2pi * 1/F) - cos0 = 1.0; - sin0 = 0.0; - ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos1, &sin1); - //y = 0.5*(3.0 * sqrt((1.0 + cos1 / 9.0) / (1.0 + cos1)) - 1.0); - y = 2.0*(4.0*lambda - 1.0) / (1.0 + cos1) - 1; - E1[0] = 0.0; - E2[0] = 0.0; - E1[1] = sin0; - E2[1] = cos0; - unsigned int i = 2; - for (;;) - { - E1[i] = y*(sin0 + sin1); - E2[i] = y*(cos0 + cos1); - i++; - if (i==R) - break; - E1[i] = sin1; - E2[i] = cos1; - i++; - cos0 = cos1; - sin0 = sin1; - ON_SubDMatrix::EvaluateCosAndSin(i, F, &cos1, &sin1); - // E1[] and E2[] values are symmetric and we could stop halfway and copy - // current loop debugged and tested Feb 10, 2015 - } + E1[i] = y*(sin0 + sin1); + E2[i] = y*(cos0 + cos1); + i++; + if (i==R) + break; + E1[i] = sin1; + E2[i] = cos1; + i++; + cos0 = cos1; + sin0 = sin1; + ON_SubDMatrix::EvaluateCosAndSin(i, F, &cos1, &sin1); + // E1[] and E2[] values are symmetric and we could stop halfway and copy + // current loop debugged and tested Feb 10, 2015 } - return lambda; } - break; + return lambda; + } + break; - case ON_SubD::VertexTag::Dart: - if (1 == (R % 2)) + case ON_SubD::VertexTag::Dart: + if (1 == (R % 2)) + { + if (nullptr != E1) { - if (nullptr != E1) + // cos0 = cos(2pi * 0/F) + // sin0 = sin(2pi * 0/F) + // cos1 = cos(2pi * 1/F) + // sin1 = sin(2pi * 1/F) + cos0 = 1.0; + sin0 = 0.0; + ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos1, &sin1); + E1[0] = 0.0; + E2[0] = 0.0; + E1[1] = sin0; + E2[1] = cos0; + unsigned int i = 2; + for (;;) { - // cos0 = cos(2pi * 0/F) - // sin0 = sin(2pi * 0/F) - // cos1 = cos(2pi * 1/F) - // sin1 = sin(2pi * 1/F) - cos0 = 1.0; - sin0 = 0.0; - ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos1, &sin1); - E1[0] = 0.0; - E2[0] = 0.0; - E1[1] = sin0; - E2[1] = cos0; - unsigned int i = 2; - for (;;) - { - E1[i] = sin0 + sin1; - E2[i] = cos0 + cos1; - i++; - if (i==R) - break; - E1[i] = sin1; - E2[i] = cos1; - i++; - cos0 = cos1; - sin0 = sin1; - ON_SubDMatrix::EvaluateCosAndSin(i, F, &cos1, &sin1); - // E1[] and E2[] values are symmetric and we could stop halfway and copy - // current loop debugged and tested Feb 10, 2015 - } + E1[i] = sin0 + sin1; + E2[i] = cos0 + cos1; + i++; + if (i==R) + break; + E1[i] = sin1; + E2[i] = cos1; + i++; + cos0 = cos1; + sin0 = sin1; + ON_SubDMatrix::EvaluateCosAndSin(i, F, &cos1, &sin1); + // E1[] and E2[] values are symmetric and we could stop halfway and copy + // current loop debugged and tested Feb 10, 2015 } - return lambda; } - break; + return lambda; + } + break; - case ON_SubD::VertexTag::Corner: - if (0 == (R % 2)) + case ON_SubD::VertexTag::Corner: + if (0 == (R % 2)) + { + const unsigned int sector_angle_index = CornerSectorAngleIndex(); + const unsigned int M = ON_SubDSectorType::MaximumCornerAngleIndex; + //const unsigned int I = ((2*sector_angle_index <= M) ? sector_angle_index : (M-sector_angle_index)); + const unsigned int I = 2*((2*sector_angle_index <= M) ? sector_angle_index : (M-sector_angle_index)); + // F faces, F-1 interior smooth edges and 2 boundary edges + // theta = (i/M*2pi)/F + cos1 = 1.0; // = cos(0) + sin1 = 0.0; // = sin(0) + E1[0] = 0.0; + E2[0] = 0.0; + unsigned int i = 1; + for (;;) + { + cos0 = cos1; + sin0 = sin1; + ON_SubDMatrix::EvaluateCosAndSin((i/2 + 1)*I,F*M,&cos1,&sin1); + E1[i] = sin0; + E2[i] = cos0; + i++; + E1[i] = sin0+sin1; + E2[i] = cos0+cos1; + i++; + if (R - 1 == i) + { + E1[i] = sin1; // = sin(pi) + E2[i] = cos1; // = cos(pi) + break; + } + } + return lambda; + } + break; + + case ON_SubD::VertexTag::Crease: + if (0 == (R % 2)) + { + if (1 == F) + { + // one face and 2 boundary edges + // E1 = eigenvector with eigenvalue = 1/2 + E1[0] = 0.0; // center point coefficients + E1[1] = 1.0; // initial boundary edge point coefficient + E1[2] = 0.0; // face point coefficient + E1[3] = -1.0; // final boundary edge point coefficient + + + // (0,0,1,0) = eigenvector with eigenvalue = 1/4. + // E2 is not an eigenvector + // S * E2 = (1/4, 1/4, 2/5, 1/4) + E2[0] = 1.0; // center point coefficients + E2[1] = -2.0; // boundary edge point coefficient + E2[2] = -5.0; // face point coefficient + E2[3] = -2.0; // boundary edge point coefficient + } + else { - const unsigned int sector_angle_index = CornerSectorAngleIndex(); - const unsigned int M = ON_SubDSectorType::MaximumAngleIndex; - //const unsigned int I = ((2*sector_angle_index <= M) ? sector_angle_index : (M-sector_angle_index)); - const unsigned int I = 2*((2*sector_angle_index <= M) ? sector_angle_index : (M-sector_angle_index)); // F faces, F-1 interior smooth edges and 2 boundary edges - // theta = (i/M*2pi)/F + // theta = pi/F cos1 = 1.0; // = cos(0) sin1 = 0.0; // = sin(0) E1[0] = 0.0; @@ -1247,7 +1437,7 @@ double ON_SubDSectorType::GetSubdominantEigenvectors( { cos0 = cos1; sin0 = sin1; - ON_SubDMatrix::EvaluateCosAndSin((i/2 + 1)*I,F*M,&cos1,&sin1); + ON_SubDMatrix::EvaluateCosAndSin(i/2 + 1,F,&cos1,&sin1); E1[i] = sin0; E2[i] = cos0; i++; @@ -1261,64 +1451,11 @@ double ON_SubDSectorType::GetSubdominantEigenvectors( break; } } - return lambda; } - break; - case ON_SubD::VertexTag::Crease: - if (0 == (R % 2)) - { - if (1 == F) - { - // one face and 2 boundary edges - // E1 = eigenvector with eigenvalue = 1/2 - E1[0] = 0.0; // center point coefficients - E1[1] = 1.0; // initial boundary edge point coefficient - E1[2] = 0.0; // face point coefficient - E1[3] = -1.0; // final boundary edge point coefficient - - - // (0,0,1,0) = eigenvector with eigenvalue = 1/4. - // E2 is not an eigenvector - // S * E2 = (1/4, 1/4, 2/5, 1/4) - E2[0] = 1.0; // center point coefficients - E2[1] = -2.0; // boundary edge point coefficient - E2[2] = -5.0; // face point coefficient - E2[3] = -2.0; // boundary edge point coefficient - } - else - { - // F faces, F-1 interior smooth edges and 2 boundary edges - // theta = pi/F - cos1 = 1.0; // = cos(0) - sin1 = 0.0; // = sin(0) - E1[0] = 0.0; - E2[0] = 0.0; - unsigned int i = 1; - for (;;) - { - cos0 = cos1; - sin0 = sin1; - ON_SubDMatrix::EvaluateCosAndSin(i/2 + 1,F,&cos1,&sin1); - E1[i] = sin0; - E2[i] = cos0; - i++; - E1[i] = sin0+sin1; - E2[i] = cos0+cos1; - i++; - if (R - 1 == i) - { - E1[i] = sin1; // = sin(pi) - E2[i] = cos1; // = cos(pi) - break; - } - } - } - - return lambda; - } - break; + return lambda; } + break; } return ON_SUBD_RETURN_ERROR(rc_error); @@ -1352,7 +1489,7 @@ static double ON_SubDSectorType_LimitSurfaceNormalSign( return z; } -double ON_SubDSectorType::LimitSurfaceNormalSign() const +double ON_SubDSectorType::SurfaceNormalSign() const { const double rc_error = ON_UNSET_VALUE; if (false == IsValid()) @@ -1368,7 +1505,7 @@ double ON_SubDSectorType::LimitSurfaceNormalSign() const double* L1 = LP + R; double* L2 = L1 + R; - if (R != GetLimitSurfaceEvaluationCoefficients(R,LP,R,L1,R,L2)) + if (R != GetSurfaceEvaluationCoefficients(LP, R, L1, R, L2, R)) return ON_SUBD_RETURN_ERROR(rc_error); double sector_angle; @@ -1394,7 +1531,7 @@ double ON_SubDSectorType::LimitSurfaceNormalSign() const return z; } -bool ON_SubDSectorType::LimitEvaluationCoefficientsAvailable() const +bool ON_SubDSectorType::SurfaceEvaluationCoefficientsAvailable() const { if (IsValid()) { @@ -1411,13 +1548,13 @@ bool ON_SubDSectorType::LimitEvaluationCoefficientsAvailable() const -unsigned int ON_SubDSectorType::GetLimitSurfaceEvaluationCoefficients( - size_t LP_capacity, +unsigned int ON_SubDSectorType::GetSurfaceEvaluationCoefficients( double* LP, - size_t L1_capacity, + size_t LP_capacity, double* L1, - size_t L2_capacity, - double* L2 + size_t L1_capacity, + double* L2, + size_t L2_capacity ) const { const double lambda = SubdominantEigenvalue(); @@ -1467,34 +1604,61 @@ unsigned int ON_SubDSectorType::GetLimitSurfaceEvaluationCoefficients( double p, q, y, z, cos0, cos1, sin0, sin1; bool b180degreeCorner = false; - if (ON_SubD::SubDType::QuadCatmullClark == SubDType()) + switch (VertexTag()) { - switch (VertexTag()) + case ON_SubD::VertexTag::Unset: + break; + + case ON_SubD::VertexTag::Smooth: + if (R >= 5 && 1 == (R % 2)) { - case ON_SubD::VertexTag::Unset: - break; - - case ON_SubD::VertexTag::Smooth: - if (1 == (R % 2)) + if (nullptr != LP) { - if (nullptr != LP) + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth + // + // General case code works for R = 5 case: LP[5] = {2/7, 2/7, 1/14, 2/7, 1/14}; + LP[0] = dF / (5.0 + dF); // center point coefficient + y = dF * (dF + 5.0); + p = 4.0 / y; // edge point coefficient + q = 1.0 / y; // face point coefficient + for (unsigned int i = 1; i < R; i++) { - //if ( R == GetQuadSmoothLevFromCache(F,R,LP,R,L1,R,L2) ) - // return R; - // Lpev[] - LP[0] = dF / (5.0 + dF); // center point coefficient - y = dF*(dF + 5.0); - p = 4.0 / y; // edge point coefficient - q = 1.0 / y; // face point coefficient - for (unsigned int i = 1; i < R; i++) - { - LP[i] = p; - i++; - LP[i] = q; - } + LP[i] = p; + i++; + LP[i] = q; } + } - if (nullptr != L1) + if (nullptr != L1) + { + if (5 == R) + { + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth + // + // Evaluation matrix has eigenvalues 1, 1/4, 1/4, -1/4, 1/8, + // so we have a well defined limit point and 3 subdominant eigenvalues. + // The 1 eigenspace is 1 dimensional and spanned by (1,1,1,1,1). + // The 1/4 eigenspace is 2 dimensional and spanned by (0,1,0,-1,0) and (0,0,1,0,-1). + // The -1/4 eigenspace is 1 dimensional and spanned by (2,-1,0,-1,0). + // The 1/8 is 1 dimensional and spanned by (1,1,-6,1,-6). + // The general case code below will divide by zero when y is evaluated because cos1 = -1. + // Turns out there is 1 well defined tangent vector (Q1-Q0) (quad points) + + // This is a "pseudo" limit tangent + L1[0] = 0.0; + L1[1] = 1.0; + L1[2] = 0.0; + L1[3] = -1.0; + L1[4] = 0.0; + + // This is a bonafide limit tangent + L2[0] = 0.0; + L2[1] = 0.0; + L2[2] = 1.0; + L2[3] = 0.0; + L2[4] = -1.0; + } + else { // cos0 = cos(2pi * 0/F) // sin0 = sin(2pi * 0/F) @@ -1503,7 +1667,7 @@ unsigned int ON_SubDSectorType::GetLimitSurfaceEvaluationCoefficients( cos0 = 1.0; sin0 = 0.0; ON_SubDMatrix::EvaluateCosAndSin(2, F, &cos1, &sin1); - y = 2.0* (lambda - 0.125*(3.0 + cos1))/(1.0 + cos1); + y = 2.0* (lambda - 0.125*(3.0 + cos1)) / (1.0 + cos1); L1[0] = 0.0; L1[1] = cos0; L2[0] = 0.0; @@ -1511,8 +1675,8 @@ unsigned int ON_SubDSectorType::GetLimitSurfaceEvaluationCoefficients( unsigned int i = 2; for (;;) { - L1[i] = y*(cos0 + cos1); - L2[i] = y*(sin0 + sin1); + L1[i] = y * (cos0 + cos1); + L2[i] = y * (sin0 + sin1); i++; if (i == R) break; @@ -1526,108 +1690,122 @@ unsigned int ON_SubDSectorType::GetLimitSurfaceEvaluationCoefficients( // current loop debugged and tested Feb 10, 2015 } } - return R; } - break; + return R; + } + break; - case ON_SubD::VertexTag::Dart: - if (1 == (R % 2)) + case ON_SubD::VertexTag::Dart: + if (1 == (R % 2)) + { + ON_Matrix Sbuffer; + const double*const* S = nullptr; + double* E1 = nullptr; + double* E2 = nullptr; + if (F > 5) { - ON_Matrix Sbuffer; - const double*const* S = nullptr; - double* E1 = nullptr; - double* E2 = nullptr; - if (F > 5) - { - if (false == Sbuffer.Create(R+2,R)) - return ON_SUBD_RETURN_ERROR(0); - if (R != ON_SubDSectorType::GetSubdivisionMatrix(R, Sbuffer.m)) - return ON_SUBD_RETURN_ERROR(0); - S = Sbuffer.m; - E1 = Sbuffer.m[R]; - E2 = Sbuffer.m[R+1]; - } - const double* termination_tolerances = nullptr; - double* eigenvectors[2] = { E1, E2 }; - double eigenprecision[2] = { 0 }; - double eigenpivots[3] = { 0 }; + if (false == Sbuffer.Create(R+2,R)) + return ON_SUBD_RETURN_ERROR(0); + if (R != ON_SubDSectorType::GetSubdivisionMatrix(Sbuffer.m, R)) + return ON_SUBD_RETURN_ERROR(0); + S = Sbuffer.m; + E1 = Sbuffer.m[R]; + E2 = Sbuffer.m[R+1]; + } + const double* termination_tolerances = nullptr; + double* eigenvectors[2] = { E1, E2 }; + double eigenprecision[2] = { 0 }; + double eigenpivots[3] = { 0 }; - if (nullptr != LP) + if (nullptr != LP) + { + switch (F) { - switch (F) + case 2: + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart + // + // Evaluation matrix is 5x5 with has eigenvalues 1, 1/2, 1/4, sqrt(2)/8, - sqrt(2)/8. + LP[0] = 8.0 / 31.0; + LP[1] = 10.0 / 31.0; + LP[2] = 3.0 / 62.0; + LP[3] = 10.0 / 31.0; + LP[4] = 3.0 / 62.0; + break; + + case 3: + LP[0] = 243/19.0; + LP[1] = 130/19.0; + LP[2] = 1.0; + LP[3] = 120/19.0; + LP[4] = 29/19.0; + //LP[5] = 120/19.0; + //LP[6] = 1.0; + break; + case 4: + LP[0] = 752/29.0; + LP[1] = 210/29.0; + LP[2] = 1.0; + LP[3] = 160/29.0; + LP[4] = 44/29.0; + LP[5] = 180/29.0; + //LP[6] = 44/29.0; + //LP[7] = 160/29.0; + //LP[8] = 1.0; + break; + case 5: + LP[0] = 43.222764462468341719; + LP[1] = 7.4578211569974673376; + LP[2] = 1.0; + LP[3] = 5.0843576860050653249; + LP[4] = 1.4771459186191773181; + LP[5] = 5.7257510234301278178; + LP[6] = 1.5305953634045991926; + //LP[7] = 5.7257510234301278178; + //LP[8] = 1.4771459186191773181; + //LP[9] = 5.0843576860050653249; + //LP[10] = 1.0000000000000000000; + break; + default: { - case 3: - LP[0] = 243/19.0; - LP[1] = 130/19.0; - LP[2] = 1.0; - LP[3] = 120/19.0; - LP[4] = 29/19.0; - //LP[5] = 120/19.0; - //LP[6] = 1.0; - break; - case 4: - LP[0] = 752/29.0; - LP[1] = 210/29.0; - LP[2] = 1.0; - LP[3] = 160/29.0; - LP[4] = 44/29.0; - LP[5] = 180/29.0; - //LP[6] = 44/29.0; - //LP[7] = 160/29.0; - //LP[8] = 1.0; - break; - case 5: - LP[0] = 43.222764462468341719; - LP[1] = 7.4578211569974673376; - LP[2] = 1.0; - LP[3] = 5.0843576860050653249; - LP[4] = 1.4771459186191773181; - LP[5] = 5.7257510234301278178; - LP[6] = 1.5305953634045991926; - //LP[7] = 5.7257510234301278178; - //LP[8] = 1.4771459186191773181; - //LP[9] = 5.0843576860050653249; - //LP[10] = 1.0000000000000000000; - break; - default: + // TODO + // Finish general case formula for dominant eigenvector of Transpose(S) + // Until I derive the formula, I can get the dominant eigenvector as the kernel of the matrix (S - I). + // This calculation has plenty of precision, but it's a less elegant way to get the solution. + if (nullptr == S) + return ON_SUBD_RETURN_ERROR(0); + const double lambda_local = 1.0; + const unsigned int lambda_multiplicity = 1; + if (lambda_multiplicity != ON_GetEigenvectors(R, S, true, lambda_local, lambda_multiplicity, termination_tolerances, eigenvectors, eigenprecision, eigenpivots)) + return ON_SUBD_RETURN_ERROR(0); + if (E1[0] < 0.0) { - // TODO - // Finish general case formula for dominant eigenvector of Transpose(S) - // Until I derive the formula, I can get the dominant eigenvector as the kernel of the matrix (S - I). - // This calculation has plenty of precision, but it's a less elegant way to get the solution. - if (nullptr == S) - return ON_SUBD_RETURN_ERROR(0); - const double lambda_local = 1.0; - const unsigned int lambda_multiplicity = 1; - if (lambda_multiplicity != ON_GetEigenvectors(R, S, true, lambda_local, lambda_multiplicity, termination_tolerances, eigenvectors, eigenprecision, eigenpivots)) - return ON_SUBD_RETURN_ERROR(0); - if (E1[0] < 0.0) - { - for ( unsigned int i = 0; i < R; i++ ) - E1[i] = -E1[i]; - } - for (unsigned int i = 0; i < R; i++) - { - if (E1[i] < 0.0) - return ON_SUBD_RETURN_ERROR(0); - } - LP[0] = E1[0]; - LP[1] = E1[1]; - unsigned int i0 = 2; - unsigned int i1 = R-1; - while (i0 <= i1) - { - LP[i0] = (E1[i0] == E1[i1]) ? E1[i0] : 0.5*(E1[i0] + E1[i1]); - i0++; - i1--; - } + for ( unsigned int i = 0; i < R; i++ ) + E1[i] = -E1[i]; + } + for (unsigned int i = 0; i < R; i++) + { + if (E1[i] < 0.0) + return ON_SUBD_RETURN_ERROR(0); + } + LP[0] = E1[0]; + LP[1] = E1[1]; + unsigned int i0 = 2; + unsigned int i1 = R-1; + while (i0 <= i1) + { + LP[i0] = (E1[i0] == E1[i1]) ? E1[i0] : 0.5*(E1[i0] + E1[i1]); + i0++; + i1--; } - break; } + break; + } + if (F >= 3) + { // The code below works for all F >= 3. const double* q0 = &LP[2]; - double* q1 = &LP[R-1]; + double* q1 = &LP[R - 1]; while (q0 < q1) *q1-- = *q0++; double LPsum = 0.0; @@ -1636,272 +1814,304 @@ unsigned int ON_SubDSectorType::GetLimitSurfaceEvaluationCoefficients( for (unsigned int i = 0; i < R; i++) LP[i] /= LPsum; } + } - if (nullptr != L1) + if (nullptr != L1) + { + switch (F) { - switch (F) + case 2: + // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart + // + // Evaluation matrix is 5x5 with has eigenvalues 1, 1/2, 1/4, sqrt(2)/8, - sqrt(2)/8. + // The multiplicity of the subdominant eigen value is 1/2 and there is a single well defined + // tangent in the limit surface. The other is a "fake" tangent that is close enough. + + // This is pseudo tangent associated with the eigenvalue 1/4 + // (1/4)^n goes to zero much faster than (1/2)^n, but ... + // If the 5 points were translated so the limit point was zero + // and then a rotation about the origin were applied so bonafide limit tangent + // was parallel to the x-axis (L2*(C,P0,Q0,P1,P1) = 0), then, as n goes to infinity, + // Q0 - Q1 would be the farthest point from the x-axis. + // the 5 + L1[0] = 0.0; + L1[1] = 0.0; + L1[2] = -1.0; + L1[3] = 0.0; + L1[4] = 1.0; + + // This is a bonafide limit tangent associated with the eigenvalue 1/2. + // I + L2[0] = 4.0; // 2^2 + L2[1] = 6.0; + L2[2] = -1.0; + L2[3] = -8.0; + L2[4] = -1.0; + break; + + case 3: + L1[0] = -9/4.0; // -L2[0]/4 + L1[1] = -3.0; + L1[2] = 1/2.0; + L1[3] = 3.0; + L1[4] = 3/4.0; + L1[5] = 1; + L1[6] = 0; + + L2[0] = 9.0; // 3^2 + L2[1] = 12.0; + L2[2] = -3; + L2[3] = -16; // -16 // -3, -16 = L2[3] = -16, -3 + L2[4] = -3; // -3 + L2[5] = 0; + L2[6] = 1; + break; + + case 4: + L1[0] = -4.0; // -L2[0]/4 + L1[1] = -7.0; + L1[2] = 1/2.0; + L1[3] = 3.0; + L1[4] = 3/2.0; + L1[5] = 4.0; + L1[6] = 1.0; + L1[7] = 1; + L1[8] = 0; + + L2[0] = 16.0; // 4^2 + L2[1] = 28.0; + L2[2] = -3; + L2[3] = -16; + L2[4] = -7.0; // -3, -16, L2[4], -16, -3 + L2[5] = -16; + L2[6] = -3; + L2[7] = 0; + L2[8] = 1; + break; + + case 5: + L1[0] = -6.25; // -L2[0]/4 + L1[1] = -14.208203932499369089; + L1[2] = 0.50000000000000000000; + L1[3] = 3.0000000000000000000; + L1[4] = 1.9635254915624211362; + L1[5] = 5.8541019662496845446; + L1[6] = 2.3680339887498948482; + L1[7] = 4.6180339887498948482; + L1[8] = 1.1545084971874737121; + L1[9] = 1; + L1[10] = 0; + + L2[0] = 25.0; // 5^2 + L2[1] = 56.832815729997476357; + L2[2] = -3; + L2[3] = -16; + L2[4] = -9.4721359549995793928; + L2[5] = -25.888543819998317571; // -3, -16, ..., L2[5], ..., -16, -3 + L2[6] = -9.4721359549995793928; + L2[7] = -16; // -16 + L2[8] = -3; // -3 + L2[9] = 0; + L2[10] = 1; + break; + + default: { - case 3: - L1[0] = -9/4.0; // -L2[0]/4 - L1[1] = -3.0; - L1[2] = 1/2.0; - L1[3] = 3.0; - L1[4] = 3/4.0; - L1[5] = 1; - L1[6] = 0; + // TODO + // Finish general case formula for subdominant eigenvectors of Transpose(S) + // Until I derive the formula, I can get the subdominant eigenvectors as the kernel of the matrix (S - 1/2*I). + // This calculation has plenty of precision, but it's a less elegant way to get the solution. + if (nullptr == S) + return ON_SUBD_RETURN_ERROR(0); + const double lambda_local = SubdominantEigenvalue(); + if (!(lambda_local > 0.0 && lambda_local < 1.0)) + return ON_SUBD_RETURN_ERROR(0); + const unsigned int lambda_multiplicity = 2; + if (lambda_multiplicity != ON_GetEigenvectors(R, S, true, lambda_local, lambda_multiplicity, termination_tolerances, eigenvectors, eigenprecision, eigenpivots)) + return ON_SUBD_RETURN_ERROR(0); - L2[0] = 9.0; // 3^2 - L2[1] = 12.0; - L2[2] = -3; - L2[3] = -16; // -16 // -3, -16 = L2[3] = -16, -3 - L2[4] = -3; // -3 - L2[5] = 0; - L2[6] = 1; - break; - case 4: - L1[0] = -4.0; // -L2[0]/4 - L1[1] = -7.0; - L1[2] = 1/2.0; - L1[3] = 3.0; - L1[4] = 3/2.0; - L1[5] = 4.0; - L1[6] = 1.0; - L1[7] = 1; - L1[8] = 0; + const double E[2][2] = { { E1[R - 2], E2[R - 2] }, { E1[R - 1], E2[R - 1] } }; - L2[0] = 16.0; // 4^2 - L2[1] = 28.0; - L2[2] = -3; - L2[3] = -16; - L2[4] = -7.0; // -3, -16, L2[4], -16, -3 - L2[5] = -16; - L2[6] = -3; - L2[7] = 0; - L2[8] = 1; - break; - case 5: - L1[0] = -6.25; // -L2[0]/4 - L1[1] = -14.208203932499369089; - L1[2] = 0.50000000000000000000; - L1[3] = 3.0000000000000000000; - L1[4] = 1.9635254915624211362; - L1[5] = 5.8541019662496845446; - L1[6] = 2.3680339887498948482; - L1[7] = 4.6180339887498948482; - L1[8] = 1.1545084971874737121; - L1[9] = 1; - L1[10] = 0; + const double det = E[0][0] * E[1][1] - E[0][1] * E[1][0]; + if (!(0.0 != det)) + return 0; - L2[0] = 25.0; // 5^2 - L2[1] = 56.832815729997476357; - L2[2] = -3; - L2[3] = -16; - L2[4] = -9.4721359549995793928; - L2[5] = -25.888543819998317571; // -3, -16, ..., L2[5], ..., -16, -3 - L2[6] = -9.4721359549995793928; - L2[7] = -16; // -16 - L2[8] = -3; // -3 - L2[9] = 0; - L2[10] = 1; - break; - default: + const double EtoL[2][2] = { + { E[1][1] / det, (0.0 == E[0][1]) ? 0.0 : (-E[0][1] / det) }, + { (0.0 == E[1][0]) ? 0.0 : (-E[1][0] / det), E[0][0] / det } }; + + for (unsigned int i = 0; i < R; i++) { - // TODO - // Finish general case formula for subdominant eigenvectors of Transpose(S) - // Until I derive the formula, I can get the subdominant eigenvectors as the kernel of the matrix (S - 1/2*I). - // This calculation has plenty of precision, but it's a less elegant way to get the solution. - if (nullptr == S) - return ON_SUBD_RETURN_ERROR(0); - const double lambda_local = SubdominantEigenvalue(); - if (!(lambda_local > 0.0 && lambda_local < 1.0)) - return ON_SUBD_RETURN_ERROR(0); - const unsigned int lambda_multiplicity = 2; - if (lambda_multiplicity != ON_GetEigenvectors(R, S, true, lambda_local, lambda_multiplicity, termination_tolerances, eigenvectors, eigenprecision, eigenpivots)) - return ON_SUBD_RETURN_ERROR(0); - - const double E[2][2] = { { E1[R - 2], E2[R - 2] }, { E1[R - 1], E2[R - 1] } }; - - const double det = E[0][0] * E[1][1] - E[0][1] * E[1][0]; - if (!(0.0 != det)) - return 0; - - const double EtoL[2][2] = { - { E[1][1] / det, (0.0 == E[0][1]) ? 0.0 : (-E[0][1] / det) }, - { (0.0 == E[1][0]) ? 0.0 : (-E[1][0] / det), E[0][0] / det } }; - - for (unsigned int i = 0; i < R; i++) - { - L1[i] = E1[i] * EtoL[0][0] + E2[i] * EtoL[1][0]; - L2[i] = E1[i] * EtoL[0][1] + E2[i] * EtoL[1][1]; - } - - const double int_tol = 1e-12; - double x; - for (unsigned int i = 0; i < R; i++) - { - x = floor(L1[i]+0.49); - if (fabs(x - L1[i]) <= int_tol) - L1[i] = x; - x = floor(L2[i]+0.49); - if (fabs(x - L2[i]) <= int_tol) - L2[i] = x; - } - - L1[0] = -0.25*F*F; - L1[R - 2] = 1.0; - L1[R - 1] = 0.0; - - L2[0] = F*F; - L2[2] = -3.0; - L2[3] = -16.0; - L2[R-4] = -16.0; - L2[R-3] = -3.0; - L2[R-2] = 0.0; - L2[R-1] = 1.0; - - // L2[2, ..., R-1] is symmetric about L2[2+R/2] - unsigned int i0 = 4; - unsigned int i1 = R - 5; - while (i0 < i1) - { - if (L2[i0] != L2[i1]) - { - x = 0.5*(L2[i0] + L2[i1]); - L2[i0] = x; - L2[i1] = x; - } - i0++; - i1--; - } + L1[i] = E1[i] * EtoL[0][0] + E2[i] * EtoL[1][0]; + L2[i] = E1[i] * EtoL[0][1] + E2[i] * EtoL[1][1]; + } + + const double int_tol = 1e-12; + double x; + for (unsigned int i = 0; i < R; i++) + { + x = floor(L1[i]+0.49); + if (fabs(x - L1[i]) <= int_tol) + L1[i] = x; + x = floor(L2[i]+0.49); + if (fabs(x - L2[i]) <= int_tol) + L2[i] = x; + } + + L1[0] = -0.25*F*F; + L1[R - 2] = 1.0; + L1[R - 1] = 0.0; + + L2[0] = F*F; + L2[2] = -3.0; + L2[3] = -16.0; + L2[R-4] = -16.0; + L2[R-3] = -3.0; + L2[R-2] = 0.0; + L2[R-1] = 1.0; + + // L2[2, ..., R-1] is symmetric about L2[2+R/2] + unsigned int i0 = 4; + unsigned int i1 = R - 5; + while (i0 < i1) + { + if (L2[i0] != L2[i1]) + { + x = 0.5*(L2[i0] + L2[i1]); + L2[i0] = x; + L2[i1] = x; + } + i0++; + i1--; } - break; } - } - return R; - } - break; - - - case ON_SubD::VertexTag::Corner: - if (0 == (R % 2)) - { - const unsigned int angle_index = CornerSectorAngleIndex(); - if ( 36 == angle_index && F > 1) - b180degreeCorner = true; // use crease calculation - else - { - // the corner angle alpha is not equal to pi (180 degrees) - // so limit tangents will not be parallel - // Lpev[] - LP[0] = 1.0; // center point coefficient - for (unsigned int i = 1; i < R; i++) - LP[i++] = 0.0; // all face and edge coefficients = 0 - - L1[0] = -1.0; // center point coefficient - L1[1] = 1.0; // initial boundary edge point coefficient - L1[R - 1] = 0.0; // final boundary edge point coefficient - - // The sign of s is selected to keep the normal pointing "up". - double s = (2 * angle_index <= ON_SubDSectorType::MaximumAngleIndex) ? 1.0 : -1.0; - L2[0] = -s; // center point coefficient - L2[1] = 0.0; // initial boundary edge point coefficient - L2[R - 1] = s; // final boundary edge point coefficient - for (unsigned int i = 2; i + 1 < R; i++) - { - L1[i] = 0.0; // all face and interior edge coefficients = 0 - L2[i] = 0.0; // all face and interior edge coefficients = 0 - } - - if (36 == angle_index && 1 == F && 4 == R) - { - // This case has no good solution. - L2[0] = -1.0; - L2[1] = 0.0; - L2[2] = 1.0; - L2[3] = 0.0; - } - - // TODO deal with 180 degree "corner case". - // L0 and L1 are the same as above. L2 will be similar to the crease case - return R; + break; } } - if ( false == b180degreeCorner) - break; - - case ON_SubD::VertexTag::Crease: - // NOTE: In the case when there are 2 crease edes and a single face, - // The Catmull-Clark subdivision matrix is singular. - if (0 == (R % 2)) - { - // Lpev[] - if (b180degreeCorner) - { - LP[0] = 1.0; // center point coefficient - for (unsigned int i = 1; i < R; i++) - LP[i++] = 0.0; // all face and edge coefficients = 0 - } - else - { - LP[0] = 2.0 / 3.0; // center point coefficient - LP[1] = LP[R - 1] = 1.0 / 6.0; // boundary edge point coefficients - for (unsigned int i = 2; i + 1 < R; i++) - LP[i++] = 0.0; // all face and interior edge coefficients = 0 - } - - // Using L1[] computes the tangent to the crease curve. This - // curve is a uniform cubic spline with control points - // (..., final crease edge point, center point, initial crease edge point, ...) - L1[ 0] = 0.0; // center point coefficient - L1[ 1] = 1.0; // initial boundary edge point coefficient - L1[R-1] = -1.0; // final boundary edge point coefficient - for (unsigned int i = 2; i+1 < R; i++) - L1[i] = 0.0; // all face and interior edge coefficients = 0 - - // Using L2[] computes a tangent that points from the limit point - // into the subdivision surface. - if (1 == F) - { - // This is the special where IsCatmullClarkCreaseOneFaceCase() returns true. - // Catmull-Clark subdivison, - // center vertex is a crease vertex, - // one face and two crease edges. - L2[0] = -2.0; // center point coefficients - L2[1] = 1.0; // boundary edge point coefficient - L2[2] = 0.0; // face point coefficient - L2[3] = 1.0; // boundary edge point coefficient - } - else - { - // F faces, F-1 interior smooth edges and 2 boundary edges - // theta = pi/F - cos0 = 1.0; // cos(0) - sin0 = 0.0; // sin(0) - ON_SubDMatrix::EvaluateCosAndSin(1,F,&cos1,&sin1); // cos1 = cos(pi/F), sin1 = sin(pi/F) - z = (1.0 + cos1) / sin1; - L2[0] = -sin1; // center point coefficients - L2[1] = -0.25*z*(1.0 + 2.0*cos1); // initial boundary edge point coefficient - L2[2] = 0.25*(sin0 + sin1); // first interior face point coefficient - L2[R-1] = L2[1]; // final boundary edge point coefficient - unsigned int i = 3; - for (;;) - { - cos0 = cos1; - sin0 = sin1; - ON_SubDMatrix::EvaluateCosAndSin(i/2 + 1, F, &cos1, &sin1); - L2[i] = sin0; // interior edge point coefficient - i++; - L2[i] = 0.25*(sin0 + sin1); // interior face point coefficient - i++; - if ( i >= R-1 ) - break; - } - } - - return R; - } - break; + return R; } + break; + + + case ON_SubD::VertexTag::Corner: + if (0 == (R % 2)) + { + const unsigned int angle_index = CornerSectorAngleIndex(); + if ( 36 == angle_index && F > 1) + b180degreeCorner = true; // use crease calculation + else + { + // the corner angle alpha is not equal to pi (180 degrees) + // so limit tangents will not be parallel + // Lpev[] + LP[0] = 1.0; // center point coefficient + for (unsigned int i = 1; i < R; i++) + LP[i++] = 0.0; // all face and edge coefficients = 0 + + L1[0] = -1.0; // center point coefficient + L1[1] = 1.0; // initial boundary edge point coefficient + L1[R - 1] = 0.0; // final boundary edge point coefficient + + // The sign of s is selected to keep the normal pointing "up". + double s = (2 * angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex) ? 1.0 : -1.0; + L2[0] = -s; // center point coefficient + L2[1] = 0.0; // initial boundary edge point coefficient + L2[R - 1] = s; // final boundary edge point coefficient + for (unsigned int i = 2; i + 1 < R; i++) + { + L1[i] = 0.0; // all face and interior edge coefficients = 0 + L2[i] = 0.0; // all face and interior edge coefficients = 0 + } + + if (36 == angle_index && 1 == F && 4 == R) + { + // This case has no good solution. + L2[0] = -1.0; + L2[1] = 0.0; + L2[2] = 1.0; + L2[3] = 0.0; + } + + // TODO deal with 180 degree "corner case". + // L0 and L1 are the same as above. L2 will be similar to the crease case + return R; + } + } + if ( false == b180degreeCorner) + break; + + case ON_SubD::VertexTag::Crease: + // NOTE: In the case when there are 2 crease edes and a single face, + // The Catmull-Clark subdivision matrix is singular. + if (0 == (R % 2)) + { + // Lpev[] + if (b180degreeCorner) + { + LP[0] = 1.0; // center point coefficient + for (unsigned int i = 1; i < R; i++) + LP[i++] = 0.0; // all face and edge coefficients = 0 + } + else + { + LP[0] = 2.0 / 3.0; // center point coefficient + LP[1] = LP[R - 1] = 1.0 / 6.0; // boundary edge point coefficients + for (unsigned int i = 2; i + 1 < R; i++) + LP[i++] = 0.0; // all face and interior edge coefficients = 0 + } + + // Using L1[] computes the tangent to the crease curve. This + // curve is a uniform cubic spline with control points + // (..., final crease edge point, center point, initial crease edge point, ...) + L1[ 0] = 0.0; // center point coefficient + L1[ 1] = 1.0; // initial boundary edge point coefficient + L1[R-1] = -1.0; // final boundary edge point coefficient + for (unsigned int i = 2; i+1 < R; i++) + L1[i] = 0.0; // all face and interior edge coefficients = 0 + + // Using L2[] computes a tangent that points from the limit point + // into the subdivision surface. + if (1 == F) + { + // This is the special where IsCatmullClarkCreaseOneFaceCase() returns true. + // Catmull-Clark subdivison, + // center vertex is a crease vertex, + // one face and two crease edges. + L2[0] = -2.0; // center point coefficients + L2[1] = 1.0; // boundary edge point coefficient + L2[2] = 0.0; // face point coefficient + L2[3] = 1.0; // boundary edge point coefficient + } + else + { + // F faces, F-1 interior smooth edges and 2 boundary edges + // theta = pi/F + cos0 = 1.0; // cos(0) + sin0 = 0.0; // sin(0) + ON_SubDMatrix::EvaluateCosAndSin(1,F,&cos1,&sin1); // cos1 = cos(pi/F), sin1 = sin(pi/F) + z = (1.0 + cos1) / sin1; + L2[0] = -sin1; // center point coefficients + L2[1] = -0.25*z*(1.0 + 2.0*cos1); // initial boundary edge point coefficient + L2[2] = 0.25*(sin0 + sin1); // first interior face point coefficient + L2[R-1] = L2[1]; // final boundary edge point coefficient + unsigned int i = 3; + for (;;) + { + cos0 = cos1; + sin0 = sin1; + ON_SubDMatrix::EvaluateCosAndSin(i/2 + 1, F, &cos1, &sin1); + L2[i] = sin0; // interior edge point coefficient + i++; + L2[i] = 0.25*(sin0 + sin1); // interior face point coefficient + i++; + if ( i >= R-1 ) + break; + } + } + + return R; + } + break; } return ON_SUBD_RETURN_ERROR(0); @@ -1913,9 +2123,9 @@ bool ON_SubDMatrix::IsValid() const } bool ON_SubDMatrix::IsValidPointRing( + const double* point_ring, size_t point_ring_count, - size_t point_ring_stride, - const double* point_ring + size_t point_ring_stride ) const { return ( point_ring_count >= 4 && point_ring_stride >= 3 && nullptr != point_ring && m_R == point_ring_count); @@ -1923,15 +2133,15 @@ bool ON_SubDMatrix::IsValidPointRing( bool ON_SubDMatrix::EvaluateSubdivisionPoint( unsigned int element_index, + const double* point_ring, size_t point_ring_count, size_t point_ring_stride, - const double* point_ring, double subd_point[3] ) const { if ( nullptr == m_S || element_index >= m_R ) return ON_SUBD_RETURN_ERROR(false); - if (false == IsValidPointRing(point_ring_count,point_ring_stride,point_ring)) + if (false == IsValidPointRing(point_ring,point_ring_count,point_ring_stride)) return ON_SUBD_RETURN_ERROR(false); subd_point[0] = 0.0; @@ -1952,19 +2162,21 @@ bool ON_SubDMatrix::EvaluateSubdivisionPoint( return true; } -bool ON_SubDMatrix::EvaluateLimitPoint( +bool ON_SubDMatrix::EvaluateSurfacePoint( + const double* point_ring, size_t point_ring_count, size_t point_ring_stride, - const double* point_ring, - ON_SubDSectorLimitPoint& limit_point + bool bUndefinedNormalIsPossible, + ON_SubDSectorSurfacePoint& limit_point ) const { limit_point.m_next_sector_limit_point = nullptr; limit_point.m_sector_face = nullptr; - return EvaluateLimitPoint( + return EvaluateSurfacePoint( + point_ring, point_ring_count, point_ring_stride, - point_ring, + bUndefinedNormalIsPossible, limit_point.m_limitP, limit_point.m_limitT1, limit_point.m_limitT2, @@ -1972,13 +2184,105 @@ bool ON_SubDMatrix::EvaluateLimitPoint( ); } -bool ON_SubDMatrix::EvaluateLimitPoint( +static bool Internal_GetAlterateTangent( + const ON_SubDMatrix& subd_matrix, + unsigned int Ldex, size_t point_ring_count, size_t point_ring_stride, const double* point_ring, + const double L[3][3], + double* alternate_tangent +) +{ + if (point_ring_count < 4 || point_ring_stride < 3 || nullptr == point_ring) + return false; + + if (2 == Ldex) + { + if ( 4 == subd_matrix.m_R && ON_SubD::VertexTag::Crease == subd_matrix.m_sector_type.VertexTag() ) + { + // valence 2 crease case when crease edges are colinear + // F = face point, C = crease vertex point. + // Default tangents: + // L[1] = ON_3dPoint(point_ring + point_ring_stride) - ON_3dPoint(point_ring + 3 * point_ring_stride); + const ON_3dPoint C(point_ring); + const ON_3dPoint Q(point_ring + 2 * point_ring_stride); + ON_3dVector V(Q - C); + if (V.IsNotZero()) + { + alternate_tangent[0] = V.x; + alternate_tangent[1] = V.y; + alternate_tangent[2] = V.z; + return true; + } + } + } + + return false; +} + +static bool Internal_GetAlterateNormal( + const ON_SubDMatrix& subd_matrix, + size_t point_ring_count, + size_t point_ring_stride, + const double* point_ring, + double L[3][3], + bool bHaveAlternateL[3], + double alternate_normal[3] +) +{ + if (point_ring_count < 4 || point_ring_stride < 3 || nullptr == point_ring) + return false; + + if ( 4 == subd_matrix.m_R && ON_SubD::VertexTag::Crease == subd_matrix.m_sector_type.VertexTag() ) + { + // valence 2 crease case when crease edges are colinear + // F = face point, C = crease vertex point. + ON_3dVector N(ON_3dVector::ZeroVector); + if (false == bHaveAlternateL[2]) + { + bHaveAlternateL[2] = Internal_GetAlterateTangent(subd_matrix, 2, point_ring_count, point_ring_stride, point_ring, L, L[2]); + if (bHaveAlternateL[2]) + { + ON_3dVector T1(L[1]); + ON_3dVector T2(L[2]); + N = ON_CrossProduct(T1, T2).UnitVector(); + } + } + if (N.IsZero()) + { + N = ON_CrossProduct(L[1], L[2]).UnitVector(); + if (N.IsZero()) + { + const ON_3dPoint C(point_ring); + const ON_3dPoint P0(point_ring + 1 * point_ring_stride); // end of crease leaving C + const ON_3dPoint Q(point_ring + 2 * point_ring_stride); + const ON_3dPoint P1(point_ring + 1 * point_ring_stride); // end of crease entering C + const ON_3dVector A = (P0 - P1).UnitVector(); + const ON_3dVector B = (Q - C).UnitVector(); + N = ON_CrossProduct(A, B).UnitVector(); + } + } + if (N.IsNotZero()) + { + alternate_normal[0] = N.x; + alternate_normal[1] = N.y; + alternate_normal[2] = N.z; + return true; + } + } + + return false; +} + +bool ON_SubDMatrix::EvaluateSurfacePoint( + const double* point_ring, + size_t point_ring_count, + size_t point_ring_stride, + bool bUndefinedNormalIsPossible, double limit_point[3], - double limit_tangent0[3], double limit_tangent1[3], + double limit_tangent2[3], double limit_normal[3] ) const { @@ -1996,24 +2300,24 @@ bool ON_SubDMatrix::EvaluateLimitPoint( limit_normal[2] = ON_DBL_QNAN; } - if (nullptr != limit_tangent0) - { - limit_tangent0[0] = ON_DBL_QNAN; - limit_tangent0[1] = ON_DBL_QNAN; - limit_tangent0[2] = ON_DBL_QNAN; - } - if (nullptr != limit_tangent1) { limit_tangent1[0] = ON_DBL_QNAN; limit_tangent1[1] = ON_DBL_QNAN; limit_tangent1[2] = ON_DBL_QNAN; } + + if (nullptr != limit_tangent2) + { + limit_tangent2[0] = ON_DBL_QNAN; + limit_tangent2[1] = ON_DBL_QNAN; + limit_tangent2[2] = ON_DBL_QNAN; + } if (nullptr == m_LP || nullptr == m_L1 || nullptr == m_L2 ) return ON_SUBD_RETURN_ERROR(false); - if (false == IsValidPointRing(point_ring_count,point_ring_stride,point_ring)) + if (false == IsValidPointRing(point_ring,point_ring_count,point_ring_stride)) return ON_SUBD_RETURN_ERROR(false); double x, y, z, c, L[3][3] = { {0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0} }; @@ -2049,38 +2353,82 @@ bool ON_SubDMatrix::EvaluateLimitPoint( limit_point[1] = L[0][1]; limit_point[2] = L[0][2]; } - if (nullptr != limit_tangent0) + + if (nullptr == limit_tangent1 && nullptr == limit_tangent2 && nullptr == limit_normal) + return true; + + bool bHaveAlternateL[3] = {}; + for (unsigned int Ldex = 1; Ldex < 3; ++Ldex) { - limit_tangent0[0] = L[1][0]; - limit_tangent0[1] = L[1][1]; - limit_tangent0[2] = L[1][2]; - if (0.0 == limit_tangent0[0] && 0.0 == limit_tangent0[1] && 0.0 == limit_tangent0[2]) + if (0.0 == L[Ldex][0] && 0.0 == L[Ldex][1] && 0.0 == L[Ldex][2]) { - ON_ERROR("limit_tangent0[0] = zero vector"); + Internal_GetAlterateTangent(*this, Ldex, point_ring_count, point_ring_stride, point_ring, L, L[Ldex]); + bHaveAlternateL[Ldex] = true; } } + + ON_3dVector T1(L[1]); + T1.Unitize(); + + ON_3dVector T2(L[2]); + T2.Unitize(); + + ON_3dVector N = ON_CrossProduct(T1, T2); + N.Unitize(); + if (N.IsZero()) + { + Internal_GetAlterateNormal( + *this, + point_ring_count, + point_ring_stride, + point_ring, + L, + bHaveAlternateL, + &N.x + ); + } + if (nullptr != limit_tangent1) { - limit_tangent1[0] = L[2][0]; - limit_tangent1[1] = L[2][1]; - limit_tangent1[2] = L[2][2]; + limit_tangent1[0] = T1.x; + limit_tangent1[1] = T1.y; + limit_tangent1[2] = T1.z; if (0.0 == limit_tangent1[0] && 0.0 == limit_tangent1[1] && 0.0 == limit_tangent1[2]) { - ON_ERROR("limit_tangent1[0] = zero vector"); + if (false == bUndefinedNormalIsPossible) + { + ON_ERROR("limit_tangent1[0] = zero vector"); + bUndefinedNormalIsPossible = true; // one error is sufficient + } } } + + if (nullptr != limit_tangent2) + { + limit_tangent2[0] = T2.x; + limit_tangent2[1] = T2.y; + limit_tangent2[2] = T2.z; + if (0.0 == limit_tangent2[0] && 0.0 == limit_tangent2[1] && 0.0 == limit_tangent2[2]) + { + if ( false == bUndefinedNormalIsPossible ) + { + ON_ERROR("limit_tangent2[0] = zero vector"); + bUndefinedNormalIsPossible = true; // one error is sufficient + } + } + } + if (nullptr != limit_normal) { - ((ON_3dVector*)L[1])->Unitize(); - ((ON_3dVector*)L[2])->Unitize(); - ON_3dVector N = ON_CrossProduct(L[1], L[2]); - N.Unitize(); limit_normal[0] = N.x; limit_normal[1] = N.y; limit_normal[2] = N.z; if (0.0 == limit_normal[0] && 0.0 == limit_normal[1] && 0.0 == limit_normal[2]) { - ON_ERROR("limit_normal[0] = zero vector"); + if ( false == bUndefinedNormalIsPossible ) + { + ON_ERROR("limit_normal[0] = zero vector"); + } } } @@ -2251,12 +2599,6 @@ unsigned int ON_SubDMatrix::SetFromSectorType( if (false == sector_type.IsValid() ) return SetTypeAndValenceFailure(); - if (ON_SubD::SubDType::QuadCatmullClark != sector_type.SubDType()) - { - // support for tri subd is not ready yet - return SetTypeAndValenceFailure(); - } - const unsigned int R = sector_type.PointRingCount(); if (R < 3) return SetTypeAndValenceFailure(); @@ -2270,10 +2612,10 @@ unsigned int ON_SubDMatrix::SetFromSectorType( double* L1 = LP + R; double* L2 = L1 + R; - const bool bLimitEvaluationCoefficientsAvailable = sector_type.LimitEvaluationCoefficientsAvailable(); + const bool bLimitEvaluationCoefficientsAvailable = sector_type.SurfaceEvaluationCoefficientsAvailable(); if (bLimitEvaluationCoefficientsAvailable) { - if (R != sector_type.GetLimitSurfaceEvaluationCoefficients(R, LP, R, L1, R, L2)) + if (R != sector_type.GetSurfaceEvaluationCoefficients(LP, R, L1, R, L2, R)) return SetTypeAndValenceFailure(); } else @@ -2288,7 +2630,7 @@ unsigned int ON_SubDMatrix::SetFromSectorType( m__max_R = R; } - if (R != sector_type.GetSubdivisionMatrix(R, m__S.m)) + if (R != sector_type.GetSubdivisionMatrix(m__S.m, R)) return SetTypeAndValenceFailure(); m_sector_type = sector_type; @@ -2374,7 +2716,7 @@ double ON_SubDMatrix::TestMatrix() const double rc = 0.0; - const bool bTestLimitEvaluation = m_sector_type.LimitEvaluationCoefficientsAvailable(); + const bool bTestLimitEvaluation = m_sector_type.SurfaceEvaluationCoefficientsAvailable(); // Eigen information test ON_SimpleArray buffer; @@ -2383,13 +2725,13 @@ double ON_SubDMatrix::TestMatrix() const double* LP = E2 + m_R; double* L1 = LP + m_R; double* L2 = L1 + m_R; - double d = m_sector_type.GetSubdominantEigenvectors(m_R,E1,m_R,E2); + double d = m_sector_type.GetSubdominantEigenvectors(E1, m_R, E2, m_R); if (!(d == lambda)) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); if (bTestLimitEvaluation) { - if (!m_sector_type.GetLimitSurfaceEvaluationCoefficients(m_R, LP, m_R, L1, m_R, L2)) + if (!m_sector_type.GetSurfaceEvaluationCoefficients(LP, m_R, L1, m_R, L2, m_R)) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); } else @@ -2551,7 +2893,7 @@ double ON_SubDMatrix::TestMatrix() const } // See if L1 and L2 can produce a reasonable normal in the simplist possible case - double z = m_sector_type.LimitSurfaceNormalSign(); + double z = m_sector_type.SurfaceNormalSign(); if (!(z > 0.0)) return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); } @@ -2569,16 +2911,6 @@ double ON_SubDMatrix::TestEvaluation( ON_TextLog* text_log ) { - - ON_SubD::SubDType subd_types[] = { - ON_SubD::SubDType::QuadCatmullClark, - ON_SubD::SubDType::TriLoopWarren, - }; - const char* subd_type_names[sizeof(subd_types) / sizeof(subd_types[0])] = { - "ccquad", - "lwtri" - }; - ON_SubD::VertexTag vertex_tags[] = { ON_SubD::VertexTag::Smooth ,ON_SubD::VertexTag::Crease @@ -2600,23 +2932,10 @@ double ON_SubDMatrix::TestEvaluation( : ON_SubDSectorType::UnsetCornerSectorAngle; - ON_SubD::SubDType subdivision_type = sector_type.SubDType(); ON_SubD::VertexTag vertex_tag = sector_type.VertexTag(); - size_t subd_type_count = sizeof(subd_types) / sizeof(subd_types[0]); + size_t subd_type_count = 1; size_t subd_type_index0 = 0; - if (ON_SubD::IsQuadOrTriSubDType(subdivision_type)) - { - for (size_t subd_type_index = 0; subd_type_index < subd_type_count; subd_type_index++) - { - if (subdivision_type == subd_types[subd_type_index]) - { - subd_type_index0 = subd_type_index; - subd_type_count = subd_type_index + 1; - break; - } - } - } if ( 0 == subd_type_index0 && 2 == subd_type_count) subd_type_count = 1; // tri stuff no ready @@ -2635,7 +2954,7 @@ double ON_SubDMatrix::TestEvaluation( if (ON_SubD::VertexTag::Corner == vertex_tag && ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians) ) { unsigned int angle_dex = sector_type.CornerSectorAngleIndex(); - if (angle_dex <= ON_SubDSectorType::MaximumAngleIndex) + if (angle_dex <= ON_SubDSectorType::MaximumCornerAngleIndex) { corner_sector_angle_index0 = angle_dex; corner_sector_angle_index1 = angle_dex+1; @@ -2660,71 +2979,65 @@ double ON_SubDMatrix::TestEvaluation( unsigned int fail_count = 0; double max_d = 0.0; const unsigned int maximum_fail_count = 10; - for (size_t subd_type_index = subd_type_index0; subd_type_index < subd_type_count; subd_type_index++) + + const char* sSubDTypeName = "ccquad"; + + for (size_t vertex_tag_index = vertex_tag_index0; vertex_tag_index < vertex_tag_count; vertex_tag_index++) { - const ON_SubD::SubDType subd_type = subd_types[subd_type_index]; + const ON_SubD::VertexTag vertex_tag_for_scope = vertex_tags[vertex_tag_index]; + const char* sVertexTagName = vertex_tag_names[vertex_tag_index]; - const char* sSubDTypeName = subd_type_names[subd_type_index]; + unsigned int Fmin = ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag_for_scope); + if ( minimum_sector_face_count > Fmin ) + Fmin = minimum_sector_face_count; - for (size_t vertex_tag_index = vertex_tag_index0; vertex_tag_index < vertex_tag_count; vertex_tag_index++) + unsigned int angle_i0 = corner_sector_angle_index0; + unsigned int angle_i1 = corner_sector_angle_index1; + if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope && ON_SubDSectorType::UnsetCornerSectorAngle == corner_sector_angle_radians) { - const ON_SubD::VertexTag vertex_tag_for_scope = vertex_tags[vertex_tag_index]; - const char* sVertexTagName = vertex_tag_names[vertex_tag_index]; + angle_i0 = 2; + angle_i1 = ON_SubDSectorType::MaximumCornerAngleIndex/2 - 1; + } - unsigned int Fmin = ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag_for_scope); - if ( minimum_sector_face_count > Fmin ) - Fmin = minimum_sector_face_count; - - unsigned int angle_i0 = corner_sector_angle_index0; - unsigned int angle_i1 = corner_sector_angle_index1; - if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope && ON_SubDSectorType::UnsetCornerSectorAngle == corner_sector_angle_radians) + for (unsigned int F = Fmin; F <= Fmax; F++) + { + for (unsigned int corner_sector_angle_index = angle_i0; corner_sector_angle_index < angle_i1; corner_sector_angle_index++) { - angle_i0 = 2; - angle_i1 = ON_SubDSectorType::MaximumAngleIndex/2 - 1; - } + double angle_radians = corner_sector_angle_radians; + if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope && ON_SubDSectorType::UnsetCornerSectorAngle == angle_radians) + angle_radians = ON_SubDSectorType::AngleRadiansFromCornerAngleIndex(corner_sector_angle_index); - for (unsigned int F = Fmin; F <= Fmax; F++) - { - for (unsigned int corner_sector_angle_index = angle_i0; corner_sector_angle_index < angle_i1; corner_sector_angle_index++) + ON_SubDSectorType test_sector_type = ON_SubDSectorType::Create( vertex_tag_for_scope, F, angle_radians); + + if (false == test_sector_type.SurfaceEvaluationCoefficientsAvailable()) + continue; + const unsigned int N = test_sector_type.EdgeCount(); + double d = ON_SubDMatrix::FromCache(test_sector_type).TestEvaluation(); + if (d >= 0.0) { - double angle_radians = corner_sector_angle_radians; - if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope && ON_SubDSectorType::UnsetCornerSectorAngle == angle_radians) - angle_radians = ON_SubDSectorType::AngleRadiansFromAngleIndex(corner_sector_angle_index); - - ON_SubDSectorType test_sector_type = ON_SubDSectorType::Create(subd_type, vertex_tag_for_scope, F, angle_radians); - - if (false == test_sector_type.LimitEvaluationCoefficientsAvailable()) - continue; - const unsigned int N = test_sector_type.EdgeCount(); - double d = ON_SubDMatrix::FromCache(test_sector_type).TestEvaluation(); - if (d >= 0.0) - { - pass_count++; - if (d > max_d) - max_d = d; - } - else - { - fail_count++; - } - if (nullptr != text_log) - { - ON_String test_description; - if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope) - test_description.Format("%s, %s, %u faces, %u edges, angle = %u/%u 2pi", sSubDTypeName, sVertexTagName, F, N, corner_sector_angle_index, ON_SubDSectorType::MaximumAngleIndex); - else - test_description.Format("%s, %s, %u faces, %u edges", sSubDTypeName, sVertexTagName, F, N); - - if (d >= 0.0) - text_log->Print("Test( %s) passed. Deviation = %g\n", (const char*)test_description, d); - else - text_log->Print("Test( %s ) failed\n", (const char*)test_description); - } - if (ON_SubD::VertexTag::Corner != vertex_tag_for_scope) - break; - if (fail_count >= maximum_fail_count) - break; + pass_count++; + if (d > max_d) + max_d = d; } + else + { + fail_count++; + } + if (nullptr != text_log) + { + ON_String test_description; + if (ON_SubD::VertexTag::Corner == vertex_tag_for_scope) + test_description.Format("%s, %s, %u faces, %u edges, angle = %u/%u 2pi", sSubDTypeName, sVertexTagName, F, N, corner_sector_angle_index, ON_SubDSectorType::MaximumCornerAngleIndex); + else + test_description.Format("%s, %s, %u faces, %u edges", sSubDTypeName, sVertexTagName, F, N); + + if (d >= 0.0) + text_log->Print("Test( %s) passed. Deviation = %g\n", (const char*)test_description, d); + else + text_log->Print("Test( %s ) failed\n", (const char*)test_description); + } + if (ON_SubD::VertexTag::Corner != vertex_tag_for_scope) + break; if (fail_count >= maximum_fail_count) break; } @@ -2735,6 +3048,7 @@ double ON_SubDMatrix::TestEvaluation( break; } + if (text_log) { text_log->Print("%u tests. %d failed. Maximum deviation = %g\n",pass_count+fail_count, fail_count, max_d); @@ -2763,8 +3077,8 @@ static void TestPrecision( } double ON_SubDMatrix::TestComponentRing( - size_t component_ring_count, - const ON_SubDComponentPtr* component_ring + const ON_SubDComponentPtr* component_ring, + size_t component_ring_count ) const { const double rc_error = ON_UNSET_VALUE; @@ -2783,7 +3097,6 @@ double ON_SubDMatrix::TestComponentRing( if (m_R != R) return ON_SUBD_RETURN_ERROR(rc_error); - const ON_SubD::SubDType subd_type = m_sector_type.SubDType(); const ON_SubD::VertexTag vertex_tag = m_sector_type.VertexTag(); const unsigned int face_edge_count = m_sector_type.FacetEdgeCount(); const bool bSubdivideFaces = (R == F+N+1 && 4 == face_edge_count); @@ -2792,16 +3105,16 @@ double ON_SubDMatrix::TestComponentRing( if (nullptr == center_vertex) return ON_SUBD_RETURN_ERROR(rc_error); - if (vertex_tag != center_vertex->m_vertex_tag || center_vertex->IsStandard(subd_type)) + if (vertex_tag != center_vertex->m_vertex_tag || center_vertex->IsStandard()) return ON_UNSET_VALUE; // not an error - need a round of subdivision before matrix can be used ON_SimpleArray point_ring_array; - if ( R != ON_SubD::GetSectorPointRing(subd_type,false,component_ring_count,component_ring,point_ring_array) ) + if ( R != ON_SubD::GetSectorPointRing(false,component_ring_count,component_ring,point_ring_array) ) return ON_SUBD_RETURN_ERROR(rc_error); const ON_3dPoint* vertexR = point_ring_array.Array(); ON_3dPoint Q; - if (false == center_vertex->GetSubdivisionPoint(subd_type, false, &Q.x)) + if (false == center_vertex->EvaluateCatmullClarkSubdivisionPoint(&Q.x)) return ON_SUBD_RETURN_ERROR(rc_error); double Pdelta = 0.0; @@ -2809,7 +3122,7 @@ double ON_SubDMatrix::TestComponentRing( double Fdelta = 0.0; ON_3dPoint P = ON_3dPoint::Origin; - if (false == EvaluateSubdivisionPoint(0,R,3,&vertexR[0].x,P)) + if (false == EvaluateSubdivisionPoint(0, &vertexR[0].x, R,3,P)) return ON_SUBD_RETURN_ERROR(rc_error); double d = P.DistanceTo(Q); TestPrecision(d, Pdelta); @@ -2820,11 +3133,11 @@ double ON_SubDMatrix::TestComponentRing( if (nullptr == edge) return ON_SUBD_RETURN_ERROR(rc_error); - if (false == edge->GetSubdivisionPoint(subd_type, false, &Q.x)) + if (false == edge->EvaluateCatmullClarkSubdivisionPoint(&Q.x)) return ON_SUBD_RETURN_ERROR(rc_error); P = ON_3dPoint::Origin; - if (false == EvaluateSubdivisionPoint(i,R,3,&vertexR[0].x,P)) + if (false == EvaluateSubdivisionPoint(i, &vertexR[0].x, R,3,P)) return ON_SUBD_RETURN_ERROR(rc_error); double dist_PQ = P.DistanceTo(Q); @@ -2841,11 +3154,11 @@ double ON_SubDMatrix::TestComponentRing( if (face_edge_count != face->m_edge_count) return ON_SUBD_RETURN_ERROR(rc_error); - if (false == face->GetSubdivisionPoint(subd_type, false, &Q.x)) + if (false == face->EvaluateCatmullClarkSubdivisionPoint(&Q.x)) return ON_SUBD_RETURN_ERROR(rc_error); P = ON_3dPoint::Origin; - if (false == EvaluateSubdivisionPoint(i,R,3,&vertexR[0].x,P)) + if (false == EvaluateSubdivisionPoint(i, &vertexR[0].x, R,3,P)) return ON_SUBD_RETURN_ERROR(rc_error); dist_PQ = P.DistanceTo(Q); @@ -2862,28 +3175,20 @@ double ON_SubDMatrix::TestComponentRing( } double ON_SubDMatrix::TestEvaluation( - ON_SubD::SubDType subd_type, const unsigned int subd_recursion_count, ON_SubDSectorIterator sit, ON_SimpleArray& component_ring, ON_SimpleArray< ON_3dPoint >& subd_points, - ON_SubDSectorLimitPoint& limit_point + ON_SubDSectorSurfacePoint& limit_point ) { const double rc_error = ON_UNSET_VALUE; - limit_point = ON_SubDSectorLimitPoint::Unset; + limit_point = ON_SubDSectorSurfacePoint::Unset; component_ring.SetCount(0); subd_points.SetCount(0); - if (ON_SubD::SubDType::QuadCatmullClark != subd_type) - return ON_SUBD_RETURN_ERROR(rc_error); // TODO add support for ON_SubD::SubDType::TriLoopWarren - - const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); - if (ON_SubD::FacetType::Tri != facet_type && ON_SubD::FacetType::Quad != facet_type) - return ON_SUBD_RETURN_ERROR(rc_error); - - const ON_SubDSectorType sector_type = ON_SubDSectorType::Create(subd_type,sit); + const ON_SubDSectorType sector_type = ON_SubDSectorType::Create(sit); const unsigned int component_ring_count = ON_SubD::GetSectorComponentRing(sit,component_ring); if ( component_ring_count < 4 || component_ring_count != sector_type.ComponentRingCount()) @@ -2895,7 +3200,7 @@ double ON_SubDMatrix::TestEvaluation( return ON_SUBD_RETURN_ERROR(rc_error); if ( F != sector_type.FaceCount()) return ON_SUBD_RETURN_ERROR(rc_error); - const unsigned int R = N + ((ON_SubD::FacetType::Quad == facet_type) ? F : 0); + const unsigned int R = N + F; if (R != sector_type.PointRingCount()) return ON_SUBD_RETURN_ERROR(rc_error); @@ -2905,13 +3210,12 @@ double ON_SubDMatrix::TestEvaluation( const ON_3dPoint* point_ringEnd = point_ring + R*(subd_recursion_count+1); unsigned int rc = ON_SubD::GetSectorPointRing( - subd_type, true, // bSubdivideIfNeeded, - component_ring_count, component_ring.Array(), + component_ring_count, + &point_ring[0].x, R, - 3, // point_ring_stride, - &point_ring[0].x + 3 // point_ring_stride, ); if (rc != R) @@ -2928,7 +3232,7 @@ double ON_SubDMatrix::TestEvaluation( const ON_SubDVertex* v0 = nullptr; for (unsigned int recursion_level = 0; recursion_level < subd_recursion_count; recursion_level++) { - const ON_SubDVertex* v1 = ON_SubD::SubdivideSector(subd_type, v0, component_ring_count, element, fsh[recursion_level%2]); + const ON_SubDVertex* v1 = ON_SubD::SubdivideSector( v0, element, component_ring_count, fsh[recursion_level%2]); if ( nullptr == v1 || N != v1->m_edge_count || F != v1->m_face_count) return ON_SUBD_RETURN_ERROR(rc_error); if (0 == recursion_level) @@ -2937,7 +3241,7 @@ double ON_SubDMatrix::TestEvaluation( { // subdivision was reqiured to get initial point ring. v0 = v1; - v1 = ON_SubD::SubdivideSector(subd_type, v0, component_ring_count, element, fsh[recursion_level % 2]); + v1 = ON_SubD::SubdivideSector( v0, element, component_ring_count, fsh[recursion_level % 2]); if (nullptr == v1 || N != v1->m_edge_count || F != v1->m_face_count) return ON_SUBD_RETURN_ERROR(rc_error); } @@ -2954,7 +3258,7 @@ double ON_SubDMatrix::TestEvaluation( if ( nullptr == v ) return ON_SUBD_RETURN_ERROR(rc_error); subd_points.AppendNew() = v->m_P; - if (ON_SubD::FacetType::Quad == facet_type && i < F) + if (i < F) { const ON_SubDFace* f = v1->Face(i); if ( nullptr == f ) @@ -2983,7 +3287,7 @@ double ON_SubDMatrix::TestEvaluation( for (unsigned int i = 0; i < R; i++) { ON_3dPoint P(ON_3dPoint::Origin); - if ( false == SM.EvaluateSubdivisionPoint(i,R,point_ring_stride,&point_ring1[0].x,P) ) + if ( false == SM.EvaluateSubdivisionPoint(i, &point_ring1[0].x, R,point_ring_stride,P) ) return ON_SUBD_RETURN_ERROR(rc_error); d = P.DistanceTo(point_ring2[i]); if (d > maxd) @@ -2994,11 +3298,11 @@ double ON_SubDMatrix::TestEvaluation( } // calculate limit point using ON_SubDVertex evaluation code - ON_SubDSectorLimitPoint center_vertex_limit_point = ON_SubDSectorLimitPoint::Unset; - center_vertex->GetLimitPoint(subd_type,sector_face,true,center_vertex_limit_point); + ON_SubDSectorSurfacePoint center_vertex_limit_point = ON_SubDSectorSurfacePoint::Unset; + center_vertex->GetSurfacePoint(sector_face,center_vertex_limit_point); // calculate limit point using matrix - if ( false == SM.EvaluateLimitPoint( R, point_ring_stride, &point_ring0[0].x, limit_point ) ) + if ( false == SM.EvaluateSurfacePoint(&point_ring0[0].x, R, point_ring_stride, false, limit_point ) ) return ON_SUBD_RETURN_ERROR(rc_error); // calculate limit point deviation (should be nearly zero) @@ -3767,4 +4071,3 @@ unsigned int ON_SubDMatrix_GetJordonDecomposition( } #endif -#endif diff --git a/opennurbs_subd_mesh.cpp b/opennurbs_subd_mesh.cpp index 084e1110..506f0622 100644 --- a/opennurbs_subd_mesh.cpp +++ b/opennurbs_subd_mesh.cpp @@ -26,14 +26,6 @@ //////////////////////////////////////////////////////////////// */ -void ON_SubD::ClearLimitSurfaceMesh() const -{ - const ON_SubDLevel* level = ActiveLevelConstPointer(); - - if ( nullptr != level ) - level->m_limit_mesh = ON_SubDLimitMesh::Empty; -} - bool ON_SubDFaceRegionBreakpoint( unsigned int level0_face_id, const class ON_SubDComponentRegionIndex& region_index @@ -111,7 +103,7 @@ const ON_SubDComponentRegion ON_SubDComponentRegion::Create( const ON_SubDComponentRegion ON_SubDComponentRegion::CreateSubdivisionRegion( ON_SubDComponentPtr::Type component_type, - bool bComponentMark, + bool bComponentDirection, unsigned short subdivision_count, bool bAssignTransientId ) @@ -119,7 +111,7 @@ const ON_SubDComponentRegion ON_SubDComponentRegion::CreateSubdivisionRegion( 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); + r.m_level0_component = ON_SubDComponentPtr::CreateNull(component_type, bComponentDirection); if (bAssignTransientId) { @@ -164,7 +156,7 @@ const ON_SubDComponentRegion ON_SubDComponentRegion::CreateSubdivisionRegion( //// : *this; ////} -int ON_SubDComponentRegion::CompareTypeIdMark( +int ON_SubDComponentRegion::CompareTypeIdDirection( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) @@ -185,7 +177,7 @@ int ON_SubDComponentRegion::CompareTypeIdMark( 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); + rc = (0 != lhs->m_level0_component.ComponentDirection() ? (int)1 : (int)0) - (0 != lhs->m_level0_component.ComponentDirection() ? (int)1 : (int)0); if (0 != rc) return rc; @@ -234,7 +226,7 @@ int ON_SubDComponentRegionIndex::CompareMinimumSubregion( return 0; } -int ON_SubDComponentRegion::CompareTypeIdMarkMinimumSubregion( +int ON_SubDComponentRegion::CompareTypeIdDirectionMinimumSubregion( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) @@ -242,14 +234,14 @@ int ON_SubDComponentRegion::CompareTypeIdMarkMinimumSubregion( if (lhs == rhs) return 0; - const int rc = ON_SubDComponentRegion::CompareTypeIdMark(lhs, rhs); + const int rc = ON_SubDComponentRegion::CompareTypeIdDirection(lhs, rhs); if (0 != rc) return rc; return ON_SubDComponentRegionIndex::CompareMinimumSubregion( &lhs->m_region_index, &rhs->m_region_index); } -int ON_SubDComponentRegion::CompareTypeIdMarkSubregion( +int ON_SubDComponentRegion::CompareTypeIdDirectionSubregion( const ON_SubDComponentRegion* lhs, const ON_SubDComponentRegion* rhs ) @@ -257,7 +249,7 @@ int ON_SubDComponentRegion::CompareTypeIdMarkSubregion( if (lhs == rhs) return 0; - int rc = ON_SubDComponentRegion::CompareTypeIdMark(lhs, rhs); + int rc = ON_SubDComponentRegion::CompareTypeIdDirection(lhs, rhs); if (0 == rc) { rc = ON_SubDComponentRegionIndex::CompareMinimumSubregion(&lhs->m_region_index, &rhs->m_region_index); @@ -282,7 +274,7 @@ int ON_SubDComponentRegion::Compare( if (lhs == rhs) return 0; - const int rc = ON_SubDComponentRegion::CompareTypeIdMarkSubregion(lhs, rhs); + const int rc = ON_SubDComponentRegion::CompareTypeIdDirectionSubregion(lhs, rhs); if (0 != rc) return rc; @@ -362,7 +354,7 @@ void ON_SubDComponentRegion::PushAdjusted( { if ( ON_SubDComponentPtr::Type::Edge == m_level0_component.ComponentType() - && 0 != m_level0_component.ComponentMark() + && 0 != m_level0_component.ComponentDirection() && region_index <= 1 ) { @@ -798,7 +790,7 @@ wchar_t* ON_SubDComponentPtr::ToString( break; case ON_SubDComponentPtr::Type::Edge: if ( s+2 < s1 ) - *s++ = (ComponentMark()) ? '-' : '+'; + *s++ = (ComponentDirection()) ? '-' : '+'; c = 'e'; break; case ON_SubDComponentPtr::Type::Face: @@ -1003,7 +995,7 @@ ON__UINT32 ON_SubDComponentRegionIndex::ToCompressedRegionIndex( void ON_SubDComponentRegionIndex::FromCompressedRegionIndex( ON__UINT32 compressed_region_index, unsigned short* subdivision_count, - unsigned short region_index[] + unsigned short* region_index ) { const ON__UINT32 count = (compressed_region_index >> 24); @@ -1125,7 +1117,7 @@ static bool Internal_Seal3d(const double* src, double* dst, double tol ) } -bool ON_SubDLimitMeshFragment::SealPoints( +bool ON_SubDMeshFragment::SealPoints( bool bTestNearEqual, const double* src, double* dst @@ -1139,7 +1131,7 @@ bool ON_SubDLimitMeshFragment::SealPoints( return true; } -bool ON_SubDLimitMeshFragment::SealNormals( +bool ON_SubDMeshFragment::SealNormals( bool bTestNearEqual, const double* src, double* dst @@ -1153,13 +1145,13 @@ bool ON_SubDLimitMeshFragment::SealNormals( return true; } -bool ON_SubDLimitMeshFragment::SealAdjacentSides( +bool ON_SubDMeshFragment::SealAdjacentSides( bool bTestNearEqual, bool bCopyNormals, - const ON_SubDLimitMeshFragment& src_fragment, + const ON_SubDMeshFragment& src_fragment, unsigned int i0, unsigned int i1, - ON_SubDLimitMeshFragment& dst_fragment, + ON_SubDMeshFragment& dst_fragment, unsigned int j0, unsigned int j1 ) @@ -1196,7 +1188,7 @@ bool ON_SubDLimitMeshFragment::SealAdjacentSides( { src = &src_fragment.m_P[src_fragment.m_grid.m_S[i]*src_stride]; dst = &dst_fragment.m_P[dst_fragment.m_grid.m_S[j]*dst_stride]; - if (false == ON_SubDLimitMeshFragment::SealPoints(bTestNearEqual,src,dst)) + if (false == ON_SubDMeshFragment::SealPoints(bTestNearEqual,src,dst)) { ON_SUBD_ERROR("Point locations failed near equal test."); return false; @@ -1212,7 +1204,7 @@ bool ON_SubDLimitMeshFragment::SealAdjacentSides( { src = &src_fragment.m_N[src_fragment.m_grid.m_S[i] * src_stride]; dst = &dst_fragment.m_N[dst_fragment.m_grid.m_S[j] * dst_stride]; - if (false == ON_SubDLimitMeshFragment::SealNormals(bTestNearEqual,src,dst)) + if (false == ON_SubDMeshFragment::SealNormals(bTestNearEqual,src,dst)) { ON_SUBD_ERROR("Normal locations failed near equal test."); return false; @@ -1541,11 +1533,20 @@ ON_Mesh* ON_SubD::GetControlNetMesh( { unsigned int archive_id_partition[4] = {}; - level.SetArchiveId(archive_id_partition); + bool bLevelLinkedListIncreasingId[3] = {}; + level.SetArchiveId(*subdimple,archive_id_partition,bLevelLinkedListIncreasingId); + 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) + // Have to use idit because subd editing (deleting and then adding) can leave the level's linked lists + // with components in an order that is not increasing in id and it is critical that the next three for + // loops iterate the level's components in order of increasing id. + ON_SubDLevelComponentIdIterator idit; + + // must iterate vertices in order of increasing id + idit.Initialize(bLevelLinkedListIncreasingId[0], ON_SubDComponentPtr::Type::Vertex, *subdimple, level); + for (const ON_SubDVertex* vertex = idit.FirstVertex(); nullptr != vertex; vertex = idit.NextVertex()) { unsigned int vi = vertex->ArchiveId(); if (vi < 1 || vi > subd_vertex_count) @@ -1561,7 +1562,9 @@ ON_Mesh* ON_SubD::GetControlNetMesh( 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) + // must iterate faces in order of increasing id + idit.Initialize(bLevelLinkedListIncreasingId[2], ON_SubDComponentPtr::Type::Face, *subdimple, level); + for (const ON_SubDFace* face = idit.FirstFace(); nullptr != face; face = idit.NextFace()) { ON_MeshFace meshf = {}; @@ -1599,7 +1602,7 @@ ON_Mesh* ON_SubD::GetControlNetMesh( else { ON_3dPoint center_point; - if (false == face->GetSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark, true, center_point)) + if (false == face->GetSubdivisionPoint( center_point)) continue; ON_2udex ngon_span = { mesh->m_F.UnsignedCount(), 0 }; @@ -1728,10 +1731,8 @@ void ON_SubD::ClearEvaluationCache() const if (nullptr != level) { - level->ClearEdgeFlags(); - level->ClearBoundingBox(); - level->ClearSubdivisonAndLimitPoints(); - level->m_limit_mesh = ON_SubDLimitMesh::Empty; + const_cast(this)->ChangeContentSerialNumberForExperts(); + level->ClearEvaluationCache(); } } diff --git a/opennurbs_subd_ref.cpp b/opennurbs_subd_ref.cpp index 8c0afc93..eee2eb5f 100644 --- a/opennurbs_subd_ref.cpp +++ b/opennurbs_subd_ref.cpp @@ -26,8 +26,6 @@ //////////////////////////////////////////////////////////////// */ -#if defined(OPENNURBS_SUBD_WIP) - ////////////////////////////////////////////////////////////////////////// // // ON_SubDRef @@ -89,7 +87,6 @@ class ON_SubD& ON_SubDRef::NewSubD() { ON_SubD* subd = new ON_SubD(); ON_SubD* managed_subd = SetSubDForExperts(subd); - managed_subd->SetSubDType(ON_SubD::SubDType::QuadCatmullClark); return *managed_subd; } @@ -166,6 +163,8 @@ ON_SubDComponentRef::ON_SubDComponentRef(const ON_SubDComponentRef& src) ON_NOEX , m_subd_ref(src.m_subd_ref) , m_component_ptr(src.m_component_ptr) , m_component_index(src.m_component_index) + , m_component_location(src.m_component_location) + , m_reference_id(src.m_reference_id) {} ON_SubDComponentRef& ON_SubDComponentRef::operator=(const ON_SubDComponentRef& src) @@ -176,6 +175,8 @@ ON_SubDComponentRef& ON_SubDComponentRef::operator=(const ON_SubDComponentRef& s m_subd_ref = src.m_subd_ref; m_component_ptr = src.m_component_ptr; m_component_index = src.m_component_index; + m_component_location = src.m_component_location; + m_reference_id = src.m_reference_id; } return *this; } @@ -186,6 +187,8 @@ ON_SubDComponentRef::ON_SubDComponentRef( ON_SubDComponentRef&& src ) ON_NOEXCEP , m_subd_ref(std::move(src.m_subd_ref)) , m_component_ptr(src.m_component_ptr) , m_component_index(src.m_component_index) + , m_component_location(src.m_component_location) + , m_reference_id(src.m_reference_id) {} ON_SubDComponentRef& ON_SubDComponentRef::operator=(ON_SubDComponentRef&& src) @@ -197,6 +200,8 @@ ON_SubDComponentRef& ON_SubDComponentRef::operator=(ON_SubDComponentRef&& src) m_subd_ref = std::move(src.m_subd_ref); m_component_ptr = src.m_component_ptr; m_component_index = src.m_component_index; + m_component_location = src.m_component_location; + m_reference_id = src.m_reference_id; } return *this; } @@ -232,24 +237,27 @@ int ON_SubDComponentRef::Compare2(const ON_SubDComponentRef* const* lhs, const O return ON_SubDComponentRef::Compare(*lhs, *rhs); } -ON_SubDComponentRef ON_SubDComponentRef::Create( +const ON_SubDComponentRef ON_SubDComponentRef::Create( const ON_SubDRef& subd_ref, ON_COMPONENT_INDEX component_index, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ) { ON_SubDComponentPtr component_ptr = subd_ref.SubD().ComponentPtrFromComponentIndex(component_index); - return ON_SubDComponentRef::Create(subd_ref,component_ptr,component_location); + return ON_SubDComponentRef::Create(subd_ref,component_ptr,component_location,reference_id); } -ON_SubDComponentRef ON_SubDComponentRef::Create( +const ON_SubDComponentRef ON_SubDComponentRef::Create( const ON_SubDRef& subd_ref, ON_SubDComponentPtr component_ptr, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ) { ON_SubDComponentRef component_ref; component_ref.m_subd_ref = subd_ref; + component_ref.m_reference_id = reference_id; bool bValidInput = false; switch (component_ptr.ComponentType()) { @@ -302,31 +310,34 @@ ON_SubDComponentRef ON_SubDComponentRef::Create( return ON_SUBD_RETURN_ERROR(component_ref); } -ON_SubDComponentRef ON_SubDComponentRef::Create( +const ON_SubDComponentRef ON_SubDComponentRef::Create( const ON_SubDRef& subd_ref, const class ON_SubDVertex* vertex, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ) { - return ON_SubDComponentRef::Create(subd_ref, ON_SubDComponentPtr::Create(vertex),component_location); + return ON_SubDComponentRef::Create(subd_ref, ON_SubDComponentPtr::Create(vertex),component_location,reference_id); } -ON_SubDComponentRef ON_SubDComponentRef::Create( +const ON_SubDComponentRef ON_SubDComponentRef::Create( const ON_SubDRef& subd_ref, const class ON_SubDEdge* edge, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ) { - return ON_SubDComponentRef::Create(subd_ref, ON_SubDComponentPtr::Create(edge),component_location); + return ON_SubDComponentRef::Create(subd_ref, ON_SubDComponentPtr::Create(edge),component_location,reference_id); } -ON_SubDComponentRef ON_SubDComponentRef::Create( +const ON_SubDComponentRef ON_SubDComponentRef::Create( const ON_SubDRef& subd_ref, const class ON_SubDFace* face, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ) { - return ON_SubDComponentRef::Create(subd_ref, ON_SubDComponentPtr::Create(face),component_location); + return ON_SubDComponentRef::Create(subd_ref, ON_SubDComponentPtr::Create(face),component_location,reference_id); } void ON_SubDComponentRef::Clear() @@ -336,6 +347,7 @@ void ON_SubDComponentRef::Clear() m_subd_ref.Clear(); m_component_ptr = ON_SubDComponentPtr::Null; m_component_index = ON_COMPONENT_INDEX::UnsetComponentIndex; + m_reference_id = 0; } ON_SubDRef ON_SubDComponentRef::SubDRef() const @@ -378,11 +390,23 @@ const class ON_SubDFace* ON_SubDComponentRef::Face() const return m_component_ptr.Face(); } +ON__UINT_PTR ON_SubDComponentRef::ReferenceId() const +{ + return m_reference_id; +} + +void ON_SubDComponentRef::SetReferenceId( + ON__UINT_PTR reference_id +) +{ + m_reference_id = reference_id; +} + bool ON_SubDComponentRef::IsValid(ON_TextLog* text_log) const { return ( m_component_ptr.IsNotNull() - && (ON_SubDComponentLocation::ControlNet == m_component_location || ON_SubDComponentLocation::LimitSurface == m_component_location) + && (ON_SubDComponentLocation::ControlNet == m_component_location || ON_SubDComponentLocation::Surface == m_component_location) && false == SubD().IsEmpty() ); } @@ -429,7 +453,7 @@ bool ON_SubDComponentRef::GetBBox( break; switch (m_component_location) { - case ON_SubDComponentLocation::LimitSurface: + case ON_SubDComponentLocation::Surface: // public opennubs does not provide limit mesh tools. case ON_SubDComponentLocation::ControlNet: bbox = vertex->ControlNetBoundingBox(); @@ -444,7 +468,7 @@ bool ON_SubDComponentRef::GetBBox( break; switch (m_component_location) { - case ON_SubDComponentLocation::LimitSurface: + case ON_SubDComponentLocation::Surface: // public opennubs does not provide limit mesh tools. case ON_SubDComponentLocation::ControlNet: bbox = edge->ControlNetBoundingBox(); @@ -459,7 +483,7 @@ bool ON_SubDComponentRef::GetBBox( break; switch (m_component_location) { - case ON_SubDComponentLocation::LimitSurface: + case ON_SubDComponentLocation::Surface: // public opennubs does not provide limit mesh tools. case ON_SubDComponentLocation::ControlNet: bbox = face->ControlNetBoundingBox(); @@ -593,7 +617,7 @@ bool ON_SubDComponentRefList::Internal_UpdateCount(const ON_SubDComponentRef& r, switch (e->m_edge_tag) { case ON_SubD::EdgeTag::Smooth: - case ON_SubD::EdgeTag::X: + case ON_SubD::EdgeTag::SmoothX: m_subd_edge_smooth_count += i; rc = true; break; @@ -617,7 +641,12 @@ bool ON_SubDComponentRefList::Internal_UpdateCount(const ON_SubDComponentRef& r, return rc; } -const ON_SubDComponentRef& ON_SubDComponentRefList::Append(const ON_SubDRef & subd_ref, ON_COMPONENT_INDEX ci, ON_SubDComponentLocation component_location) +const ON_SubDComponentRef& ON_SubDComponentRefList::Append( + const ON_SubDRef & subd_ref, + ON_COMPONENT_INDEX ci, + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id + ) { for (;;) { @@ -625,13 +654,18 @@ const ON_SubDComponentRef& ON_SubDComponentRefList::Append(const ON_SubDRef & su break; if (false == ci.IsSubDComponentIndex()) break; - const ON_SubDComponentRef r(ON_SubDComponentRef::Create(subd_ref, ci, component_location)); + const ON_SubDComponentRef r(ON_SubDComponentRef::Create(subd_ref, ci, component_location, reference_id)); return Append(&r); } return ON_SubDComponentRef::Empty; } -const ON_SubDComponentRef& ON_SubDComponentRefList::Append(const ON_SubDRef & subd_ref, ON_SubDComponentPtr component_ptr, ON_SubDComponentLocation component_location) +const ON_SubDComponentRef& ON_SubDComponentRefList::Append( + const ON_SubDRef & subd_ref, + ON_SubDComponentPtr component_ptr, + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id +) { for (;;) { @@ -639,12 +673,17 @@ const ON_SubDComponentRef& ON_SubDComponentRefList::Append(const ON_SubDRef & su break; if (false == component_ptr.IsNull()) break; - const ON_SubDComponentRef r(ON_SubDComponentRef::Create(subd_ref, component_ptr, component_location)); + const ON_SubDComponentRef r(ON_SubDComponentRef::Create(subd_ref, component_ptr, component_location,reference_id)); return Append(&r); } return ON_SubDComponentRef::Empty; } +const ON_SubDComponentRef& ON_SubDComponentRefList::Append(const ON_SubDComponentRef& src_ref) +{ + return Append(&src_ref); +} + const ON_SubDComponentRef& ON_SubDComponentRefList::Append(const ON_SubDComponentRef* src_ref) { for (;;) @@ -682,14 +721,15 @@ const ON_SubDComponentRef& ON_SubDComponentRefList::AppendForExperts(ON_SubDComp const ON_SubDComponentRef& ON_SubDComponentRefList::AppendForExperts( const ON_SubD& subd, ON_COMPONENT_INDEX ci, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ) { for (;;) { if (subd.IsEmpty()) break; - return Append(ON_SubDRef::CreateReferenceForExperts(subd),ci,component_location); + return Append(ON_SubDRef::CreateReferenceForExperts(subd),ci,component_location,reference_id); } return ON_SubDComponentRef::Empty; } @@ -697,14 +737,15 @@ const ON_SubDComponentRef& ON_SubDComponentRefList::AppendForExperts( const ON_SubDComponentRef& ON_SubDComponentRefList::AppendForExperts( const ON_SubD& subd, ON_SubDComponentPtr component_ptr, - ON_SubDComponentLocation component_location + ON_SubDComponentLocation component_location, + ON__UINT_PTR reference_id ) { for (;;) { if (subd.IsEmpty()) break; - return Append(ON_SubDRef::CreateReferenceForExperts(subd),component_ptr,component_location); + return Append(ON_SubDRef::CreateReferenceForExperts(subd),component_ptr,component_location,reference_id); } return ON_SubDComponentRef::Empty; } @@ -851,6 +892,3 @@ int ON_SubDComponentRefList::ComponentCount() const { return m_list.Count(); } - - -#endif diff --git a/opennurbs_subd_ring.cpp b/opennurbs_subd_ring.cpp index 774ff415..789e2330 100644 --- a/opennurbs_subd_ring.cpp +++ b/opennurbs_subd_ring.cpp @@ -26,101 +26,14 @@ //////////////////////////////////////////////////////////////// */ -////unsigned int ON_SubD::SectorEdgeCount( -//// const class ON_SubDSectorIterator& sit -//// ) -////{ -//// ON_SubDSectorIterator sit_local(sit); -//// const ON_SubDVertex* center_vertex = sit_local.CenterVertex(); -//// if ( nullptr == center_vertex ) -//// return ON_SUBD_RETURN_ERROR(0); -//// if ( 0 == center_vertex->m_edge_count && 0 == center_vertex->m_face_count ) -//// return 0; // not an error -//// if ( center_vertex->m_edge_count < 2 || center_vertex->m_face_count < 1 || nullptr == center_vertex->m_edges || nullptr == center_vertex->m_faces ) -//// return ON_SUBD_RETURN_ERROR(0); -//// const ON_SubDFace* face0 = sit_local.CurrentFace(); -//// const ON_SubDEdge* edge0 = sit_local.CurrentEdge(0); -//// const ON_SubDEdge* e1 = sit_local.CurrentEdge(1); -//// if ( nullptr == face0 || nullptr == edge0 || nullptr == e1 || edge0 == e1 ) -//// return ON_SUBD_RETURN_ERROR(0); -//// -//// const ON_SubDFace* f0 = face0; -//// const unsigned int N = center_vertex->m_edge_count; -//// unsigned int edge_count = 0; -//// while (edge_count <= N) // edge_count <= N used to prevent infinite recursion on damaged topology -//// { -//// const ON_SubDFace* f1 = sit_local.NextFace(true); -//// if (f0 == f1 || f1 == face0) -//// return ON_SUBD_RETURN_ERROR(0); -//// const ON_SubDEdge* e0 = sit_local.CurrentEdge(0); -//// if (e0 != e1) -//// return ON_SUBD_RETURN_ERROR(0); -//// if (f0 == face0 && e0 == edge0) -//// return edge_count; // back to where we started -//// -//// edge_count++; -//// e1 = sit_local.CurrentEdge(1); -//// if (e0 == e1) -//// return ON_SUBD_RETURN_ERROR(0); -//// -//// if (e1 == edge0) -//// { -//// // edge0 should be a crease and center_vertex should be a dart -//// if (ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) -//// { -//// f1 = sit_local.NextFace(true); -//// if (nullptr == f1) -//// return edge_count; -//// } -//// return ON_SUBD_RETURN_ERROR(0); -//// } -//// -//// if (nullptr == e1) -//// { -//// // e0 should be a crease and f1 should be null -//// if ( ON_SubD::EdgeTag::Crease != e0->m_edge_tag && 2 == e0->m_face_count) -//// return ON_SUBD_RETURN_ERROR(0); -//// -//// if (nullptr != f1) -//// return ON_SUBD_RETURN_ERROR(0); -//// -//// sit_local = sit; -//// f0 = face0; -//// e1 = edge0; -//// while (edge_count <= N) -//// { -//// f1 = sit_local.PrevFace(true); -//// e0 = sit_local.CurrentEdge(1); -//// if (e0 != e1) -//// return ON_SUBD_RETURN_ERROR(0); -//// e1 = sit_local.CurrentEdge(0); -//// if (nullptr == e1) -//// { -//// // e0 should be a crease, e1 and f1 should be null -//// if ( nullptr == f1) -//// return edge_count; // hit matching crease -//// return ON_SUBD_RETURN_ERROR(0); -//// } -//// if ( e1 == edge0) -//// return ON_SUBD_RETURN_ERROR(0); -//// edge_count++; -//// } -//// return ON_SUBD_RETURN_ERROR(0); // damaged topology -//// } -//// } -//// -//// return ON_SUBD_RETURN_ERROR(0); // damaged topology -////} - unsigned int ON_SubD::GetQuadSectorPointRing( - ON_SubD::SubDType subd_type, bool bFirstPass, bool bSecondPass, const ON_SubDVertex* center_vertex, - size_t component_ring_count, const ON_SubDComponentPtr* component_ring, - size_t point_ring_stride, - double* point_ring + size_t component_ring_count, + double* point_ring, + size_t point_ring_stride ) { //// NO VALIDATION CHECKS @@ -167,7 +80,7 @@ unsigned int ON_SubD::GetQuadSectorPointRing( if ( false == bSecondPass) return ON_SUBD_RETURN_ERROR(0); // subdivision not permitted - if (false == vertex0->GetSubdivisionPoint(subd_type, bUseSavedSubdivisionPoint, subP)) + if (false == vertex0->GetSubdivisionPoint(subP)) return ON_SUBD_RETURN_ERROR(0); Q = subP; @@ -191,7 +104,7 @@ unsigned int ON_SubD::GetQuadSectorPointRing( if (0 == pass) { - if (ON_SubD::EdgeTag::X == edge->m_edge_tag) + if (ON_SubD::EdgeTag::SmoothX == edge->m_edge_tag) break; // need to use subdivision point in 2nd pass if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag || ON_SubD::EdgeTag::Crease == edge->m_edge_tag @@ -205,7 +118,7 @@ unsigned int ON_SubD::GetQuadSectorPointRing( } else { - if (false == edge->GetSubdivisionPoint(subd_type, bUseSavedSubdivisionPoint, subP)) + if (false == edge->GetSubdivisionPoint(subP)) return ON_SUBD_RETURN_ERROR(0); Q = subP; } @@ -262,7 +175,7 @@ unsigned int ON_SubD::GetQuadSectorPointRing( } else { - if (false == face->GetSubdivisionPoint(subd_type, bUseSavedSubdivisionPoint, subP)) + if (false == face->GetSubdivisionPoint(subP)) return ON_SUBD_RETURN_ERROR(0); } P[0] = Q[0]; @@ -281,129 +194,6 @@ unsigned int ON_SubD::GetQuadSectorPointRing( return ON_SUBD_RETURN_ERROR(0); } - -unsigned int ON_SubD::GetTriSectorPointRing( - ON_SubD::SubDType subd_type, - bool bFirstPass, - bool bSecondPass, - const ON_SubDVertex* vertex0, - size_t component_ring_count, - const ON_SubDComponentPtr* component_ring, - size_t point_ring_stride, - double* point_ring - ) -{ - //// NO VALIDATION CHECKS - //// CALLER INSURES INPUT HAS NON-nullptr POINTERS AND CORRECT COUNTS - - double subP[3]; - const double* Q = nullptr; - - const unsigned int N = ON_SubD::ComponentRingEdgeCount(component_ring_count); - const unsigned int point_ring_count = 1 + N; - const double* point_ring1 = point_ring + (point_ring_count*point_ring_stride); - - const bool bUseSavedSubdivisionPoint = bSecondPass; - - for (unsigned int pass = (bFirstPass ? 0U : 1U); pass < (bSecondPass ? 2U : 1U); pass++) - { - double* P = point_ring; - - const ON_SubDEdgePtr* edges; - const ON_SubDFacePtr* faces; - size_t element_stride; - - if (nullptr != vertex0) - { - edges = vertex0->m_edges; - faces = (const ON_SubDFacePtr*)(vertex0->m_faces); - element_stride = 1; - } - else - { - vertex0 = component_ring[0].Vertex(); - edges = (const ON_SubDEdgePtr*)(component_ring+1); - faces = (const ON_SubDFacePtr*)(component_ring+2); - element_stride = 2; - } - - if (0 == pass) - Q = vertex0->m_P; - else - { - if ( false == bSecondPass) - return ON_SUBD_RETURN_ERROR(0); // subdivision not permitted - - if (false == vertex0->GetSubdivisionPoint(subd_type, bUseSavedSubdivisionPoint, subP)) - return ON_SUBD_RETURN_ERROR(0); - - Q = subP; - } - P[0] = Q[0]; - P[1] = Q[1]; - P[2] = Q[2]; - P += point_ring_stride; - - for (unsigned int i = 0; i < N; i++, edges += element_stride, faces += element_stride) - { - // Get edge point - ON__UINT_PTR eptr = edges->m_ptr; - const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr); - if (nullptr == edge) - return ON_SUBD_RETURN_ERROR(0); - eptr = 1 - ON_SUBD_EDGE_DIRECTION(eptr); - const ON_SubDVertex* vertex = edge->m_vertex[eptr]; - if (nullptr == vertex) - return ON_SUBD_RETURN_ERROR(0); - - if (0 == pass) - { - if (ON_SubD::EdgeTag::X == edge->m_edge_tag) - break; // need 2nd pass - if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag - || ON_SubD::EdgeTag::Crease == edge->m_edge_tag - || 0.5 == edge->m_sector_coefficient[eptr] - ) - { - Q = vertex->m_P; - } - else - { - break; // need 2nd pass - } - } - else - { - if (false == edge->GetSubdivisionPoint(subd_type, bUseSavedSubdivisionPoint, subP)) - return ON_SUBD_RETURN_ERROR(0); - Q = subP; - } - P[0] = Q[0]; - P[1] = Q[1]; - P[2] = Q[2]; - P += point_ring_stride; - - if (point_ring1 == P) - { - // success on a sector with crease boundary - return point_ring_count; - } - - const ON_SubDFace* face = ON_SUBD_FACE_POINTER(faces->m_ptr); - if (3 != face->m_edge_count) - return ON_SUBD_RETURN_ERROR(0); - } - - if (point_ring1 == P) - { - // success on a smooth sector - return point_ring_count; - } - } - - return ON_SUBD_RETURN_ERROR(0); -} - unsigned int ON_SubD::ComponentRingEdgeCount(size_t component_ring_count) { const unsigned int N @@ -424,8 +214,8 @@ unsigned int ON_SubD::ComponentRingFaceCount(size_t component_ring_count) } bool ON_SubD::ComponentRingIsValid( - size_t component_ring_count, - const ON_SubDComponentPtr* component_ring + const ON_SubDComponentPtr* component_ring, + size_t component_ring_count ) { if (nullptr == component_ring || component_ring_count < 4) @@ -457,14 +247,14 @@ bool ON_SubD::ComponentRingIsValid( const ON_SubDEdge* edge = component_ring[component_ring_index].Edge(); if ( nullptr == edge) return ON_SUBD_RETURN_ERROR(false); - if (vertex != edge->m_vertex[component_ring[component_ring_index].ComponentMark()]) + if (vertex != edge->m_vertex[component_ring[component_ring_index].ComponentDirection()]) return ON_SUBD_RETURN_ERROR(false); if (0 == i) { if (edge0_tag != edge->m_edge_tag) { - if ( ON_SubD::EdgeTag::Smooth != edge0_tag || ON_SubD::EdgeTag::X != edge->m_edge_tag ) + if ( ON_SubD::EdgeTag::Smooth != edge0_tag || ON_SubD::EdgeTag::SmoothX != edge->m_edge_tag ) return ON_SUBD_RETURN_ERROR(false); } } @@ -472,7 +262,7 @@ bool ON_SubD::ComponentRingIsValid( { if (edge1_tag != edge->m_edge_tag) { - if ( ON_SubD::EdgeTag::Smooth != edge1_tag || ON_SubD::EdgeTag::X != edge->m_edge_tag ) + if ( ON_SubD::EdgeTag::Smooth != edge1_tag || ON_SubD::EdgeTag::SmoothX != edge->m_edge_tag ) return ON_SUBD_RETURN_ERROR(false); } if ( ON_SubD::EdgeTag::Crease == edge1_tag) @@ -500,7 +290,6 @@ bool ON_SubD::ComponentRingIsValid( } unsigned int ON_SubD::GetSectorPointRing( - ON_SubD::SubDType subd_type, bool bSubdivideIfNeeded, size_t component_ring_count, const ON_SubDComponentPtr* component_ring, @@ -517,7 +306,7 @@ unsigned int ON_SubD::GetSectorPointRing( ON_3dPoint* point_ring_array = point_ring.Reserve(point_ring_capacity); if ( nullptr == point_ring_array) return ON_SUBD_RETURN_ERROR(0); - unsigned int point_ring_count = GetSectorPointRing(subd_type, bSubdivideIfNeeded, component_ring_count, component_ring, point_ring_capacity, 3, &point_ring_array[0].x); + unsigned int point_ring_count = GetSectorPointRing( bSubdivideIfNeeded, component_ring, component_ring_count, &point_ring_array[0].x, point_ring_capacity, 3); if (point_ring_count > 0) { point_ring.SetCount(point_ring_count); @@ -528,36 +317,26 @@ unsigned int ON_SubD::GetSectorPointRing( unsigned int ON_SubD::GetSectorSubdivsionPointRing( - ON_SubD::SubDType subd_type, - size_t component_ring_count, const ON_SubDComponentPtr* component_ring, + size_t component_ring_count, + double* subd_point_ring, size_t subd_point_ring_capacity, - size_t subd_point_ring_stride, - double* subd_point_ring + size_t subd_point_ring_stride ) { - if (false == ComponentRingIsValid(component_ring_count,component_ring)) + if (false == ComponentRingIsValid(component_ring,component_ring_count)) return ON_SUBD_RETURN_ERROR(0); - const unsigned int K = ON_SubD::FacetEdgeCount(subd_type); - if ( 3 != K && 4 != K ) - return ON_SUBD_RETURN_ERROR(0); const unsigned int N = ON_SubD::ComponentRingEdgeCount(component_ring_count); const unsigned int F = ON_SubD::ComponentRingFaceCount(component_ring_count); - const unsigned int point_ring_count = N + (K-3)*F; + const unsigned int point_ring_count = N + F; if ( point_ring_count > subd_point_ring_capacity || nullptr == subd_point_ring) return ON_SUBD_RETURN_ERROR(0); const bool bFirstPass = false; const bool bSecondPass = true; - unsigned int rc; - if ( 3 == K ) - rc = GetTriSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,subd_point_ring_stride,subd_point_ring); - else if ( 4 == K ) - rc = GetQuadSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,subd_point_ring_stride,subd_point_ring); - else - return ON_SUBD_RETURN_ERROR(0); + unsigned int rc = GetQuadSectorPointRing(bFirstPass,bSecondPass,nullptr,component_ring,component_ring_count, subd_point_ring, subd_point_ring_stride); if (0 == rc) return ON_SUBD_RETURN_ERROR(0); @@ -567,9 +346,8 @@ unsigned int ON_SubD::GetSectorSubdivsionPointRing( } unsigned int ON_SubD::GetSectorSubdivisionPointRing( - ON_SubD::SubDType subd_type, - size_t component_ring_count, const ON_SubDComponentPtr* component_ring, + size_t component_ring_count, ON_SimpleArray& subd_point_ring ) { @@ -583,7 +361,7 @@ unsigned int ON_SubD::GetSectorSubdivisionPointRing( ON_3dPoint* subd_point_ring_array = subd_point_ring.Reserve(subd_point_ring_capacity); if ( nullptr == subd_point_ring_array) return ON_SUBD_RETURN_ERROR(0); - unsigned int subd_point_ring_count = GetSectorSubdivsionPointRing(subd_type, component_ring_count, component_ring, subd_point_ring_capacity, 3, &subd_point_ring_array[0].x); + unsigned int subd_point_ring_count = GetSectorSubdivsionPointRing(component_ring, component_ring_count, &subd_point_ring_array[0].x, subd_point_ring_capacity, 3); if (subd_point_ring_count > 0) { subd_point_ring.SetCount(subd_point_ring_count); @@ -593,37 +371,26 @@ unsigned int ON_SubD::GetSectorSubdivisionPointRing( } unsigned int ON_SubD::GetSectorPointRing( - ON_SubD::SubDType subd_type, bool bSubdivideIfNeeded, - size_t component_ring_count, const ON_SubDComponentPtr* component_ring, + size_t component_ring_count, + double* point_ring, size_t point_ring_capacity, - size_t point_ring_stride, - double* point_ring + size_t point_ring_stride ) { - if (false == ComponentRingIsValid(component_ring_count,component_ring)) - return ON_SUBD_RETURN_ERROR(0); - - const unsigned int K = ON_SubD::FacetEdgeCount(subd_type); - if ( 3 != K && 4 != K ) + if (false == ComponentRingIsValid(component_ring,component_ring_count)) return ON_SUBD_RETURN_ERROR(0); const unsigned int N = ON_SubD::ComponentRingEdgeCount(component_ring_count); const unsigned int F = ON_SubD::ComponentRingFaceCount(component_ring_count); - const unsigned int point_ring_count = N + (K-3)*F; + const unsigned int point_ring_count = N + F; if ( point_ring_count > point_ring_capacity || nullptr == point_ring) return ON_SUBD_RETURN_ERROR(0); const bool bFirstPass = true; const bool bSecondPass = bSubdivideIfNeeded; - unsigned int rc; - if ( 3 == K ) - rc = GetTriSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,point_ring_stride,point_ring); - else if ( 4 == K ) - rc = GetQuadSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,point_ring_stride,point_ring); - else - return ON_SUBD_RETURN_ERROR(0); + unsigned int rc = GetQuadSectorPointRing(bFirstPass,bSecondPass,nullptr, component_ring,component_ring_count, point_ring,point_ring_stride); if (0 == rc) return ON_SUBD_RETURN_ERROR(0); @@ -633,18 +400,13 @@ unsigned int ON_SubD::GetSectorPointRing( unsigned int ON_SubD::GetSectorPointRing( - ON_SubD::SubDType subd_type, bool bSubdivideIfNeeded, const class ON_SubDSectorIterator& sit, + double* point_ring, size_t point_ring_capacity, - size_t point_ring_stride, - double* point_ring + size_t point_ring_stride ) { - const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); - if ( ON_SubD::FacetType::Quad != facet_type && ON_SubD::FacetType::Tri != facet_type ) - return ON_SUBD_RETURN_ERROR(0); - const ON_SubDVertex* center_vertex = sit.CenterVertex(); if ( nullptr == center_vertex ) return ON_SUBD_RETURN_ERROR(0); @@ -662,15 +424,12 @@ unsigned int ON_SubD::GetSectorPointRing( } unsigned int point_ring_count = 0; - unsigned int component_ring_count = ON_SubD::GetSectorComponentRing(sit,component_ring_capacity,component_ring); + unsigned int component_ring_count = ON_SubD::GetSectorComponentRing(sit, component_ring,component_ring_capacity); if (component_ring_count > 0) { const bool bFirstPass = true; const bool bSecondPass = bSubdivideIfNeeded; - point_ring_count = - ( ON_SubD::FacetType::Quad == facet_type ) - ? ON_SubD::GetQuadSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,point_ring_stride,point_ring) - : ON_SubD::GetTriSectorPointRing(subd_type,bFirstPass,bSecondPass,nullptr,component_ring_count,component_ring,point_ring_stride,point_ring); + point_ring_count = ON_SubD::GetQuadSectorPointRing( bFirstPass, bSecondPass, nullptr, component_ring, component_ring_count, point_ring, point_ring_stride); } if ( component_ring != stack_component_ring) @@ -680,7 +439,6 @@ unsigned int ON_SubD::GetSectorPointRing( } unsigned int ON_SubD::GetSectorPointRing( - ON_SubD::SubDType subd_type, bool bSubdivideIfNeeded, const class ON_SubDSectorIterator& sit, ON_SimpleArray& point_ring @@ -694,7 +452,7 @@ unsigned int ON_SubD::GetSectorPointRing( ON_3dPoint* point_ring_array = point_ring.Reserve(point_ring_capacity); if ( nullptr == point_ring_array) return ON_SUBD_RETURN_ERROR(0); - unsigned int point_ring_count = GetSectorPointRing(subd_type, bSubdivideIfNeeded, sit, point_ring_capacity, 3, &point_ring_array[0].x); + unsigned int point_ring_count = GetSectorPointRing( bSubdivideIfNeeded, sit, &point_ring_array[0].x, point_ring_capacity, 3); if (point_ring_count > 0) { point_ring.SetCount(point_ring_count); @@ -710,7 +468,7 @@ static double Subdivide_CenterVertexSectorWeight( { if ( ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) return ON_SubDSectorType::IgnoredSectorWeight; - if (ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag || ON_SubD::EdgeTag::X == edge0->m_edge_tag) + if (ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag || ON_SubD::EdgeTag::SmoothX == edge0->m_edge_tag) { if (vertex0 == edge0->m_vertex[0]) return edge0->m_sector_coefficient[0]; @@ -721,16 +479,19 @@ static double Subdivide_CenterVertexSectorWeight( } const ON_SubDVertex* ON_SubD::SubdivideSector( - ON_SubD::SubDType subd_type, const ON_SubDVertex* center_vertex, - size_t component_ring_count, const ON_SubDComponentPtr* component_ring, + size_t component_ring_count, ON_SubD_FixedSizeHeap& fsh ) { const unsigned int N = (nullptr != center_vertex) ? center_vertex->m_edge_count : ON_SubD::ComponentRingEdgeCount(component_ring_count); const unsigned int F = (nullptr != center_vertex) ? center_vertex->m_face_count : ON_SubD::ComponentRingFaceCount(component_ring_count); - + if ( N < 2 ) + return ON_SUBD_RETURN_ERROR(nullptr); + if ( F != N && F+1 != N ) + return ON_SUBD_RETURN_ERROR(nullptr); + size_t element_stride; const ON_SubDEdgePtr* edges; const ON_SubDFacePtr* faces; @@ -751,65 +512,74 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( edges = center_vertex->m_edges; faces = (const ON_SubDFacePtr*)(center_vertex->m_faces); element_stride = 1; - if ( F != N && F+1 != N ) - return ON_SUBD_RETURN_ERROR(nullptr); + } + + // smooth and dart sectors have F = N + // crease and corner have F = N-1 + if (F != (center_vertex->IsCreaseOrCorner() ? (N-1) : N) ) + { + return ON_SUBD_RETURN_ERROR(nullptr); } const ON_SubD::EdgeTag edge0_tag = (F+1 == N) ? ON_SubD::EdgeTag::Crease : ON_SubD::EdgeTag::Smooth; - const unsigned int face_edge_count = ON_SubD::FacetEdgeCount(subd_type); - if ( 3 != face_edge_count && 4 != face_edge_count ) - return ON_SUBD_RETURN_ERROR(nullptr); - const unsigned int K = face_edge_count-1; + //const unsigned int face_edge_count = 4; + const unsigned int K = 3; const ON_SubDEdge* edge0 = edges->Edge(); if ( nullptr == edge0) return ON_SUBD_RETURN_ERROR(nullptr); edges += element_stride; - if ( edge0_tag != edge0->m_edge_tag) - return ON_SUBD_RETURN_ERROR(nullptr); + if (ON_SubD::EdgeTag::Smooth == edge0_tag) + { + if (false == edge0->IsSmooth() ) + return ON_SUBD_RETURN_ERROR(nullptr); + } + else + { + if (edge0_tag != edge0->m_edge_tag) + return ON_SUBD_RETURN_ERROR(nullptr); + } const ON_SubDFace* face0 = faces->Face(); if ( nullptr == face0) return ON_SUBD_RETURN_ERROR(nullptr); edges += element_stride; - if ( false == fsh.ReserveSubDWorkspace(N+1,K*N,N,(3*K+1)*N)) + if ( false == fsh.ReserveSubDWorkspace(N) ) return ON_SUBD_RETURN_ERROR(nullptr); - const bool bUseSavedSubdivisionPoint = true; - ON_SubDVertex* v1[4] = {}; - ON_SubDEdge* e1[4] = {}; + ON_SubDEdgePtr e1[4] = {}; ON_SubDEdgePtr f1epts[4] = {}; const ON_SubDVertex* vertex0 = center_vertex; - v1[0] = fsh.AllocateVertex(vertex0,subd_type,bUseSavedSubdivisionPoint,N,N); + v1[0] = fsh.AllocateVertex(vertex0,N); if ( nullptr == v1[0]) return ON_SUBD_RETURN_ERROR(nullptr); //v1[0]->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::radial; - ON_SubDVertex* vertex1 = fsh.AllocateVertex(edge0,subd_type,bUseSavedSubdivisionPoint,3,2); + ON_SubDVertex* vertex1 = fsh.AllocateVertex(edge0); if ( nullptr == vertex1) return ON_SUBD_RETURN_ERROR(nullptr); // at_crease weight is used when the cooresponding vertex is a crease. // Otherwise, fsh.AllocateEdge() ignores at_crease_weight. - ON_SubD::EdgeTag edge1_tag = (ON_SubD::EdgeTag::X == edge0_tag) ? ON_SubD::EdgeTag::Smooth : edge0_tag; + ON_SubD::EdgeTag edge1_tag = (ON_SubD::EdgeTag::SmoothX == edge0_tag) ? ON_SubD::EdgeTag::Smooth : edge0_tag; const double at_crease_weight = ON_SubD::EdgeTag::Crease == edge1_tag - ? ON_SubDSectorType::CreaseSectorWeight(subd_type,5-K) + ? ON_SubDSectorType::CreaseSectorWeight(5-K) : ON_SubDSectorType::IgnoredSectorWeight; - ON_SubDEdge* edge1 = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0,vertex0), vertex1, ON_SubDSectorType::IgnoredSectorWeight ); - if ( nullptr == edge1) + ON_SubDEdgePtr edge1 = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0,vertex0), vertex1, ON_SubDSectorType::IgnoredSectorWeight ); + if (edge1.IsNull()) return ON_SUBD_RETURN_ERROR(nullptr); - edge1->m_edge_tag = edge1_tag; + edge1.Edge()->m_edge_tag = edge1_tag; v1[1] = vertex1; e1[0] = edge1; - f1epts[0] = ON_SubDEdgePtr::Create(e1[0],0); + f1epts[0] = e1[0]; edge1_tag = ON_SubD::EdgeTag::Smooth; for (unsigned int i = 1; i < N; i++, edges += element_stride, faces += element_stride) @@ -834,36 +604,26 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( if (nullptr == v1[K]) { - v1[K] = fsh.AllocateVertex(edge0, subd_type, bUseSavedSubdivisionPoint, 3, 2); + v1[K] = fsh.AllocateVertex(edge0); if (nullptr == v1[K]) return ON_SUBD_RETURN_ERROR(nullptr); e1[K] = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0, vertex0), v1[K], ON_SubDSectorType::IgnoredSectorWeight); - if (nullptr == e1[K]) + if (e1[K].IsNull()) return ON_SUBD_RETURN_ERROR(nullptr); - e1[K]->m_edge_tag = edge1_tag; + e1[K].Edge()->m_edge_tag = edge1_tag; } - f1epts[K] = ON_SubDEdgePtr::Create(e1[K],1); - if (3 == K) - { - // quads - v1[2] = fsh.AllocateVertex(face0, subd_type, bUseSavedSubdivisionPoint, 2, 1 ); - e1[1] = fsh.AllocateEdge(v1[1], at_crease_weight, v1[2], ON_SubDSectorType::IgnoredSectorWeight); - e1[2] = fsh.AllocateEdge(v1[2], ON_SubDSectorType::IgnoredSectorWeight, v1[3], at_crease_weight); - f1epts[1] = ON_SubDEdgePtr::Create(e1[1],0); - f1epts[2] = ON_SubDEdgePtr::Create(e1[2],0); - if (nullptr == fsh.AllocateQuad(face0->m_zero_face_id, face0->m_id, f1epts) ) - return ON_SUBD_RETURN_ERROR(nullptr); - } - else - { - // tris - e1[1] = fsh.AllocateEdge(v1[1], at_crease_weight, v1[2], at_crease_weight); - f1epts[1] = ON_SubDEdgePtr::Create(e1[1],0); - if (nullptr == fsh.AllocateTri(face0->m_zero_face_id, face0->m_id, f1epts) ) - return ON_SUBD_RETURN_ERROR(nullptr); - } - + f1epts[K] = e1[K].Reversed(); + + // quads + v1[2] = fsh.AllocateSectorFaceVertex(face0 ); + e1[1] = fsh.AllocateEdge(v1[1], at_crease_weight, v1[2], ON_SubDSectorType::IgnoredSectorWeight); + e1[2] = fsh.AllocateEdge(v1[2], ON_SubDSectorType::IgnoredSectorWeight, v1[3], at_crease_weight); + f1epts[1] = e1[1]; + f1epts[2] = e1[2]; + if (nullptr == fsh.AllocateQuad(face0->m_zero_face_id, face0->m_id, f1epts) ) + return ON_SUBD_RETURN_ERROR(nullptr); + if (i + 1 == N) { if (i + 1 == N && edge0_tag == edge1_tag) @@ -880,7 +640,7 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( e1[0] = e1[K]; f1epts[0] = f1epts[K].Reversed(); v1[K] = nullptr; - e1[K] = nullptr; + e1[K] = ON_SubDEdgePtr::Null; } return ON_SUBD_RETURN_ERROR(nullptr); @@ -888,8 +648,8 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( unsigned int ON_SubD::GetSectorComponentRing( const class ON_SubDSectorIterator& sit, - size_t component_ring_capacity, - ON_SubDComponentPtr* component_ring + ON_SubDComponentPtr* component_ring, + size_t component_ring_capacity ) { if ( component_ring_capacity < 4 || nullptr == component_ring) @@ -952,6 +712,12 @@ unsigned int ON_SubD::GetSectorComponentRing( if (nullptr == face && ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) return component_ring_count; // back to start dart case. } + if (ON_SubD::VertexTag::Corner == center_vertex_tag) + { + // occurs in nonmanifold cases like the one in RH-49843 + if (nullptr == face && ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) + return component_ring_count; // back to start corner case. + } } return ON_SUBD_RETURN_ERROR(false); // damaged topology information @@ -994,7 +760,7 @@ unsigned int ON_SubD::GetSectorComponentRing( return ON_SUBD_RETURN_ERROR(0); const unsigned int component_ring_capacity = 1 + vertex->m_edge_count + vertex->m_face_count; - unsigned int component_ring_count = ON_SubD::GetSectorComponentRing(sit, component_ring_capacity, elements.Reserve(component_ring_capacity)); + unsigned int component_ring_count = ON_SubD::GetSectorComponentRing(sit, elements.Reserve(component_ring_capacity), component_ring_capacity); if (component_ring_count >= 4 && component_ring_count <= component_ring_capacity) elements.SetCount(component_ring_count); diff --git a/opennurbs_subd_sector.cpp b/opennurbs_subd_sector.cpp index 1e7cbf0a..c5b61534 100644 --- a/opennurbs_subd_sector.cpp +++ b/opennurbs_subd_sector.cpp @@ -101,18 +101,6 @@ bool ON_SubDSectorType::IsValid() const if ( 0 == m_hash) return ON_SUBD_RETURN_ERROR(false); - switch (m_subd_type) - { - case ON_SubD::SubDType::TriLoopWarren: - case ON_SubD::SubDType::QuadCatmullClark: - case ON_SubD::SubDType::CustomTri: - case ON_SubD::SubDType::CustomQuad: - break; - default: - return ON_SUBD_RETURN_ERROR(false); - break; - } - if ( m_sector_face_count < ON_SubDSectorType::MinimumSectorFaceCount(m_vertex_tag)) return ON_SUBD_RETURN_ERROR(false); @@ -131,21 +119,21 @@ bool ON_SubDSectorType::IsValid() const case ON_SubD::VertexTag::Crease: if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle)) return ON_SUBD_RETURN_ERROR(false); - if (!(m_sector_weight == ON_SubDSectorType::CreaseSectorWeight(m_subd_type,m_sector_face_count))) + if (!(m_sector_weight == ON_SubDSectorType::CreaseSectorWeight(m_sector_face_count))) return ON_SUBD_RETURN_ERROR(false); break; case ON_SubD::VertexTag::Corner: - if (!(m_corner_sector_angle_radians > 0.0 && m_corner_sector_angle_radians < 2.0*ON_PI)) + if (!(m_corner_sector_angle_radians > 0.0 && m_corner_sector_angle_radians < ON_2PI)) return ON_SUBD_RETURN_ERROR(false); - if (!(m_sector_weight == ON_SubDSectorType::CornerSectorWeight(m_subd_type,m_sector_face_count,m_corner_sector_angle_radians))) + if (!(m_sector_weight == ON_SubDSectorType::CornerSectorWeight(m_sector_face_count,m_corner_sector_angle_radians))) return ON_SUBD_RETURN_ERROR(false); break; case ON_SubD::VertexTag::Dart: if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle)) return ON_SUBD_RETURN_ERROR(false); - if (!(m_sector_weight == ON_SubDSectorType::DartSectorWeight(m_subd_type,m_sector_face_count))) + if (!(m_sector_weight == ON_SubDSectorType::DartSectorWeight(m_sector_face_count))) return ON_SUBD_RETURN_ERROR(false); break; @@ -162,30 +150,10 @@ ON_SubD::VertexTag ON_SubDSectorType::VertexTag() const return m_vertex_tag; } -ON_SubD::SubDType ON_SubDSectorType::SubDType() const -{ - return m_subd_type; -} - -ON_SubD::FacetType ON_SubDSectorType::FacetType() const -{ - return ON_SubD::FacetTypeFromSubDType(m_subd_type); -} unsigned int ON_SubDSectorType::FacetEdgeCount() const { - switch (FacetType()) - { - case ON_SubD::FacetType::Tri: - return 3; - break; - case ON_SubD::FacetType::Quad: - return 4; - break; - default: - break; - } - return 0; + return 4; } double ON_SubDSectorType::CornerSectorAngleRadians() const @@ -220,17 +188,17 @@ bool ON_SubDSectorType::IsCreaseSector() const bool ON_SubDSectorType::IsCornerSector() const { - return (m_vertex_tag == ON_SubD::VertexTag::Corner && m_corner_sector_angle_index <= ON_SubDSectorType::MaximumAngleIndex); + return (m_vertex_tag == ON_SubD::VertexTag::Corner && m_corner_sector_angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex); } bool ON_SubDSectorType::IsConvexCornerSector() const { - return (m_vertex_tag == ON_SubD::VertexTag::Corner && 2*m_corner_sector_angle_index >= ON_SubDSectorType::MaximumAngleIndex); + return (m_vertex_tag == ON_SubD::VertexTag::Corner && 2*m_corner_sector_angle_index >= ON_SubDSectorType::MaximumCornerAngleIndex); } bool ON_SubDSectorType::IsConcaveCornerSector() const { - return (m_vertex_tag == ON_SubD::VertexTag::Corner && 2*m_corner_sector_angle_index <= ON_SubDSectorType::MaximumAngleIndex); + return (m_vertex_tag == ON_SubD::VertexTag::Corner && 2*m_corner_sector_angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex); } @@ -279,7 +247,6 @@ unsigned int ON_SubDSectorType::ComponentRingCount() const unsigned int ON_SubDSectorType::PointRingCount() const { return ON_SubDSectorType::SectorPointRingCountFromFaceCount( - m_subd_type, m_vertex_tag, m_sector_face_count ); @@ -299,23 +266,32 @@ double ON_SubDSectorType::DartSectorTheta( ) { if (sector_face_count >= 2) - return (2.0*ON_PI / ((double)sector_face_count)); + return (ON_2PI / ((double)sector_face_count)); return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorTheta); } -unsigned int ON_SubDSectorType::AngleIndexFromAngleRadians( +unsigned int ON_SubDSectorType::CornerAngleIndexFromCornerAngleRadians( double corner_sector_angle_radians ) { corner_sector_angle_radians = ON_SubDSectorType::ClampCornerSectorAngleRadians(corner_sector_angle_radians); if (ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians)) { - const double max_index = ON_SubDSectorType::MaximumAngleIndex; - unsigned int i = (unsigned int)floor(max_index*(corner_sector_angle_radians / (2.0*ON_PI))); - if (i < ON_SubDSectorType::MaximumAngleIndex) + if (corner_sector_angle_radians <= ON_SubDSectorType::MinimumCornerAngleRadians) + return 1; + if (corner_sector_angle_radians >= ON_SubDSectorType::MaximumCornerAngleRadians) + return ON_SubDSectorType::MaximumCornerAngleIndex - 1; + + const double max_index = ON_SubDSectorType::MaximumCornerAngleIndex; + unsigned int i = (unsigned int)floor(max_index*(corner_sector_angle_radians / ON_2PI)); + if (i >= ON_SubDSectorType::MaximumCornerAngleIndex - 1) + i = ON_SubDSectorType::MaximumCornerAngleIndex - 1; + else if (i < 1) + i = 1; + else if (i < ON_SubDSectorType::MaximumCornerAngleIndex-1) { - double a0 = ON_SubDSectorType::AngleRadiansFromAngleIndex(i); - double a1 = ON_SubDSectorType::AngleRadiansFromAngleIndex(i+1); + double a0 = ON_SubDSectorType::AngleRadiansFromCornerAngleIndex(i); + double a1 = ON_SubDSectorType::AngleRadiansFromCornerAngleIndex(i+1); double d0 = fabs(a0 - corner_sector_angle_radians); double d1 = fabs(a1 - corner_sector_angle_radians); if (d1 < d0) @@ -326,14 +302,61 @@ unsigned int ON_SubDSectorType::AngleIndexFromAngleRadians( return ON_SUBD_RETURN_ERROR(ON_UNSET_UINT_INDEX); } -double ON_SubDSectorType::AngleRadiansFromAngleIndex( +double ON_SubDSectorType::AngleRadiansFromCornerAngleIndex( unsigned int corner_sector_angle_index ) { - if (corner_sector_angle_index <= ON_SubDSectorType::MaximumAngleIndex) + if (corner_sector_angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex) { - const double max_index = ON_SubDSectorType::MaximumAngleIndex; - return (corner_sector_angle_index / max_index)*2.0*ON_PI; + //const double max_index = ON_SubDSectorType::MaximumCornerAngleIndex; + double corner_angle_radians; + switch (corner_sector_angle_index) + { + case 0: + corner_angle_radians = 0.0; + break; + case (ON_SubDSectorType::MaximumCornerAngleIndex/12): + corner_angle_radians = ON_PI/6.0; + break; + case (ON_SubDSectorType::MaximumCornerAngleIndex/8): + corner_angle_radians = 0.25*ON_PI; + break; + case (ON_SubDSectorType::MaximumCornerAngleIndex/6): + corner_angle_radians = ON_PI/3.0; + break; + case (ON_SubDSectorType::MaximumCornerAngleIndex/4): + corner_angle_radians = 0.5*ON_PI; + break; + case (ON_SubDSectorType::MaximumCornerAngleIndex/3): + corner_angle_radians = ON_2PI/3.0; + break; + case (3*ON_SubDSectorType::MaximumCornerAngleIndex*8): + corner_angle_radians = 0.75*ON_PI; + break; + case (5*ON_SubDSectorType::MaximumCornerAngleIndex/12): + corner_angle_radians = (5.0*ON_PI)/6.0; + break; + case (ON_SubDSectorType::MaximumCornerAngleIndex/2): + corner_angle_radians = ON_PI; + break; + case (5*ON_SubDSectorType::MaximumCornerAngleIndex/8): + corner_angle_radians = 1.25*ON_PI; + break; + case (3*ON_SubDSectorType::MaximumCornerAngleIndex/2): + corner_angle_radians = 1.5*ON_PI; + break; + case (7*ON_SubDSectorType::MaximumCornerAngleIndex/8): + corner_angle_radians = 1.75*ON_PI; + break; + case ON_SubDSectorType::MaximumCornerAngleIndex: + corner_angle_radians = ON_2PI; + break; + + default: + corner_angle_radians = ((double)(corner_sector_angle_index))*ON_SubDSectorType::MinimumCornerAngleRadians; + break; + } + return corner_angle_radians; } return ON_SUBD_RETURN_ERROR(ON_UNSET_VALUE); } @@ -348,15 +371,15 @@ double ON_SubDSectorType::CornerSectorThetaFromCornerAngle( && sector_face_count <= ON_SubDVertex::MaximumFaceCount ) { - unsigned int corner_index = ON_SubDSectorType::AngleIndexFromAngleRadians(corner_sector_angle_radians); - if (corner_index <= ON_SubDSectorType::MaximumAngleIndex) + unsigned int corner_index = ON_SubDSectorType::CornerAngleIndexFromCornerAngleRadians(corner_sector_angle_radians); + if (corner_index <= ON_SubDSectorType::MaximumCornerAngleIndex) { - if (2*corner_index > ON_SubDSectorType::MaximumAngleIndex) + if (2*corner_index > ON_SubDSectorType::MaximumCornerAngleIndex) { // concave corner - theta = (2 pi - corner_sector_angle_radians)/sector_face_count - corner_index = ON_SubDSectorType::MaximumAngleIndex - corner_index; + corner_index = ON_SubDSectorType::MaximumCornerAngleIndex - corner_index; } - double a = ((corner_index/((double)ON_SubDSectorType::MaximumAngleIndex))*ON_PI)/((double)sector_face_count); + double a = ((corner_index/((double)ON_SubDSectorType::MaximumCornerAngleIndex))*ON_PI)/((double)sector_face_count); return a; } } @@ -378,7 +401,14 @@ double ON_SubDSectorType::CornerSectorAngleRadiansFromEdges( return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorCornerSectorAngle); if (edges[0] == edges[1]) - return ON_SUBD_RETURN_ERROR(0.0); + { + // occurs in nonmanifold cases like RH-49843 + // When an interior nonmanifold edge terminates a corner + // and is the only crease edge is a sector for some + // faces attached to the edge. + return ON_SubDSectorType::MaximumCornerAngleRadians; + //return ON_SUBD_RETURN_ERROR(0.0); + } ON__UINT_PTR edge_ends[2] = { ON_SUBD_EDGE_DIRECTION(sector_boundary_edge0_ptr.m_ptr), ON_SUBD_EDGE_DIRECTION(sector_boundary_edge1_ptr.m_ptr) }; @@ -407,7 +437,7 @@ double ON_SubDSectorType::CornerSectorAngleRadiansFromEdges( // In reality, we will be lucky if we get 3 digits of precision in the trig functions // using the dot and cross of unitized differences. double cos_alpha = A*B; - double sin_alpha = ON_CrossProduct(A, B).Length(); + double sin_alpha = ON_CrossProduct(A, B).Length(); // NOTE WELL:: sin_alpha >= 0.0 or is invalid const double trig_zero_tol = 0.002; if (fabs(cos_alpha) <= trig_zero_tol) @@ -419,34 +449,38 @@ double ON_SubDSectorType::CornerSectorAngleRadiansFromEdges( { // valid sin and cos and no NaNs const double trig_one_tol = 0.999; + double alpha = ON_DBL_QNAN; + if ( 0.0 == cos_alpha || fabs(sin_alpha) >= trig_one_tol) - return (sin_alpha < 0.0) ? 1.5*ON_PI : 0.5*ON_PI; - - if ( 0.0 == sin_alpha || fabs(cos_alpha) >= trig_one_tol) - return (cos_alpha < 0.0) ? ON_PI : 0.0; - - double alpha = atan2(sin_alpha, cos_alpha); - if (!ON_IsValid(alpha)) - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorCornerSectorAngle); - - if (alpha < 0.0) + alpha = (sin_alpha < 0.0) ? 1.5*ON_PI : 0.5*ON_PI; + else if ( 0.0 == sin_alpha || fabs(cos_alpha) >= trig_one_tol) + alpha = (cos_alpha < 0.0) ? ON_PI : 0.0; + else { - alpha += 2.0*ON_PI; - if ( alpha >= 2.0*ON_PI ) - alpha = 0.0; + alpha = atan2(sin_alpha, cos_alpha); + if (!ON_IsValid(alpha)) + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorCornerSectorAngle); + + if (alpha < 0.0) + { + alpha += 2.0*ON_PI; + if (alpha >= 2.0*ON_PI) + alpha = 0.0; + } } if (alpha >= 0.0 && alpha <= (1.0 + ON_EPSILON)*2.0*ON_PI) { - if (alpha >= 2.0*ON_PI) - { - alpha = 0.0; - } - else if ( fabs(alpha - ON_PI) <= 0.002 ) + if (alpha <= ON_SubDSectorType::MinimumCornerAngleRadians) + alpha = ON_SubDSectorType::MinimumCornerAngleRadians; + else if (alpha >= ON_SubDSectorType::MaximumCornerAngleRadians) + alpha = ON_SubDSectorType::MaximumCornerAngleRadians; + else if (fabs(alpha - ON_PI) <= 0.002) { // straight "corner" alpha = ON_PI; } + return alpha; } } @@ -459,27 +493,12 @@ double ON_SubDSectorType::CornerSectorAngleRadiansFromEdges( double ON_SubDSectorType::SectorWeightFromTheta( - ON_SubD::SubDType subdivision_type, double sector_theta ) { if (!(sector_theta > 0.0 && sector_theta <= ON_PI)) return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); - const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subdivision_type); - - switch (facet_type) - { - case ON_SubD::FacetType::Tri: - case ON_SubD::FacetType::Quad: - break; - - default: - // bogus facet type - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); - break; - } - double cos_theta = cos(sector_theta); // If cos_theta is near 0, +1/2, -1/2, +1 or -1, then use those values @@ -494,26 +513,12 @@ double ON_SubDSectorType::SectorWeightFromTheta( else if (abs_cos_theta + cos_tol >= 1.0) cos_theta = (cos_theta < 0.0) ? -1.0 : 1.0; - double a, wrange[2]; - - if (ON_SubD::FacetType::Tri == facet_type) - { - // Triangle case: w = 1/3 + 1/3*cos(theta); - a = 1.0 / 3.0; - wrange[0] = 0.0; - wrange[1] = 2.0 / 3.0; - } - else - { - // Quadrangle case: w = 1/2 + 1/3*cos(theta); - a = 0.5; - wrange[0] = 1.0 / 6.0; - wrange[1] = 5.0 / 6.0; - } + // Quadrangle case: w = 1/2 + 1/3*cos(theta); + const double wrange[2] = { 1.0 / 6.0, 5.0 / 6.0 }; if (cos_theta > -1.0 && cos_theta < 1.0) { - const double w = a + cos_theta / 3.0; + const double w = 0.5 + cos_theta / 3.0; if (w > wrange[0] && w < wrange[1]) return w; if (w <= wrange[0]) @@ -528,7 +533,7 @@ double ON_SubDSectorType::SectorWeightFromTheta( if (cos_theta <= -1.0) return wrange[0]; - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); // error + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); } double ON_SubDSectorType::SectorWeight() const @@ -548,7 +553,7 @@ bool ON_SubDSectorType::IsValidCornerSectorAngleRadians( double corner_sector_angle_radians ) { - return (corner_sector_angle_radians >= 0.0 && corner_sector_angle_radians <= 2.0*ON_PI); + return (corner_sector_angle_radians >= 0.0 && corner_sector_angle_radians <= ON_2PI); } double ON_SubDSectorType::ClampCornerSectorAngleRadians( @@ -556,14 +561,16 @@ double ON_SubDSectorType::ClampCornerSectorAngleRadians( ) { if ( corner_sector_angle_radians < -ON_PI ) - corner_sector_angle_radians += 2.0*ON_PI; + corner_sector_angle_radians += ON_2PI; else if ( corner_sector_angle_radians > 3.0*ON_PI ) corner_sector_angle_radians -= 2.0*ON_PI; const double angle_tol = 0.25*(ON_PI/180.0); // 1/4 degree. if (fabs(corner_sector_angle_radians - ON_PI) <= angle_tol) return ON_PI; - if (fabs(corner_sector_angle_radians - 2.0*ON_PI) <= angle_tol) - return 2.0*ON_PI; + if (fabs(corner_sector_angle_radians + ON_PI) <= angle_tol) + return -ON_PI; + if (fabs(corner_sector_angle_radians - ON_2PI) <= angle_tol) + return ON_2PI; return corner_sector_angle_radians; } @@ -574,50 +581,39 @@ double ON_SubDSectorType::SmoothSectorWeight() } double ON_SubDSectorType::CreaseSectorWeight( - ON_SubD::SubDType subdivision_type, unsigned int sector_face_count ) { if (sector_face_count < 1) return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); - if (false == ON_SubD::IsQuadOrTriSubDType(subdivision_type)) - return ON_SubDSectorType::UnsetSectorWeight; double sector_theta = ON_SubDSectorType::CreaseSectorTheta(sector_face_count); - return ON_SubDSectorType::SectorWeightFromTheta(subdivision_type, sector_theta); + return ON_SubDSectorType::SectorWeightFromTheta(sector_theta); } double ON_SubDSectorType::DartSectorWeight( - ON_SubD::SubDType subdivision_type, unsigned int sector_face_count ) { if (sector_face_count < 2) return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); - if (false == ON_SubD::IsQuadOrTriSubDType(subdivision_type)) - return ON_SubDSectorType::UnsetSectorWeight; double sector_theta = ON_SubDSectorType::DartSectorTheta(sector_face_count); - return ON_SubDSectorType::SectorWeightFromTheta(subdivision_type, sector_theta); + return ON_SubDSectorType::SectorWeightFromTheta(sector_theta); } double ON_SubDSectorType::CornerSectorWeight( - ON_SubD::SubDType subdivision_type, unsigned int sector_face_count, double corner_sector_angle_radians ) { - if ( ON_SubD::SubDType::Unset == subdivision_type) - return ON_SubDSectorType::UnsetSectorWeight; - corner_sector_angle_radians = ON_SubDSectorType::ClampCornerSectorAngleRadians(corner_sector_angle_radians); if (ON_SubDSectorType::IsValidCornerSectorAngleRadians(corner_sector_angle_radians) && sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(ON_SubD::VertexTag::Corner) && sector_face_count <= ON_SubDVertex::MaximumFaceCount - && ON_SubD::IsQuadOrTriSubDType(subdivision_type) ) { const double sector_theta = ON_SubDSectorType::CornerSectorThetaFromCornerAngle(sector_face_count,corner_sector_angle_radians); if (sector_theta >= 0.0) - return ON_SubDSectorType::SectorWeightFromTheta(subdivision_type, sector_theta); + return ON_SubDSectorType::SectorWeightFromTheta(sector_theta); } return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); } @@ -640,22 +636,9 @@ int ON_SubDSectorType::Compare(const ON_SubDSectorType* a, const ON_SubDSectorTy if (nullptr == b) return 1; - int rc = CompareUnsinged((unsigned int)a->m_subd_type,(unsigned int)b->m_subd_type); + int rc = 0; for (;;) { - if (0 != rc) - { - // bias towards ccquad, then lwtri, then exotics - if ( ON_SubD::SubDType::QuadCatmullClark == a->m_subd_type) - rc = -1; - else if ( ON_SubD::SubDType::QuadCatmullClark == b->m_subd_type) - rc = 1; - else if ( ON_SubD::SubDType::TriLoopWarren == a->m_subd_type) - rc = -1; - else if ( ON_SubD::SubDType::TriLoopWarren == b->m_subd_type) - rc = 1; - break; - } rc = CompareUnsinged((unsigned int)a->m_vertex_tag,(unsigned int)b->m_vertex_tag); if (0 != rc) { @@ -693,7 +676,6 @@ int ON_SubDSectorType::Compare(const ON_SubDSectorType* a, const ON_SubDSectorTy void ON_SubDSectorType::SetHash() { unsigned int hash = 0; - hash = ON_CRC32(hash,sizeof(m_subd_type),&m_subd_type); hash = ON_CRC32(hash,sizeof(m_vertex_tag),&m_vertex_tag); hash = ON_CRC32(hash,sizeof(m_sector_face_count),&m_sector_face_count); if ( ON_SubD::VertexTag::Corner == m_vertex_tag) @@ -724,118 +706,84 @@ static bool ON_SubDSectorType_IsValidFaceCountForCreate( return ( 0 == sector_face_count || ON_UNSET_UINT_INDEX == sector_face_count || ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count)); } -static bool ON_SubDSectorType_IsValidTypes( - ON_SubD::SubDType subd_type, - ON_SubD::VertexTag vertex_tag, - ON_SubD::FacetType facet_type - ) -{ - if (ON_SubD::SubDType::Unset == subd_type && ON_SubD::FacetType::Unset == facet_type ) - return true; - if (ON_SubD::FacetType::Unset != facet_type) - return true; - return false; -} - ON_SubDSectorType ON_SubDSectorType::CreateSmoothSectorType( - ON_SubD::SubDType subd_type, unsigned int sector_face_count ) { const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Smooth; if (ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count)) { - const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); - if (ON_SubDSectorType_IsValidTypes(subd_type,vertex_tag,facet_type)) - { - ON_SubDSectorType st; - st.m_subd_type = subd_type; - st.m_facet_type = facet_type; - st.m_vertex_tag = vertex_tag; - st.m_sector_face_count - = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) - ? sector_face_count - : 0; - st.m_sector_weight - = (st.m_sector_face_count>0) - ? ON_SubDSectorType::IgnoredSectorWeight - : ON_SubDSectorType::UnsetSectorWeight; - st.m_sector_theta - = (st.m_sector_face_count>0) - ? ON_SubDSectorType::SmoothSectorTheta - : ON_SubDSectorType::UnsetSectorTheta; - st.SetHash(); - return st; - } + ON_SubDSectorType st; + st.m_vertex_tag = vertex_tag; + st.m_sector_face_count + = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) + ? sector_face_count + : 0; + st.m_sector_weight + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::IgnoredSectorWeight + : ON_SubDSectorType::UnsetSectorWeight; + st.m_sector_theta + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::SmoothSectorTheta + : ON_SubDSectorType::UnsetSectorTheta; + st.SetHash(); + return st; } return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); } ON_SubDSectorType ON_SubDSectorType::CreateCreaseSectorType( - ON_SubD::SubDType subd_type, unsigned int sector_face_count ) { const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Crease; if (ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count)) { - const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); - if (ON_SubDSectorType_IsValidTypes(subd_type,vertex_tag,facet_type)) - { - ON_SubDSectorType st; - st.m_subd_type = subd_type; - st.m_facet_type = facet_type; - st.m_vertex_tag = vertex_tag; - st.m_sector_face_count - = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) - ? sector_face_count - : 0; - st.m_sector_weight - = (st.m_sector_face_count>0) - ? ON_SubDSectorType::CreaseSectorWeight(subd_type,sector_face_count) - : ON_SubDSectorType::UnsetSectorWeight; - st.m_sector_theta - = (st.m_sector_face_count>0) - ? ON_SubDSectorType::CreaseSectorTheta(sector_face_count) - : ON_SubDSectorType::UnsetSectorTheta; - st.SetHash(); - return st; - } + ON_SubDSectorType st; + st.m_vertex_tag = vertex_tag; + st.m_sector_face_count + = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) + ? sector_face_count + : 0; + st.m_sector_weight + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::CreaseSectorWeight(sector_face_count) + : ON_SubDSectorType::UnsetSectorWeight; + st.m_sector_theta + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::CreaseSectorTheta(sector_face_count) + : ON_SubDSectorType::UnsetSectorTheta; + st.SetHash(); + return st; } return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); } ON_SubDSectorType ON_SubDSectorType::CreateDartSectorType( - ON_SubD::SubDType subd_type, unsigned int sector_face_count ) { const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Dart; if ( ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count) ) { - const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); - if (ON_SubDSectorType_IsValidTypes(subd_type,vertex_tag,facet_type)) - { - ON_SubDSectorType st; - st.m_subd_type = subd_type; - st.m_facet_type = facet_type; - st.m_vertex_tag = vertex_tag; - st.m_sector_face_count - = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) - ? sector_face_count - : 0; - st.m_sector_weight - = (st.m_sector_face_count>0) - ? ON_SubDSectorType::DartSectorWeight(subd_type,sector_face_count) - : ON_SubDSectorType::UnsetSectorWeight; - st.m_sector_theta - = (st.m_sector_face_count>0) - ? ON_SubDSectorType::DartSectorTheta(sector_face_count) - : ON_SubDSectorType::UnsetSectorTheta; - st.SetHash(); - return st; - } + ON_SubDSectorType st; + st.m_vertex_tag = vertex_tag; + st.m_sector_face_count + = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) + ? sector_face_count + : 0; + st.m_sector_weight + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::DartSectorWeight(sector_face_count) + : ON_SubDSectorType::UnsetSectorWeight; + st.m_sector_theta + = (st.m_sector_face_count>0) + ? ON_SubDSectorType::DartSectorTheta(sector_face_count) + : ON_SubDSectorType::UnsetSectorTheta; + st.SetHash(); + return st; } return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); } @@ -843,7 +791,6 @@ ON_SubDSectorType ON_SubDSectorType::CreateDartSectorType( ON_SubDSectorType ON_SubDSectorType::CreateCornerSectorType( - ON_SubD::SubDType subd_type, unsigned int sector_face_count, double corner_sector_angle_radians ) @@ -867,36 +814,30 @@ ON_SubDSectorType ON_SubDSectorType::CreateCornerSectorType( const ON_SubD::VertexTag vertex_tag = ON_SubD::VertexTag::Corner; if (ON_SubDSectorType_IsValidFaceCountForCreate(vertex_tag,sector_face_count)) { - const ON_SubD::FacetType facet_type = ON_SubD::FacetTypeFromSubDType(subd_type); - if (ON_SubDSectorType_IsValidTypes(subd_type,vertex_tag,facet_type)) + unsigned int corner_sector_angle_index + = (ON_SubDSectorType::UnsetCornerSectorAngle == corner_sector_angle_radians) + ? 0 + : ON_SubDSectorType::CornerAngleIndexFromCornerAngleRadians(corner_sector_angle_radians); + if (corner_sector_angle_index <= ON_SubDSectorType::MaximumCornerAngleIndex) { - unsigned int corner_sector_angle_index - = (ON_SubDSectorType::UnsetCornerSectorAngle == corner_sector_angle_radians) - ? 0 - : ON_SubDSectorType::AngleIndexFromAngleRadians(corner_sector_angle_radians); - if (corner_sector_angle_index <= ON_SubDSectorType::MaximumAngleIndex) - { - ON_SubDSectorType st; - st.m_subd_type = subd_type; - st.m_facet_type = facet_type; - st.m_vertex_tag = vertex_tag; - st.m_sector_face_count - = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) - ? sector_face_count - : 0; - st.m_sector_weight - = (st.m_sector_face_count > 0 && ON_SubDSectorType::UnsetCornerSectorAngle != corner_sector_angle_radians) - ? ON_SubDSectorType::CornerSectorWeight(subd_type, sector_face_count, corner_sector_angle_radians) - : ON_SubDSectorType::UnsetSectorWeight; - st.m_sector_theta - = (st.m_sector_face_count > 0 && ON_SubDSectorType::UnsetCornerSectorAngle != corner_sector_angle_radians) - ? ON_SubDSectorType::CornerSectorThetaFromCornerAngle(sector_face_count, corner_sector_angle_radians) - : ON_SubDSectorType::UnsetSectorTheta; - st.m_corner_sector_angle_index = (unsigned char)corner_sector_angle_index; - st.m_corner_sector_angle_radians = corner_sector_angle_radians; - st.SetHash(); - return st; - } + ON_SubDSectorType st; + st.m_vertex_tag = vertex_tag; + st.m_sector_face_count + = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) + ? sector_face_count + : 0; + st.m_sector_weight + = (st.m_sector_face_count > 0 && ON_SubDSectorType::UnsetCornerSectorAngle != corner_sector_angle_radians) + ? ON_SubDSectorType::CornerSectorWeight( sector_face_count, corner_sector_angle_radians) + : ON_SubDSectorType::UnsetSectorWeight; + st.m_sector_theta + = (st.m_sector_face_count > 0 && ON_SubDSectorType::UnsetCornerSectorAngle != corner_sector_angle_radians) + ? ON_SubDSectorType::CornerSectorThetaFromCornerAngle(sector_face_count, corner_sector_angle_radians) + : ON_SubDSectorType::UnsetSectorTheta; + st.m_corner_sector_angle_index = (unsigned char)corner_sector_angle_index; + st.m_corner_sector_angle_radians = corner_sector_angle_radians; + st.SetHash(); + return st; } } } @@ -905,28 +846,27 @@ ON_SubDSectorType ON_SubDSectorType::CreateCornerSectorType( ON_SubDSectorType ON_SubDSectorType::Create( - ON_SubD::SubDType subd_type, ON_SubD::VertexTag vertex_tag, unsigned int sector_face_count, double corner_sector_angle_radians ) { - if ( ON_SubD::SubDType::Unset == subd_type && ON_SubD::VertexTag::Unset == vertex_tag && 0 == sector_face_count) + if (ON_SubD::VertexTag::Unset == vertex_tag && 0 == sector_face_count) return ON_SubDSectorType::Empty; switch (vertex_tag) { case ON_SubD::VertexTag::Smooth: - return ON_SubDSectorType::CreateSmoothSectorType(subd_type,sector_face_count); + return ON_SubDSectorType::CreateSmoothSectorType(sector_face_count); break; case ON_SubD::VertexTag::Crease: - return ON_SubDSectorType::CreateCreaseSectorType(subd_type,sector_face_count); + return ON_SubDSectorType::CreateCreaseSectorType(sector_face_count); break; case ON_SubD::VertexTag::Corner: - return ON_SubDSectorType::CreateCornerSectorType(subd_type,sector_face_count,corner_sector_angle_radians); + return ON_SubDSectorType::CreateCornerSectorType(sector_face_count,corner_sector_angle_radians); break; case ON_SubD::VertexTag::Dart: - return ON_SubDSectorType::CreateDartSectorType(subd_type,sector_face_count); + return ON_SubDSectorType::CreateDartSectorType(sector_face_count); break; } @@ -934,7 +874,6 @@ ON_SubDSectorType ON_SubDSectorType::Create( } ON_SubDSectorType ON_SubDSectorType::Create( - ON_SubD::SubDType subd_type, const ON_SubDSectorIterator& sit ) { @@ -976,7 +915,7 @@ ON_SubDSectorType ON_SubDSectorType::Create( = (ON_SubD::VertexTag::Corner == vertex_tag) ? ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(edge0ptr,local_sit.CurrentEdgePtr(0)) : 0.0; - return ON_SubDSectorType::Create(subd_type,vertex_tag,sector_face_count,corner_sector_angle_radians); + return ON_SubDSectorType::Create(vertex_tag,sector_face_count,corner_sector_angle_radians); } } @@ -984,7 +923,6 @@ ON_SubDSectorType ON_SubDSectorType::Create( } ON_SubDSectorType ON_SubDSectorType::Create( - ON_SubD::SubDType subd_type, const class ON_SubDFace* face, unsigned int face_vertex_index ) @@ -995,11 +933,10 @@ ON_SubDSectorType ON_SubDSectorType::Create( return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); ON_SubDSectorIterator sit; sit.Initialize(face,0,face_vertex_index); - return ON_SubDSectorType::Create(subd_type,sit); + return ON_SubDSectorType::Create(sit); } ON_SubDSectorType ON_SubDSectorType::Create( - ON_SubD::SubDType subd_type, const class ON_SubDFace* face, const class ON_SubDVertex* vertex ) @@ -1011,12 +948,11 @@ ON_SubDSectorType ON_SubDSectorType::Create( unsigned int face_vertex_index = face->VertexIndex(vertex); if (face_vertex_index >= face->m_edge_count) return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); - return ON_SubDSectorType::Create(subd_type,face,face_vertex_index); + return ON_SubDSectorType::Create(face,face_vertex_index); } ON_SubDSectorType ON_SubDSectorType::Create( - ON_SubD::SubDType subd_type, const class ON_SubDEdge* edge, unsigned int edge_vertex_index ) @@ -1030,5 +966,5 @@ ON_SubDSectorType ON_SubDSectorType::Create( if (nullptr == face) return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::Empty); - return ON_SubDSectorType::Create(subd_type,face,vertex); + return ON_SubDSectorType::Create(face,vertex); } diff --git a/opennurbs_subd_texture.cpp b/opennurbs_subd_texture.cpp new file mode 100644 index 00000000..bcf9234b --- /dev/null +++ b/opennurbs_subd_texture.cpp @@ -0,0 +1,1017 @@ +#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-2019 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 . +// +//////////////////////////////////////////////////////////////// +*/ + + + +ON_SubDTextureDomainType ON_SubD::TextureDomainTypeFromUnsigned +( + unsigned int texture_domain_type_as_unsigned +) +{ + switch (texture_domain_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::PerFace); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Packed); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Zero); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Nan); + ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Custom); + } + return ON_SUBD_RETURN_ERROR(ON_SubDTextureDomainType::Unset); +} + +const ON_wString ON_SubD::TextureDomainTypeToString( + ON_SubDTextureDomainType texture_domain_type +) +{ + const wchar_t* s; + switch (texture_domain_type) + { + case ON_SubDTextureDomainType::Unset: + s = L"Unset"; + break; + case ON_SubDTextureDomainType::PerFace: + s = L"PerFace"; + break; + case ON_SubDTextureDomainType::Packed: + s = L"Packed"; + break; + case ON_SubDTextureDomainType::Zero: + s = L"Zero"; + break; + case ON_SubDTextureDomainType::Nan: + s = L"Nan"; + break; + case ON_SubDTextureDomainType::Custom: + s = L"Custom"; + break; + default: + s = nullptr; + break; + } + return (nullptr != s && 0 != s[0]) + ? ON_wString(s) + : ON_wString::FormatToString(L"ON_SubDTextureDomainType(%u)", ((unsigned)static_cast(texture_domain_type))); +} + +////////////////////////////////////////////////////////////////////////////// +// +// ON_SubDimple - texture coordinates +// +#if 1 +#pragma region ON_SubD - texture coordinates + +void ON_SubDimple::SetTextureMappingTag(const ON_MappingTag &mapping_tag) const +{ + this->m_texture_mapping_tag = mapping_tag; +} + +const ON_MappingTag ON_SubDimple::TextureMappingTag() const +{ + return m_texture_mapping_tag; +} + +#pragma endregion +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// ON_SubD - texture coordinates +// +#if 1 +#pragma region ON_SubD - texture coordinates + +ON_SubDTextureDomainType ON_SubD::TextureDomainType() const +{ + const ON_SubDimple* subdimple = SubDimple(); + return (nullptr != subdimple) ? subdimple->TextureDomainType() : ON_SubDTextureDomainType::Unset; +} + +ON_SubDTextureDomainType ON_SubDimple::TextureDomainType() const +{ + return m_texture_domain_type; +} + +void ON_SubDimple::SetTextureDomainType( + ON_SubDTextureDomainType texture_domain_type +) const +{ + m_texture_domain_type = texture_domain_type; +} + +unsigned int ON_SubD::TextureImageSuggestedMinimumSize() const +{ + const ON_2udex grid_size = ON_SubD::TextureDomainGridSize(FaceCount(), 0.0, 0.0); + return ON_SubD::TextureImageSuggestedMinimumSize(grid_size); +} + +unsigned int ON_SubD::TextureImageSuggestedMinimumSize( + ON_2udex grid_size +) +{ + const unsigned min_pixels = 16; + const unsigned max_pixels = 4098; + unsigned pixels = grid_size.i >= grid_size.j ? grid_size.i : grid_size.j; + if (0 == pixels) + return ON_SUBD_RETURN_ERROR(1); + + if (pixels < max_pixels / min_pixels) + pixels *= min_pixels; + else + pixels = max_pixels; + + if (pixels < min_pixels) + { + pixels = min_pixels; + const unsigned smallgrid_min_pixels = 128; + if (grid_size.i > 0 && grid_size.j > 0) + { + while (grid_size.i * grid_size.j * pixels < smallgrid_min_pixels) + pixels *= 2; + } + } + + if (pixels > max_pixels) + pixels = max_pixels; + + return pixels; +} + +const ON_2udex ON_SubD::TextureDomainGridSize( + unsigned minimum_rectangle_count, + double image_width, + double image_height +) +{ + if (0 == minimum_rectangle_count) + return ON_2udex(1, 1); + + unsigned i = (unsigned)floor(sqrt((double)(minimum_rectangle_count))); + while (i < minimum_rectangle_count && i*i < minimum_rectangle_count) + ++i; + unsigned int j = i; + if (j > 1 && (j - 1)*i >= minimum_rectangle_count) + --j; + return (image_height > image_width) ? ON_2udex(j, i) : ON_2udex(i, j); +} + +const ON_2udex ON_SubD::GetTextureDomainAndDelta( + unsigned quad_count, + double image_width, + double image_height, + ON_2dVector& quad_texture_domain, + ON_2dVector& quad_texture_delta +) +{ + ON_2udex grid_size(0, 0); + quad_texture_domain = ON_2dVector::ZeroVector; + quad_texture_delta = ON_2dVector::ZeroVector; + + const bool bImageSizeKnown + = image_width > 0.0 && image_width < ON_UNSET_POSITIVE_VALUE + && image_width > 0.0 && image_width < ON_UNSET_POSITIVE_VALUE; + if (false == bImageSizeKnown) + { + // unknown image size - assume it's square. + grid_size = ON_SubD::TextureDomainGridSize(quad_count, 0.0, 0.0); + + // use grid size to set minimum sugggested square image size + image_width = image_height = (double)ON_SubD::TextureImageSuggestedMinimumSize(grid_size); + } + else + { + // use image size to help set grid size + grid_size = ON_SubD::TextureDomainGridSize(quad_count, image_width, image_height); + } + + if (0 == grid_size.i || 0 == grid_size.j) + return ON_SUBD_RETURN_ERROR(grid_size); + + const double image_size[2] = { image_width ,image_height }; + + // quad_image_size = size of image for each quad + const ON_2dVector quad_image_size(image_size[0] / ((double)grid_size.i), image_size[1] / ((double)grid_size.j)); + + if (1U == quad_count) + { + // quad uses entire image_delta x image_delta region + quad_texture_delta.Set(1.0, 1.0); + quad_texture_domain = quad_texture_delta; + } + else + { + for (int k = 0; k < 2; ++k) + { + if (quad_image_size[k] >= 16.0) + { + // An image with image_delta.x X image_delta.y pixels + // doesn't have to share a pixel with its neighbors. + quad_texture_delta[k] = floor(quad_image_size[k]) / image_size[k]; + quad_texture_domain[k] = (floor(quad_image_size[k]) - 1.0) / image_size[k]; + } + else + { + // Not as good, but a quad is getting at most 16x16 pixels in from a texture image + // with image_delta x image_delta pixels, so the texture will look bad anyway. + quad_texture_delta[k] = (quad_image_size[k] / image_size[k]); + quad_texture_domain[k] = (15.0 / 16.0)*quad_texture_delta[k]; + } + } + } + + return grid_size; +} + +bool ON_SubD::SetTextureDomains( + ON_SubDTextureDomainType texture_domain_type, + bool bLazy, + bool bSetFragmentTextureCoordinates +) const +{ + const ON_SubDimple* subdimple = SubDimple(); + if (nullptr == subdimple) + return (ON_SubDTextureDomainType::Unset == texture_domain_type); + + if (ON_SubDTextureDomainType::Unset == texture_domain_type || ON_SubDTextureDomainType::Custom == texture_domain_type) + bLazy = true; + if (bLazy && texture_domain_type == subdimple->TextureDomainType()) + return true; + + if (0 == FaceCount()) + { + subdimple->SetTextureDomainType(ON_SubDTextureDomainType::Unset); + return (ON_SubDTextureDomainType::Unset == texture_domain_type); + } + + bool rc = false; + + switch (texture_domain_type) + { + case ON_SubDTextureDomainType::Unset: + rc = true; + break; + + case ON_SubDTextureDomainType::PerFace: + case ON_SubDTextureDomainType::Packed: + case ON_SubDTextureDomainType::Zero: + case ON_SubDTextureDomainType::Nan: + { + ON_SubDFaceIterator fit(*this); + rc = ON_SubD::SetTextureDomains(fit, texture_domain_type, bSetFragmentTextureCoordinates); + } + break; + + case ON_SubDTextureDomainType::Custom: + rc = false; + break; + + default: + rc = false; + break; + } + + if (rc && nullptr != subdimple) + subdimple->SetTextureDomainType(texture_domain_type); + + return rc; +} + +bool ON_SubD::SetTextureDomains( + ON_SubDFaceIterator& fit, + ON_SubDTextureDomainType texture_domain_type, + bool bSetFragmentTextureCoordinates +) +{ + if (ON_SubDTextureDomainType::Custom == texture_domain_type) + return ON_SUBD_RETURN_ERROR(false); + + bool bFragmentsExist = false; + const double default_tc = (ON_SubDTextureDomainType::Zero == texture_domain_type) ? 0.0 : ON_DBL_QNAN; + const ON_2dPoint default_origin(default_tc, default_tc); + const ON_2dVector default_delta(default_tc, default_tc); + unsigned face_count = 0; + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + f->SetTextureDomain(texture_domain_type, default_origin, default_delta, 0); + if (f->m_edge_count >= 3) + ++face_count; + if (f->MeshFragments()) + bFragmentsExist = true; + } + + bool rc + = ON_SubDTextureDomainType::Unset == texture_domain_type + || ON_SubDTextureDomainType::Zero == texture_domain_type + || ON_SubDTextureDomainType::Nan == texture_domain_type; + if (ON_SubDTextureDomainType::Packed == texture_domain_type) + { + texture_domain_type = ON_SubDTextureDomainType::PerFace; + } + + if (ON_SubDTextureDomainType::PerFace == texture_domain_type) + { + const ON_2udex grid_size = ON_SubD::TextureDomainGridSize(face_count, 0.0, 0.0); + const double image_width = ON_SubD::TextureImageSuggestedMinimumSize(grid_size); + const double image_height = image_width; + ON_2dVector face_texture_domain = ON_2dVector::NanVector; + ON_2dVector face_texture_delta = ON_2dVector::NanVector; + ON_SubD::GetTextureDomainAndDelta( + face_count, + image_height, + image_width, + face_texture_domain, + face_texture_delta + ); + + ON_2udex k(0, 0); + unsigned int face_dex = 0; + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace(), ++face_dex) + { + if (0 != face_dex) + { + k.i = (++k.i % grid_size.i); + if (0 == k.i) + ++k.j; + } + const ON_2dPoint face_texture_origin(k.i*face_texture_delta.x, k.j*face_texture_delta.y); + f->SetTextureDomain(texture_domain_type, face_texture_origin, face_texture_domain, 0); + if (false == bFragmentsExist && nullptr != f->MeshFragments()) + bFragmentsExist = true; + } + rc = true; + } + + // Use the domains to set fragment texture coordinates + if (bFragmentsExist && bSetFragmentTextureCoordinates) + ON_SubD::SetTextureCoordinatesFromFaceDomains(fit); + + return rc; +} + +bool ON_SubD::SetTextureCoordinatesFromFaceDomains() const +{ + if (ON_SubDTextureDomainType::Unset == this->TextureDomainType()) + { + if (false == SetTextureDomains(ON_SubDTextureDomainType::PerFace, false, false)) + return false; + } + ON_SubDFaceIterator fit(*this); + const bool rc = ON_SubD::SetTextureCoordinatesFromFaceDomains(fit); + const ON_MappingTag& mapping_tag + = (rc) + ? ON_MappingTag::SurfaceParameterMapping + : ON_MappingTag::Unset; + SetTextureMappingTag(mapping_tag); + return rc; +} + + +bool ON_SubD::SetTextureCoordinatesFromFaceDomains(ON_SubDFaceIterator& fit) +{ + // estimate face image size - used to add empty space around ngon-subdfragments + const unsigned int face_image_size = ON_SubD::TextureImageSuggestedMinimumSize(ON_SubD::TextureDomainGridSize(fit.FaceCount(), 0, 0)); + + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + if (f->m_edge_count < 3) + continue; + const ON_SubDMeshFragment* fragment = f->MeshFragments(); + if (nullptr == fragment) + continue; + const ON_2dPoint face_corners[4] = { + f->TextureDomainCorner(true, false, 0), + f->TextureDomainCorner(true, false, 1), + f->TextureDomainCorner(true, false, 2), + f->TextureDomainCorner(true, false, 3) + }; + if (4 == f->m_edge_count) + { + // This face in a quad with a single fragment and that fragment gets the entire face_texture_domain + fragment->SetTextureCoordinateCorners(true, face_corners, true); + } + else + { + // This face in an n-gon with n fragments that each get a portion of the face_texture_domain + ON_2dVector frag_texture_domain = ON_2dVector::NanVector; + ON_2dVector frag_texture_delta = ON_2dVector::NanVector; + const ON_2udex fragment_grid_size = ON_SubD::GetTextureDomainAndDelta( + f->m_edge_count, + face_image_size, + face_image_size, + frag_texture_domain, + frag_texture_delta + ); + ON_2udex frag_k(0, 0); + for (unsigned short frag_dex = 0; frag_dex < f->m_edge_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment) + { + if (0 != frag_dex) + { + frag_k.i = (++frag_k.i % fragment_grid_size.i); + if (0 == frag_k.i) + ++frag_k.j; + } + const double s0 = frag_k.i*frag_texture_delta.x; + const double t0 = frag_k.j*frag_texture_delta.y; + const double s2 = s0 + frag_texture_domain.x; + const double t2 = t0 + frag_texture_domain.y; + fragment->SetTextureCoordinateCorners(true, face_corners, s0, s2, t0, t2, true); + } + } + } + + return true; +} + +void ON_SubD::SetTextureMappingTag(const ON_MappingTag &mapping_tag) const +{ + const ON_SubDimple* subdimple = SubDimple(); + if (nullptr != subdimple) + subdimple->SetTextureMappingTag(mapping_tag); +} + +bool ON_SubD::SetTextureCoordinates( + const class ON_TextureMapping& mapping, + const class ON_Xform* subd_xform, + bool bLazy +) const +{ + const ON_TextureMapping::TYPE mt = mapping.m_type; + + if (nullptr != subd_xform) + { + if (ON_TextureMapping::TYPE::srfp_mapping == mt || ON_TextureMapping::TYPE::no_mapping == mt) + subd_xform = nullptr; + else if (false == subd_xform->IsValid() || subd_xform->IsIdentity() || subd_xform->IsZero()) + subd_xform = nullptr; + } + + const ON_MappingTag tag(mapping, subd_xform); + + if (0 == TextureMappingTag().Compare(tag)) + return true; + + if (ON_TextureMapping::TYPE::srfp_mapping == mt || ON_TextureMapping::TYPE::no_mapping == mt) + SetTextureCoordinatesFromFaceDomains(); + + const bool bApplyUVW = (mapping.m_uvw.IsValid() && !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero()); + if (ON_TextureMapping::TYPE::srfp_mapping != mt || bApplyUVW) + { + // + // NOTE WELL: + // ON_SubDMeshFragment store the mesh used to render ON_SubD objects + // The mesh topology, 3d vertex locations, 3d vertex normals + // CANNOT be modified to insert "texture seams." + // + ON_3dPoint tc; + ON_SubDMeshFragmentIterator frit(*this); + for (const ON_SubDMeshFragment* fragment = frit.FirstFragment(); nullptr != fragment; fragment = frit.NextFragment()) + { + const unsigned P_count = fragment->PointCount(); + if (P_count < 4) + continue; + const double* P = fragment->m_P; + const size_t P_stride = fragment->m_P_stride; + unsigned T_count = fragment->TextureCoordinateCount(); + if (P_count != T_count) + continue; + double* T = fragment->m_T; + if (nullptr == T) + continue; + size_t T_stride = fragment->m_T_stride; + if (0 == T_stride) + { + T_stride = 3; + T_count = 1; + } + const unsigned N_count = fragment->NormalCount(); + const double* N = (N_count == P_count) ? fragment->m_N : &ON_3dVector::ZeroVector.x; + const size_t N_stride = (N_count == P_count) ? fragment->m_N_stride : 0; + for (double* T1 = T + T_stride * T_count; T < T1; T += T_stride, P += P_stride, N += N_stride) + { + if (ON_TextureMapping::TYPE::srfp_mapping != mt) + tc = ON_3dPoint(T[0], T[1], 0.0); + else if (false == mapping.Evaluate(ON_3dPoint(P), ON_3dVector(N), &tc)) + tc = ON_3dPoint::NanPoint; + if (bApplyUVW) + tc = mapping.m_uvw * tc; + T[0] = tc.x; + T[1] = tc.y; + T[2] = tc.z; + } + } + } + + SetTextureMappingTag(tag); + + return true; +} + + +const ON_MappingTag ON_SubD::TextureMappingTag() const +{ + const ON_SubDimple* subdimple = SubDimple(); + return (nullptr != subdimple) ? subdimple->TextureMappingTag() : ON_MappingTag(); +} + +#pragma endregion +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// ON_SubDFace - texture coordinates +// +#if 1 +#pragma region ON_SubDFace - texture coordinates + +void ON_SubDFace::SetTextureDomain( + ON_SubDTextureDomainType texture_domain_type, + ON_2dPoint origin, + ON_2dVector delta, + int packing_rotation_degrees +) const +{ + m_texture_coordinate_bits = 0; + unsigned char domain_type_bits = static_cast(texture_domain_type); + if (domain_type_bits < 8) + { + domain_type_bits <<= 4; + if (domain_type_bits != (domain_type_bits & TextureCoordinateBits::DomainTypeMask)) + domain_type_bits = 0; + } + if ( + 0 != domain_type_bits + && origin.IsValid() + && delta.x >= 0.0 && delta.x < ON_UNSET_POSITIVE_VALUE + && delta.y >= 0.0 && delta.y < ON_UNSET_POSITIVE_VALUE + && 0 == packing_rotation_degrees % 90 + ) + { + m_texture_coordinate_origin[0] = origin.x; + m_texture_coordinate_origin[1] = origin.y; + m_texture_coordinate_delta[0] = delta.x; + m_texture_coordinate_delta[1] = delta.y; + + ON_SubDFace::TextureCoordinateBits packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate0; + switch (((packing_rotation_degrees % 360) + 360) % 360) + { + case 90: + packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate90; + break; + case 180: + packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate180; + break; + case 270: + packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate270; + break; + } + m_texture_coordinate_bits |= packing_rotation; + m_texture_coordinate_bits |= domain_type_bits; + } + else + { + m_texture_coordinate_origin[0] = ON_DBL_QNAN; + m_texture_coordinate_origin[1] = ON_DBL_QNAN; + m_texture_coordinate_delta[0] = ON_DBL_QNAN; + m_texture_coordinate_delta[1] = ON_DBL_QNAN; + } +} + + +const bool ON_SubDFace::TextureDomainIsSet() const +{ + return (ON_SubDTextureDomainType::Unset != TextureDomainType()); +} + +const ON_2dPoint ON_SubDFace::TextureDomainOrigin() const +{ + return ON_2dPoint(m_texture_coordinate_origin); +} + +const ON_2dVector ON_SubDFace::TextureDomainDelta() const +{ + return ON_2dVector(m_texture_coordinate_delta); +} + +ON_SubDTextureDomainType ON_SubDFace::TextureDomainType() const +{ + return ON_SubD::TextureDomainTypeFromUnsigned((m_texture_coordinate_bits & TextureCoordinateBits::DomainTypeMask) >> 4); +} + +unsigned int ON_SubDFace::TextureDomainRotationDegrees() const +{ + unsigned int packing_rotation_degrees = 0; + switch (m_texture_coordinate_bits & ON_SubDFace::TextureCoordinateBits::PackingRotateMask) + { + case ON_SubDFace::TextureCoordinateBits::PackingRotate90: + packing_rotation_degrees = 90; + break; + case ON_SubDFace::TextureCoordinateBits::PackingRotate180: + packing_rotation_degrees = 180; + break; + case ON_SubDFace::TextureCoordinateBits::PackingRotate270: + packing_rotation_degrees = 270; + break; + } + return packing_rotation_degrees; +} + +double ON_SubDFace::TextureDomainRotationRadians() const +{ + double x = 0.0; + switch (m_texture_coordinate_bits & ON_SubDFace::TextureCoordinateBits::PackingRotateMask) + { + case ON_SubDFace::TextureCoordinateBits::PackingRotate90: + x = 1.0; + break; + case ON_SubDFace::TextureCoordinateBits::PackingRotate180: + x = 2.0; + break; + case ON_SubDFace::TextureCoordinateBits::PackingRotate270: + x = 3.0; + break; + } + return x * 0.5*ON_PI; +} + +const ON_2dPoint ON_SubDFace::TextureDomainCorner(bool bGridOrder, bool bNormalized, int corner_index) const +{ + if (false == TextureDomainIsSet()) + return ON_2dPoint::Origin; + + corner_index = ((corner_index % 4) + 4) % 4; + // now corner_index = 0,1,2 or 3. + + if (bGridOrder) + { + if (2 == corner_index) + corner_index = 3; + else if (3 == corner_index) + corner_index = 2; + } + // now corner index is a counter-clockwise corner index + + int packrot_dex = 0; + switch (m_texture_coordinate_bits & ON_SubDFace::TextureCoordinateBits::PackingRotateMask) + { + case ON_SubDFace::TextureCoordinateBits::PackingRotate90: + packrot_dex = 3; + break; + case ON_SubDFace::TextureCoordinateBits::PackingRotate180: + packrot_dex = 2; + break; + case ON_SubDFace::TextureCoordinateBits::PackingRotate270: + packrot_dex = 1; + break; + } + + corner_index = (corner_index + packrot_dex) % 4; + // now the packing rotation is taken into account. + + ON_2dPoint corner = bNormalized ? ON_2dPoint::Origin : TextureDomainOrigin(); + const ON_2dVector delta = bNormalized ? ON_2dVector(1.0, 1.0) : TextureDomainDelta(); + switch (corner_index) + { + case 1: + corner.x += delta.x; + break; + case 2: + corner.x += delta.x; + corner.y += delta.y; + break; + case 3: + corner.y += delta.y; + break; + } + + return corner; +} + +#pragma endregion +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// ON_SubDMeshFragment - texture coordinates +// +#if 1 +#pragma region ON_SubDMeshFragment - texture coordinates + +bool ON_SubDMeshFragment::TextureCoordinatesExist() const +{ + return (0 != (m_vertex_count_etc & ON_SubDMeshFragment::EtcTextureCoordinatesExistBit)); +} + +void ON_SubDMeshFragment::SetTextureCoordinatesExist(bool TextureCoordinatesExist) const +{ + if (TextureCoordinatesExist) + m_vertex_count_etc |= ON_SubDMeshFragment::EtcTextureCoordinatesExistBit; + else + m_vertex_count_etc &= ~ON_SubDMeshFragment::EtcTextureCoordinatesExistBit; +} + + +unsigned int ON_SubDMeshFragment::TextureCoordinateCount() const +{ + return (TextureCoordinatesExist() && nullptr != m_T && (0 == m_T_stride || m_T_stride >= 3)) ? VertexCount() : 0U; +} + +unsigned int ON_SubDMeshFragment::TextureCoordinateCapacity() const +{ + return (nullptr != m_T && m_T_stride >= 3) ? VertexCapacity() : 0U; +} + +const double* ON_SubDMeshFragment::TextureCoordinateArray(ON_SubDComponentLocation subd_appearance)const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? &m_ctrlnetT[0][0] : m_T; +} + +size_t ON_SubDMeshFragment::TextureCoordinateArrayStride(ON_SubDComponentLocation subd_appearance)const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 3 : m_T_stride; +} + +unsigned ON_SubDMeshFragment::TextureCoordinateArrayCount(ON_SubDComponentLocation subd_appearance) const +{ + return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? (TextureCoordinatesExist()?4U:0U) : TextureCoordinateCount(); +} + +bool ON_SubDMeshFragment::GetTextureCoordinteCorners( + bool bGridOrder, + ON_3dPoint texture_coordinate_coeners[4] +) const +{ + // mutable double m_T_default_bbox[2][2]; + if (nullptr != texture_coordinate_coeners) + { + int i; + texture_coordinate_coeners[0].x = m_ctrlnetT[0][0]; + texture_coordinate_coeners[0].y = m_ctrlnetT[0][1]; + texture_coordinate_coeners[0].z = m_ctrlnetT[0][2]; + + texture_coordinate_coeners[1].x = m_ctrlnetT[1][0]; + texture_coordinate_coeners[1].y = m_ctrlnetT[1][1]; + texture_coordinate_coeners[1].z = m_ctrlnetT[1][2]; + + i = bGridOrder ? 2 : 3; + texture_coordinate_coeners[i].x = m_ctrlnetT[2][0]; + texture_coordinate_coeners[i].y = m_ctrlnetT[2][1]; + texture_coordinate_coeners[i].z = m_ctrlnetT[2][2]; + + i = bGridOrder ? 3 : 2; + texture_coordinate_coeners[i].x = m_ctrlnetT[3][0]; + texture_coordinate_coeners[i].y = m_ctrlnetT[3][1]; + texture_coordinate_coeners[i].z = m_ctrlnetT[3][2]; + + return true; + } + return false; +} + +static const ON_2dPoint InternalFragTextureCorner( + bool bGridOrder, + const ON_2dPoint Tcorners[4], + double s, + double t +) +{ + return (1 - s)*(1 - t)*Tcorners[0] + s * (1 - t)*Tcorners[1] + (1 - s)*t*Tcorners[bGridOrder ? 2 : 3] + s * t*Tcorners[bGridOrder ? 3 : 2]; +} + +void ON_SubDMeshFragment::SetTextureCoordinateCorners( + bool bGridOrder, + const ON_2dPoint texture_coordinate_corners[4], + double s0, + double s1, + double t0, + double t1, + bool bSetTextureCoordinates +) const +{ + const ON_2dPoint c[4] = { + InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s0,t0), + InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s1,t0), + InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s0,t1), + InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s0,t1) + }; + + bGridOrder = true; // c[] is in grid order + SetTextureCoordinateCorners(bGridOrder, c, bSetTextureCoordinates); +} + +void ON_SubDMeshFragment::SetTextureCoordinateCorners( + bool bGridOrder, + const ON_2dPoint texture_coordinate_corners[4], + bool bSetTextureCoordinates +) const +{ + if (nullptr != texture_coordinate_corners) + { + // m_ctrlnetT[] is in grid order. + + // lower left + m_ctrlnetT[0][0] = texture_coordinate_corners[0].x; + m_ctrlnetT[0][1] = texture_coordinate_corners[0].y; + m_ctrlnetT[0][2] = 0.0; + + // lower right + m_ctrlnetT[1][0] = texture_coordinate_corners[1].x; + m_ctrlnetT[1][1] = texture_coordinate_corners[1].y; + m_ctrlnetT[1][2] = 0.0; + + // upper left + int i = bGridOrder ? 2 : 3; + m_ctrlnetT[2][0] = texture_coordinate_corners[i].x; + m_ctrlnetT[2][1] = texture_coordinate_corners[i].y; + m_ctrlnetT[2][2] = 0.0; + + // upper right + i = bGridOrder ? 3 : 2; + m_ctrlnetT[3][0] = texture_coordinate_corners[i].x; + m_ctrlnetT[3][1] = texture_coordinate_corners[i].y; + m_ctrlnetT[3][2] = 0.0; + + if (bSetTextureCoordinates) + SetTextureCoordinatesFromCorners(); + } +} + +void ON_SubDMeshFragment::SetTextureCoordinateCorners( + bool bGridOrder, + const ON_3dPoint texture_coordinate_corners[4], + bool bSetTextureCoordinates +) const +{ + if (nullptr != texture_coordinate_corners) + { + // m_ctrlnetT[] is in grid order. + + // lower left + m_ctrlnetT[0][0] = texture_coordinate_corners[0].x; + m_ctrlnetT[0][1] = texture_coordinate_corners[0].y; + m_ctrlnetT[0][2] = texture_coordinate_corners[0].z; + + // lower right + m_ctrlnetT[1][0] = texture_coordinate_corners[1].x; + m_ctrlnetT[1][1] = texture_coordinate_corners[1].y; + m_ctrlnetT[1][2] = texture_coordinate_corners[1].z; + + // upper left + int i = bGridOrder ? 2 : 3; + m_ctrlnetT[2][0] = texture_coordinate_corners[i].x; + m_ctrlnetT[2][1] = texture_coordinate_corners[i].y; + m_ctrlnetT[2][2] = texture_coordinate_corners[i].z; + + // upper right + i = bGridOrder ? 3 : 2; + m_ctrlnetT[3][0] = texture_coordinate_corners[i].x; + m_ctrlnetT[3][1] = texture_coordinate_corners[i].y; + m_ctrlnetT[3][2] = texture_coordinate_corners[i].z; + + if (bSetTextureCoordinates) + SetTextureCoordinatesFromCorners(); + } +} + +const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinate( + ON_2udex grid2dex +) const +{ + return VertexTextureCoordinate(m_grid.PointIndexFromGrid2dex(grid2dex.i, grid2dex.j)); +} + +const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinate( + unsigned grid2dex_i, + unsigned grid2dex_j +) const +{ + return VertexTextureCoordinate(m_grid.PointIndexFromGrid2dex(grid2dex_i, grid2dex_j)); +} + +const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinate( + unsigned grid_point_index +) const +{ + return + (grid_point_index >= TextureCoordinateCount()) + ? ON_3dPoint::NanPoint + : ON_3dPoint(m_T + grid_point_index * m_T_stride); +} + +const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinateFromCorners(unsigned grid2dex_i, unsigned grid2dex_j) const +{ + const unsigned n = m_grid.SideSegmentCount(); + if (n <= 0U || grid2dex_i > n || grid2dex_j > n) + return ON_3dPoint::NanPoint; + const double s = ((double)grid2dex_i) / ((double)n); + const double t = ((double)grid2dex_j) / ((double)n); + const double c[4] = { (1.0 - s)*(1.0 - t), s*(1.0 - t), (1.0 - s)*t, s*t }; + return ON_3dPoint( + c[0] * m_ctrlnetT[0][0] + c[1] * m_ctrlnetT[1][0] + c[2] * m_ctrlnetT[2][0] + c[3] * m_ctrlnetT[3][0], + c[0] * m_ctrlnetT[0][1] + c[1] * m_ctrlnetT[1][1] + c[2] * m_ctrlnetT[2][1] + c[3] * m_ctrlnetT[3][1], + c[0] * m_ctrlnetT[0][2] + c[1] * m_ctrlnetT[1][2] + c[2] * m_ctrlnetT[2][2] + c[3] * m_ctrlnetT[3][2] + ); +} + +const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinateFromCorners(ON_2udex grid2dex) const +{ + return VertexTextureCoordinateFromCorners(grid2dex.i, grid2dex.j); +} + +const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinateFromCorners(unsigned grid_point_index) const +{ + return VertexTextureCoordinateFromCorners(m_grid.Grid2dexFromPointIndex(grid_point_index)); +} + + +void ON_SubDMeshFragment::SetTextureCoordinatesFromCorners() const +{ + const unsigned n = m_grid.SideSegmentCount(); + if (n <= 0U || n > ON_SubDMeshFragment::MaximumSideSegmentCount) + return; + if (TextureCoordinateCapacity() < n*n) + return; + SetTextureCoordinatesExist(true); + double * T = m_T; + const double d = (double)n; + double s, t; + ON_3dPoint tc; + for (unsigned j = 0U; j <= n; ++j) + { + t = ((double)j) / d; + for (unsigned i = 0U; i <= n; ++i) + { + s = ((double)i) / d; + const double c[4] = { (1.0 - s)*(1.0 - t), s*(1.0 - t), (1.0 - s)*t, s*t }; + tc = ON_3dPoint( + c[0] * m_ctrlnetT[0][0] + c[1] * m_ctrlnetT[1][0] + c[2] * m_ctrlnetT[2][0] + c[3] * m_ctrlnetT[3][0], + c[0] * m_ctrlnetT[0][1] + c[1] * m_ctrlnetT[1][1] + c[2] * m_ctrlnetT[2][1] + c[3] * m_ctrlnetT[3][1], + c[0] * m_ctrlnetT[0][2] + c[1] * m_ctrlnetT[1][2] + c[2] * m_ctrlnetT[2][2] + c[3] * m_ctrlnetT[3][2] + ); + T[0] = tc.x; + T[1] = tc.y; + T[2] = tc.z; + T += m_T_stride; + } + } + return; +} + +bool ON_SubDMeshFragment::SetVertexTextureCoordinate( + ON_2udex grid2dex, + ON_3dPoint texture_coordinate +) const +{ + return SetVertexTextureCoordinate(m_grid.PointIndexFromGrid2dex(grid2dex.i, grid2dex.j), texture_coordinate); +} + +bool ON_SubDMeshFragment::SetVertexTextureCoordinate( + unsigned grid2dex_i, + unsigned grid2dex_j, + ON_3dPoint texture_coordinate +) const +{ + return SetVertexTextureCoordinate(m_grid.PointIndexFromGrid2dex(grid2dex_i, grid2dex_j), texture_coordinate); +} + +bool ON_SubDMeshFragment::SetVertexTextureCoordinate( + unsigned grid_point_index, + ON_3dPoint texture_coordinate +) const +{ + if (grid_point_index >= TextureCoordinateCapacity()) + return false; + double* T = (m_T + grid_point_index * m_T_stride); + T[0] = texture_coordinate.x; + T[1] = texture_coordinate.y; + T[2] = texture_coordinate.z; + SetTextureCoordinatesExist(true); + return true; +} + +#pragma endregion +#endif diff --git a/opennurbs_terminator.cpp b/opennurbs_terminator.cpp index d4b1ca6b..78d808d4 100644 --- a/opennurbs_terminator.cpp +++ b/opennurbs_terminator.cpp @@ -16,7 +16,6 @@ ON_Terminator::ON_Terminator() m_reserved1[3] = false; m_reserved1[4] = false; m_reserved1[5] = false; - m_reserved1[6] = false; m_reserved2[0] = 0; m_reserved2[1] = 0; } @@ -84,6 +83,16 @@ bool ON_Terminator::TerminationRequested( ON_Terminator* terminator ) return TerminationRequestedExpert( terminator, CLOCKS_PER_SEC/10 ); } +void ON_Terminator::SetAllowDefaultCancellationHandling(bool bAllow) +{ + m_bAllowDefaultCancellationHandling = bAllow; +} + +bool ON_Terminator::AllowDefaultCancellationHandling(void) const +{ + return m_bAllowDefaultCancellationHandling; +} + bool ON_Terminator::TerminationRequestedExpert( ON_Terminator* terminator, ON__UINT64 callback_delta diff --git a/opennurbs_terminator.h b/opennurbs_terminator.h index d0875f5e..db83bf9b 100644 --- a/opennurbs_terminator.h +++ b/opennurbs_terminator.h @@ -148,12 +148,29 @@ public: ON__UINT64 callback_delta ); + + /* + Description: + If bAllow is true, the worker thread cancellation handling (escape key presses etc) will cause RequestTermination to be called. + If bAllow is false, cancellation must be implemented through the callback function, otherwise all cancellation will be disabled. + Parameters: + bAllow - [in] (see above) + */ + void SetAllowDefaultCancellationHandling(bool bAllow); + + /* + Description: + returns true if default cancellation processing is allowed, otherwise false. See SetAllowDefaultCancellationHandling + */ + bool AllowDefaultCancellationHandling(void) const; + private: bool (*m_callback_function)(ON__UINT_PTR) = nullptr; ON__UINT_PTR m_callback_context = 0; ON__UINT64 m_previous_query_clock = 0; bool m_bTerminationRequested = false; - bool m_reserved1[7]; + bool m_bAllowDefaultCancellationHandling = true; + bool m_reserved1[6]; ON__UINT_PTR m_thread_id = 0; ON__UINT64 m_reserved2[2]; }; diff --git a/opennurbs_text.cpp b/opennurbs_text.cpp index c308b254..824c6476 100644 --- a/opennurbs_text.cpp +++ b/opennurbs_text.cpp @@ -79,6 +79,7 @@ void ON_TextContent::Internal_CopyFrom( { m__wrapped_runs = new ON_TextRunArray(*src.m__wrapped_runs); } + m_runtime_halign = src.m_runtime_halign; } ON_TextContent::ON_TextContent(const ON_TextContent& src) @@ -362,6 +363,11 @@ const ON_wString ON_TextContent::PlainTextWithFields() const return Internal_GetPlainText(false, false); } +const ON_wString ON_TextContent::PlainTextWithFields(ON_SimpleArray* runmap) const +{ + return Internal_GetPlainText(false, false, runmap); +} + const ON_wString ON_TextContent::PlainText() const { @@ -414,6 +420,13 @@ ON_SHA1_Hash ON_TextRunArray::TextRunArrayContentHash( ON_SHA1_Hash ON_TextContent::TextContentHash() const { + size_t size = sizeof(*this); + size_t runs_offset = ((bool*)&m__runs) - ((bool*)this); + size_t wrap_offset = ((bool*)&m_bWrapText) - ((bool*)this); + size = size; // 216 + runs_offset = runs_offset; // 56 + wrap_offset = wrap_offset; // 50 + return TextContentHash(true, true); } @@ -486,11 +499,18 @@ ON_SHA1_Hash ON_TextContent::TextContentHash( } const ON_wString ON_TextContent::Internal_GetPlainText(bool evaluate_fields, bool wrapped) const +{ + return Internal_GetPlainText(evaluate_fields, wrapped, nullptr); +} + +const ON_wString ON_TextContent::Internal_GetPlainText(bool evaluate_fields, bool wrapped, ON_SimpleArray* runmap) const { ON_wString plaintext; ON_TextRunArray* runs = TextRuns(!wrapped); if (nullptr == runs) return plaintext; + if (nullptr != runmap) + runmap->Empty(); int runcount = runs->Count(); int nlcount = 0; @@ -520,7 +540,17 @@ const ON_wString ON_TextContent::Internal_GetPlainText(bool evaluate_fields, boo const wchar_t* str = evaluate_fields ? run->DisplayString() : run->TextString(); - plaintext += str; + if (nullptr != str) + { + if (nullptr != runmap) + { + ON_3dex& map = runmap->AppendNew(); + map.i = ri; + map.j = plaintext.Length(); + map.k = (int)wcslen(str); + } + plaintext += str; + } } else if (multiline) { @@ -654,6 +684,84 @@ bool ON_TextContent::RebuildRuns( return rc; } + +bool ON_TextContent::RunReplaceString( + const wchar_t* repl_str, + int start_run_idx, + int start_run_pos, + int end_run_idx, + int end_run_pos) +{ + if (nullptr == repl_str) + repl_str = L""; + const ON_TextRunArray* text_runs = TextRuns(true); + if (nullptr == text_runs) + return false; + if (0 > start_run_idx || 0 > start_run_pos || start_run_idx > end_run_idx || end_run_idx >= text_runs->Count()) + return false; + const ON_TextRun* start_run = (*text_runs)[start_run_idx]; + const ON_TextRun* end_run = (*text_runs)[end_run_idx]; + if (nullptr == start_run || nullptr == end_run) + return false; + if (start_run->Type() != ON_TextRun::RunType::kText && start_run->Type() != ON_TextRun::RunType::kField) + return false; + if (end_run->Type() != ON_TextRun::RunType::kText && end_run->Type() != ON_TextRun::RunType::kField) + return false; + ON_wString start_run_string = start_run->TextString(); + ON_wString end_run_string = end_run->TextString(); + int start_string_length = start_run_string.Length(); + int end_string_length = end_run_string.Length(); + if (start_run_pos >= start_string_length || end_run_pos >= end_string_length) + return false; + + ON_wString new_string; + if (start_run_pos > 0) + new_string = start_run_string.Left(start_run_pos); + new_string += repl_str; + if (end_run_idx == start_run_idx) + new_string += start_run_string.Right(end_string_length - end_run_pos - 1); + + ON__UINT32* cp = nullptr; + int cpcount = ON_TextContext::ConvertStringToCodepoints(new_string, cp); + + ON_TextContent* new_text_content = Duplicate(); + ON_TextRunArray* new_text_runs = new_text_content->TextRuns(true); + ON_TextRun* new_start_run = ON_TextRun::GetManagedTextRun(*(*text_runs)[start_run_idx]); + new_start_run->SetUnicodeString(cpcount, cp); + new_text_runs->RemoveRun(start_run_idx); + new_text_runs->InsertRun(start_run_idx, new_start_run); + + int remcount = 0; + for (int ri = start_run_idx+1; ri < end_run_idx; ri++) + { + new_text_runs->RemoveRun(ri); + remcount++; + } + end_run_idx -= remcount; + + if (end_run_idx > start_run_idx) + { + if (end_run_pos < end_string_length - 1) + { + new_string = end_run_string.Right(end_string_length - end_run_pos - 1); + ON_TextRun* new_end_run = ON_TextRun::GetManagedTextRun(*(*text_runs)[end_run_idx]); + + cp = nullptr; + cpcount = ON_TextContext::ConvertStringToCodepoints(new_string, cp); + new_end_run->SetUnicodeString(cpcount, cp); + new_text_runs->RemoveRun(end_run_idx); + new_text_runs->InsertRun(end_run_idx, new_end_run); + } + else + new_text_runs->RemoveRun(end_run_idx); + } + + m__runs = *new_text_runs; + + return true; +} + + //----------------------- void ON_TextContent::Internal_SetRunTextHeight(double height) @@ -669,6 +777,17 @@ void ON_TextContent::Internal_SetRunTextHeight(double height) WrapText(w); } + +ON::TextHorizontalAlignment ON_TextContent::RuntimeHorizontalAlignment() const +{ + return m_runtime_halign; +} + +void ON_TextContent::SetRuntimeHorizontalAlignment(ON::TextHorizontalAlignment halign) const +{ + m_runtime_halign = halign; +} + void ON_TextContent::GetAlignment(ON::TextHorizontalAlignment& horz, ON::TextVerticalAlignment& vert) const { horz = m_h_align; @@ -2018,7 +2137,8 @@ bool ON_TextContent::FormatTolerance( double tol_uv = dimstyle->ToleranceUpperValue(); tol_uv *= length_factor; - if (ON_NumberFormatter::FormatLength(tol_uv, dim_length_display, 0.0, tolprecision, zs, bracket_stack_frac, sTol)) + wchar_t decimal_char = dimstyle->DecimalSeparator(); + if (ON_TextContent::FormatLength(tol_uv, dim_length_display, 0.0, tolprecision, zs, bracket_stack_frac, decimal_char, sTol)) { formatted_string += ON_wString::PlusMinusSymbol; formatted_string += sTol; @@ -2047,10 +2167,11 @@ bool ON_TextContent::FormatTolerance( tol_lv_sign = L'+'; tol_lv = fabs(tol_lv); + wchar_t decimal_char = dimstyle->DecimalSeparator(); ON_wString sTol_uv, sTol_lv; // Can't use stacked fractions in tolerances because run stacking isn't recursive in ON_Text - if (ON_NumberFormatter::FormatLength(tol_uv, dim_length_display, 0.0, tolprecision, zs, false, sTol_uv) && - ON_NumberFormatter::FormatLength(tol_lv, dim_length_display, 0.0, tolprecision, zs, false, sTol_lv)) + if (ON_TextContent::FormatLength(tol_uv, dim_length_display, 0.0, tolprecision, zs, false, decimal_char, sTol_uv) && + ON_TextContent::FormatLength(tol_lv, dim_length_display, 0.0, tolprecision, zs, false, decimal_char, sTol_lv)) { formatted_string += L" [["; formatted_string += L"|"; @@ -2073,10 +2194,12 @@ bool ON_TextContent::FormatTolerance( tol_uv = distance + tol_uv; tol_lv = distance - tol_lv; + + wchar_t decimal_char = dimstyle->DecimalSeparator(); ON_wString sDist_u, sDist_l; // Can't use stacked fractions in tolerances because run stacking isn't recursive in ON_Text - if (ON_NumberFormatter::FormatLength(tol_uv, dim_length_display, 0.0, tolprecision, zs, false, sDist_u) && - ON_NumberFormatter::FormatLength(tol_lv, dim_length_display, 0.0, tolprecision, zs, false, sDist_l)) + if (ON_TextContent::FormatLength(tol_uv, dim_length_display, 0.0, tolprecision, zs, false, decimal_char, sDist_u) && + ON_TextContent::FormatLength(tol_lv, dim_length_display, 0.0, tolprecision, zs, false, decimal_char, sDist_l)) { formatted_string += L" [["; formatted_string += L"|"; @@ -2136,7 +2259,8 @@ bool ON_TextContent::FormatDistance( if (fabs(distance) < pow(10.0, -(precision + 1))) distance = 0.0; - ON_NumberFormatter::FormatLength(distance, dim_length_display, roundoff, precision, zs, bracket_stack_frac, formatted_string); + wchar_t decimal_char = dimstyle->DecimalSeparator(); + ON_TextContent::FormatLength(distance, dim_length_display, roundoff, precision, zs, bracket_stack_frac, decimal_char, formatted_string); return true; } @@ -2279,7 +2403,9 @@ bool ON_TextContent::FormatAngleMeasurement( double roundoff = dimstyle->AngleRoundOff(); int resolution = dimstyle->AngleResolution(); ON_DimStyle::suppress_zero suppress = dimstyle->AngleZeroSuppress(); - ON_NumberFormatter::FormatAngleStringDecimal(angle, resolution, roundoff, suppress, sAngle); + + wchar_t decimal_char = dimstyle->DecimalSeparator(); + ON_TextContent::FormatAngleStringDecimal(angle, resolution, roundoff, suppress, decimal_char, sAngle); if (ON_DimStyle::angle_format::DecimalDegrees == dimstyle->AngleFormat()) sAngle += ON_wString::DegreeSymbol; else if (ON_DimStyle::angle_format::Radians == dimstyle->AngleFormat()) @@ -2289,7 +2415,8 @@ bool ON_TextContent::FormatAngleMeasurement( } else if (ON_DimStyle::angle_format::DegMinSec == dimstyle->AngleFormat()) { - ON_NumberFormatter::FormatAngleStringDMS(angle_radians, sAngle); + wchar_t decimal_char = dimstyle->DecimalSeparator(); + ON_TextContent::FormatAngleStringDMS(angle_radians, decimal_char, sAngle); } formatted_string += sAngle; @@ -2303,6 +2430,103 @@ bool ON_TextContent::FormatAngleMeasurement( return true; } +bool ON_TextContent::FormatLength( + double distance, + ON_DimStyle::LengthDisplay output_lengthdisplay, + double round_off, + int resolution, + ON_DimStyle::suppress_zero zero_suppress, + bool bracket_fractions, + wchar_t decimal_char, + ON_wString & output) +{ + bool rc = ON_NumberFormatter::FormatLength( distance, output_lengthdisplay, round_off, resolution, zero_suppress, bracket_fractions, output); + if (rc && ON_wString::DecimalAsPeriod != decimal_char) + output.Replace(ON_wString::DecimalAsPeriod, decimal_char); + return rc; +} + +bool ON_TextContent::FormatAngleStringDMS( + double angle_degrees, + wchar_t decimal_char, + ON_wString& formatted_string) +{ + bool rc = ON_NumberFormatter::FormatAngleStringDMS(angle_degrees, formatted_string); + if (rc && ON_wString::DecimalAsPeriod != decimal_char) + formatted_string.Replace(ON_wString::DecimalAsPeriod, decimal_char); + return rc; +} + +bool ON_TextContent::FormatAngleStringDecimal( + double angle_radians, + int resolution, + double roundoff, + ON_DimStyle::suppress_zero zero_suppression, + wchar_t decimal_char, + ON_wString& formatted_string) +{ + bool rc = ON_NumberFormatter::FormatAngleStringDecimal(angle_radians, resolution, roundoff, zero_suppression, formatted_string); + if (rc && ON_wString::DecimalAsPeriod != decimal_char) + formatted_string.Replace(ON_wString::DecimalAsPeriod, decimal_char); + return rc; +} + +bool ON_TextContent::FormatArea( + double area_in, + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + bool alt, + ON_wString& formatted_string) +{ + double area = area_in; + + if (nullptr == dimstyle) + dimstyle = &ON_DimStyle::Default; + + const ON_DimStyle::LengthDisplay dim_length_display = + alt + ? dimstyle->AlternateDimensionLengthDisplay() + : dimstyle->DimensionLengthDisplay(); + + const ON::LengthUnitSystem dim_us + = alt + ? dimstyle->AlternateDimensionLengthDisplayUnit(0) + : dimstyle->DimensionLengthDisplayUnit(0); + + double length_factor = + alt + ? dimstyle->AlternateLengthFactor() + : dimstyle->LengthFactor(); + + double unit_length_factor = ON::UnitScale(units_in, dim_us); + area = unit_length_factor * unit_length_factor * length_factor * area_in; + + double roundoff = alt ? dimstyle->AlternateRoundOff() : dimstyle->RoundOff(); + int precision = alt ? dimstyle->AlternateLengthResolution() : dimstyle->LengthResolution(); + ON_DimStyle::suppress_zero zs = alt ? dimstyle->AlternateZeroSuppress() : dimstyle->ZeroSuppress(); + + if (fabs(area) < pow(10.0, -(precision + 1))) + area = 0.0; + + wchar_t decimal_char = dimstyle->DecimalSeparator(); + + ON_DimStyle::OBSOLETE_length_format output_format = ON_DimStyle::OBSOLETE_length_format::Decimal; + + bool rc = ON_NumberFormatter::FormatNumber( + area, + output_format, + roundoff, + precision, + zs, + false, + formatted_string); + + if (rc && ON_wString::DecimalAsPeriod != decimal_char) + formatted_string.Replace(ON_wString::DecimalAsPeriod, decimal_char); + + return rc; +} + ON::TextVerticalAlignment ON::TextVerticalAlignmentFromUnsigned( unsigned int vertical_alignment_as_unsigned ) @@ -2330,6 +2554,7 @@ ON::TextHorizontalAlignment ON::TextHorizontalAlignmentFromUnsigned( ON_ENUM_FROM_UNSIGNED_CASE(ON::TextHorizontalAlignment::Left); ON_ENUM_FROM_UNSIGNED_CASE(ON::TextHorizontalAlignment::Center); ON_ENUM_FROM_UNSIGNED_CASE(ON::TextHorizontalAlignment::Right); + ON_ENUM_FROM_UNSIGNED_CASE(ON::TextHorizontalAlignment::Auto); } ON_ERROR("invalid vertical_alignment_as_unsigned parameter."); return (ON::TextHorizontalAlignment::Left); diff --git a/opennurbs_text.h b/opennurbs_text.h index 555f7163..178c4d9e 100644 --- a/opennurbs_text.h +++ b/opennurbs_text.h @@ -103,6 +103,14 @@ public: const ON_DimStyle* dimstyle ); + bool RunReplaceString( + const wchar_t* repl_str, + int start_run_idx, + int start_run_pos, + int end_run_idx, + int end_run_pos); + + /* Returns: The value of ON_DimStyle.TextPositionPropertiesHash() of the ON_DimStyle @@ -205,6 +213,16 @@ public: Fields are not evaluated */ const ON_wString PlainTextWithFields() const; + + /* + Returns the same string as PlainTextWithFields() and fills in the runmap + Each 3dex in the runmap array is + i: run index, + j: position in the string where text from run[i] starts, + k: length of text from run[i] + */ + const ON_wString PlainTextWithFields(ON_SimpleArray* runmap) const; + /* Same as PlainTextWithFields() but separated wrapped run lines with '\n' for soft return and '\r''\n' for hard returns @@ -271,6 +289,7 @@ private: void Internal_DeleteWrappedRuns() const; const ON_wString Internal_GetPlainText(bool evaluate_fields, bool wrapped) const; + const ON_wString Internal_GetPlainText(bool evaluate_fields, bool wrapped, ON_SimpleArray* runmap) const; public: @@ -334,6 +353,9 @@ public: //void SetCurrentDimStyle(const ON_DimStyle* dimstyle) const; + ON::TextHorizontalAlignment RuntimeHorizontalAlignment() const; + void SetRuntimeHorizontalAlignment(ON::TextHorizontalAlignment halign) const; + private: // Data members //----------------------- @@ -349,6 +371,8 @@ private: // Query by calling TextIsWrapped(). mutable bool m_bWrapText = false; + mutable ON::TextHorizontalAlignment m_runtime_halign = ON::TextHorizontalAlignment::Auto; + // m__runs and m__wrapped_runs are runtime information // generated by parsing m_text and other information. mutable ON_TextRunArray m__runs; @@ -501,6 +525,47 @@ public: const wchar_t* user_text, ON_wString& formatted_string); + static bool FormatLength( + double distance, + ON_DimStyle::LengthDisplay output_lengthdisplay, + double round_off, + int resolution, + ON_DimStyle::suppress_zero zero_suppress, + bool bracket_fractions, + wchar_t decimal_char, + ON_wString& output); + + static bool FormatAngleStringDMS( + double angle_degrees, + wchar_t decimal_char, + ON_wString& formatted_string); + + static bool FormatAngleStringDecimal( + double angle_radians, + int resolution, + double roundoff, + ON_DimStyle::suppress_zero zero_suppression, + wchar_t decimal_char, + ON_wString& formatted_string); + + // Area formatting + static bool FormatArea( + double area, + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + bool alternate, // Primary or alternate + ON_wString& formatted_string); // Output + + static bool FormatArea( + double area, + ON_DimStyle::LengthDisplay output_lengthdisplay, + double round_off, + int resolution, + ON_DimStyle::suppress_zero zero_suppress, + wchar_t decimal_char, + ON_wString& output); + + }; diff --git a/opennurbs_textiterator.cpp b/opennurbs_textiterator.cpp index 06657d40..5177b454 100644 --- a/opennurbs_textiterator.cpp +++ b/opennurbs_textiterator.cpp @@ -1821,6 +1821,8 @@ void ON_RtfStringBuilder::GroupBegin() PushRun(m_current_run); m_current_run.AddControl(L"{"); + m_current_run.SetType(ON_TextRun::RunType::kText); + m_current_run.ClearHasContent(); m_have_rtf = true; m_level++; } @@ -2161,14 +2163,23 @@ bool ON_RtfStringBuilder::AppendCodePoint(ON__UINT32 codept) if ((SettingFacename() || SkippingFacename()) && m_current_run.Type() == ON_TextRun::RunType::kFontdef) return true; - if (!m_have_rtf) + if (m_current_run.Type() == ON_TextRun::RunType::kText) { - if (MakeBold()) + if (MakeBold() && !m_current_run.IsBold()) + { m_current_run.AddControl(L"\\b"); - if (MakeItalic()) + m_current_run.SetBold(true); + } + if (MakeItalic() && !m_current_run.IsItalic()) + { m_current_run.AddControl(L"\\i"); - if (MakeUnderline()) + m_current_run.SetItalic(true); + } + if (MakeUnderline() && !m_current_run.IsUnderlined()) + { m_current_run.AddControl(L"\\ul"); + m_current_run.SetUnderlined(true); + } m_have_rtf = true; } @@ -3133,16 +3144,31 @@ bool RtfComposer::Compose( run_strings += L"\\b"; addspace = true; } + else if(style_bold) + { + run_strings += L"\\b0"; + addspace = true; + } if (run_font->IsItalic()) { run_strings += L"\\i"; addspace = true; } + else if (style_italic) + { + run_strings += L"\\i0"; + addspace = true; + } if (run_font->IsUnderlined()) { run_strings += L"\\ul"; addspace = true; } + else if (style_underline) + { + run_strings += L"\\ul0"; + addspace = true; + } if (addspace) run_strings += L" "; } @@ -3199,7 +3225,6 @@ bool RtfComposer::Compose( rtf.Format(L"{\\rtf1"); if (0 < nfont) - { ON_wString fonttable_string; temp.Format(L"\\deff%d", deffont_key); @@ -3211,9 +3236,18 @@ bool RtfComposer::Compose( temp.Format(L"{\\f%d %s;}", fi, fonttable[fi]); fonttable_string += temp; } - fonttable_string += "}\\fs40 "; rtf += fonttable_string; + } + + rtf += L"}\\fs40"; + if (style_bold) + rtf += L"\\b"; + if (style_italic) + rtf += L"\\i"; + if (style_underline) + rtf += L"\\ul"; + rtf += run_strings; // backing out the change made for RH-49725 because it causes the problem reported in RH-50733 rtf += L"\\par}"; diff --git a/opennurbs_textiterator.h b/opennurbs_textiterator.h index df7846b1..9a960764 100644 --- a/opennurbs_textiterator.h +++ b/opennurbs_textiterator.h @@ -546,6 +546,16 @@ public: { return m_text; } + + const bool HasContent() + { + return m_has_content; + } + + const void ClearHasContent() + { + m_has_content = false; + } private: bool m_has_content = false; diff --git a/opennurbs_textlog.cpp b/opennurbs_textlog.cpp index 96d52b6b..db1b0c12 100644 --- a/opennurbs_textlog.cpp +++ b/opennurbs_textlog.cpp @@ -496,6 +496,9 @@ void ON_TextLog::Print( const ON_COMPONENT_INDEX& ci ) case ON_COMPONENT_INDEX::subd_face: Print("subd_face(%d)",ci.m_index); break; + case ON_COMPONENT_INDEX::hatch_loop: + Print("hatch_loop(%d)", ci.m_index); + break; case ON_COMPONENT_INDEX::dim_linear_point: Print("dim_linear_point(%d)",ci.m_index); break; @@ -511,6 +514,12 @@ void ON_TextLog::Print( const ON_COMPONENT_INDEX& ci ) case ON_COMPONENT_INDEX::dim_text_point: Print("dim_text_point(%d)",ci.m_index); break; + case ON_COMPONENT_INDEX::dim_centermark_point: + Print("dim_centermark_point(%d)", ci.m_index); + break; + case ON_COMPONENT_INDEX::dim_leader_point: + Print("dim_leader_point(%d)", ci.m_index); + break; case ON_COMPONENT_INDEX::no_type: Print("no_type(%d)",ci.m_index); break; diff --git a/opennurbs_textobject.cpp b/opennurbs_textobject.cpp index d21a48be..83c53e38 100644 --- a/opennurbs_textobject.cpp +++ b/opennurbs_textobject.cpp @@ -309,11 +309,11 @@ bool ON_Text::GetTextXform( ON_Xform rotation_xf(ON_Xform::IdentityTransformation); ON_3dVector view_x = nullptr == vp ? ON_3dVector::XAxis : vp->CameraX(); ON_3dVector view_y = nullptr == vp ? ON_3dVector::YAxis : vp->CameraY(); + ON_3dVector view_z = nullptr == vp ? ON_3dVector::ZAxis : vp->CameraZ(); if ( ON::TextOrientation::InView == dimstyle->TextOrientation() ) // Draw text horizontal and flat to the screen { ON_Xform tp2sxf; // Text point to view plane rotation - ON_3dVector view_z = ON_CrossProduct(view_x, view_y); ON_3dPoint text_point_3d = Plane().origin; ON_3dVector text_xdir = textobjectplane.xaxis; ON_3dVector text_ydir = textobjectplane.yaxis; @@ -349,25 +349,38 @@ bool ON_Text::GetTextXform( text_center = (text_corners[0] + text_corners[2]) / 2.0; ON_3dVector text_xdir = textobjectplane.xaxis; ON_3dVector text_ydir = textobjectplane.yaxis; + ON_3dVector text_zdir = textobjectplane.zaxis; if (nullptr != model_xform) { text_xdir.Transform(*model_xform); text_ydir.Transform(*model_xform); + text_zdir.Transform(*model_xform); } if (fabs(textrotation) > ON_SQRT_EPSILON) { text_xdir.Rotate(textrotation, textobjectplane.zaxis); text_ydir.Rotate(textrotation, textobjectplane.zaxis); } - bool fx = (-0.01 > view_x * text_xdir); // text xdir doesn't match view xdir - bool fy = (-0.01 > view_y * text_ydir); // text ydir doesn't match view ydir - ON_Xform mxf; // Mirror xform for backwards text - if (fx) + + bool flip_x = false; + bool flip_y = false; + + const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : cos(80.001 * ON_DEGREES_TO_RADIANS); + CalcTextFlip( + text_xdir, text_ydir, text_zdir, + view_x, view_y, view_z, + model_xform, + fliptol, + flip_x, + flip_y); + + ON_Xform mxf; // Mirror xform for backwards text to adjust DrawForward + if (flip_x) { mxf.Mirror(text_center, ON_3dVector::XAxis); textscale_xf = textscale_xf * mxf; } - if (fy) + if (flip_y) { mxf.Mirror(text_center, ON_3dVector::YAxis); textscale_xf = textscale_xf * mxf; diff --git a/opennurbs_textrun.cpp b/opennurbs_textrun.cpp index 112b3c2d..b7100f1f 100644 --- a/opennurbs_textrun.cpp +++ b/opennurbs_textrun.cpp @@ -1018,24 +1018,6 @@ void ON_TextRun::SetUnicodeString(ON__UINT32*& dest, size_t count, const ON__UIN } } -static int ConvertCpToWChar(ON__UINT32 cp, wchar_t wch[3]) -{ - unsigned int error_status = 0; - int ok = ON_ConvertUTF32ToWideChar( - 0, //int bTestByteOrder, - &cp, //const ON__UINT32* sUTF32, - 1, //int sUTF32_count, - wch, //wchar_t* sWideChar, - 3, //int sWideChar_count, - &error_status, //unsigned int* error_status, - 0xFFFFFFFF, //unsigned int error_mask, - 0xFFFD, //ON__UINT32 error_code_point, - 0 //const ON__UINT32** sNextUTF32 - ); - - return ok; -} - int ON_TextRun::WrapTextRun( int call_count, // recursion depth int start_char_offset, // char offset in cp array @@ -1064,13 +1046,22 @@ int ON_TextRun::WrapTextRun( linewidth = 0.0; } - const ON__UINT32* cp = UnicodeString(); - int cpcount = (int)CodepointCount(cp); + const wchar_t* display_string = DisplayString(); + + size_t wcscount = 0; + if (nullptr != display_string) + wcscount = wcslen(display_string); + const ON_Font* font = Font(); if (nullptr == font) return 0; double height_scale = HeightScale(font); // Font units to world units + ON_SimpleArray< const ON_FontGlyph*> glyph_list; + ON_TextBox text_box; + + ON_FontGlyph::GetGlyphList(display_string, font, ON_NextLine, glyph_list, text_box); + const ON_FontGlyph* Aglyph = font->CodePointGlyph((ON__UINT32)L'A'); if (nullptr == Aglyph) return 0; @@ -1091,19 +1082,19 @@ int ON_TextRun::WrapTextRun( //double runwidth0 = 0.0; // run width including trailing spaces if (0 == start_char_offset) // using the whole run { - runwidth = Advance().x; + //runwidth = Advance().x; + runwidth = text_box.m_advance.i * height_scale; } else // Part of the run has already been picked off and added to the previous line { // Find width of remaining characters - for (int ci = start_char_offset; ci < cpcount; ci++) + for (int ci = start_char_offset; ci < (int)wcscount; ci++) { - const ON_FontGlyph* gi = font->CodePointGlyph(cp[ci]); - wchar_t wch[3] = { 0, 0, 0 }; + const ON_FontGlyph* gi = glyph_list[ci]; + if (nullptr != gi) { - int wccnt = ConvertCpToWChar(gi->CodePoint(), wch); - if (1 == wccnt) + //if (not surrogate pair) { const ON_TextBox glyph_box = gi->GlyphBox(); double charwidth = glyph_box.m_advance.i * height_scale; @@ -1118,7 +1109,8 @@ int ON_TextRun::WrapTextRun( #pragma endregion Run Width #pragma region Whole Run - if (runwidth + linewidth <= wrapwidth || 2 > cpcount) + wchar_t* temp_display_str = (wchar_t*)onmalloc((wcscount + 1) * sizeof(wchar_t)); + if (runwidth + linewidth <= wrapwidth || 2 > wcscount) { // Adding this entire run won't go past wrap width // or the run has only 0 or 1 character and can't be wrapped @@ -1133,10 +1125,15 @@ int ON_TextRun::WrapTextRun( else { if (0 != start_char_offset) - newrun->SetUnicodeString(cpcount - start_char_offset, cp + start_char_offset); + { + wcsncpy(temp_display_str, display_string + start_char_offset, wcscount - start_char_offset); + temp_display_str[wcscount - start_char_offset] = 0; + newrun->SetDisplayString(temp_display_str); + } linewidth += runwidth; } newruns.AppendRun(newrun); + onfree(temp_display_str); return 1; // 1 new run was added } } @@ -1149,14 +1146,11 @@ int ON_TextRun::WrapTextRun( double curwidth = 0.0; double linefeedheight = font->FontMetrics().LineSpace() * height_scale; - for (int ci = start_char_offset; ci < cpcount; ci++) + for (int ci = start_char_offset; ci < (int)wcscount; ci++) { - const ON_FontGlyph* gi = font->CodePointGlyph(cp[ci]); - wchar_t wch[3] = { 0, 0, 0 }; + const ON_FontGlyph* gi = glyph_list[ci]; if (nullptr != gi) { - int wccnt = ConvertCpToWChar(gi->CodePoint(), wch); - if (1 == wccnt) { const ON_TextBox glyph_box = gi->GlyphBox(); curwidth += glyph_box.m_advance.i * height_scale; @@ -1177,7 +1171,9 @@ int ON_TextRun::WrapTextRun( if (nullptr != newrun) { *newrun = *this; - newrun->SetUnicodeString(run_length, cp + start_char_offset); + wcsncpy(temp_display_str, display_string + start_char_offset, run_length); + temp_display_str[run_length] = 0; + newrun->SetDisplayString(temp_display_str); newrun->SetOffset(ON_2dVector(0.0, y_offset + Offset().y)); newruns.AppendRun(newrun); } @@ -1198,9 +1194,10 @@ int ON_TextRun::WrapTextRun( } int wrapcount = WrapTextRun(call_count + 1, run_length + start_char_offset, wrapwidth, y_offset, linewidth, newruns); + onfree(temp_display_str); return new_count + wrapcount; } - if (iswspace(wch[0])) + if (iswspace(display_string[ci])) { found_space = true; last_space = ci; @@ -1213,12 +1210,13 @@ int ON_TextRun::WrapTextRun( if (nullptr != newrun) { *newrun = *this; - newrun->SetUnicodeString(run_length, cp + start_char_offset); + wcsncpy(temp_display_str, display_string + start_char_offset, run_length); + temp_display_str[run_length] = 0; newrun->SetOffset(ON_2dVector(0.0, y_offset + Offset().y)); newruns.AppendRun(newrun); new_count += 1; } - + onfree(temp_display_str); return new_count; } diff --git a/opennurbs_texture.h b/opennurbs_texture.h index cf1d65e7..a2542ffc 100644 --- a/opennurbs_texture.h +++ b/opennurbs_texture.h @@ -294,6 +294,13 @@ public: // other texture settings. bool m_bOn = true; + // If false, the texture color values should be correctly by the linear workflow pre-process gamma value (in the document) + // if linear workflow is on. Otherwise, if the values is true, the values should be used raw from the texture. + bool m_bTreatAsLinear = false; + + bool reserved2 = false; + bool reserved3 = false; + // do not change TYPE enum values - they are saved in 3dm files. // The "TYPE" setting controls how the pixels in the bitmap // are interpreted. @@ -301,9 +308,36 @@ public: { no_texture_type = 0U, - bitmap_texture = 1U, // "standard" image texture. + bitmap_texture = 1U, // "standard" image texture. // Deprecated. Use Diffuse. + diffuse_texture = 1U, // ideally albedo. bump_texture = 2U, // bump map - see m_bump_scale comment - transparency_texture = 3U, // value = alpha (see m_tranparancy_id) + transparency_texture = 3U, // value = alpha (see m_tranparancy_id) Deprecated. Use Opacity. No change needed to functionality - transparency in Rhino has always meant opacity. + opacity_texture = 3U, // value = alpha. + + // The following textures are only for PBR materials + // They are not supported by the basic ON_Material definition, and should only be used when + // rendering physically based (PBR) materials. + pbr_base_color_texture = 1U, //Reuse diffuse texture. + pbr_subsurface_texture = 10U, + pbr_subsurface_scattering_texture = 11U, + pbr_subsurface_scattering_radius_texture = 12U, + pbr_metallic_texture = 13U, + pbr_specular_texture = 14U, + pbr_specular_tint_texture = 15U, + pbr_roughness_texture = 16U, + pbr_anisotropic_texture = 17U, + pbr_anisotropic_rotation_texture = 18U, + pbr_sheen_texture = 19U, + pbr_sheen_tint_texture = 20U, + pbr_clearcoat_texture = 21U, + pbr_clearcoat_roughness_texture = 22U, + pbr_opacity_ior_texture = 23U, + pbr_opacity_roughness_texture = 24U, + pbr_emission_texture = 25U, + pbr_ambient_occlusion_texture = 26U, + //pbr_smudge_texture = 27U, + pbr_displacement_texture = 28U, + pbr_clearcoat_bump_texture = 29U, // emap_texture is OBSOLETE - set m_mapping_channel_id = ON_MappingChannel::emap_mapping emap_texture = 86U // spherical environment mapping. diff --git a/opennurbs_topology.cpp b/opennurbs_topology.cpp index cd32c2a6..9c441fe0 100644 --- a/opennurbs_topology.cpp +++ b/opennurbs_topology.cpp @@ -25,22 +25,125 @@ */ +bool ON_ComponentAttributes::EdgeIsEligible( + unsigned int edge_attributes_filter, + const class ON_SubDEdge* edge +) +{ + return ON_ComponentAttributes::EdgeIsEligible(edge_attributes_filter, (nullptr != edge) ? edge->EdgeAttributes() : 0U); +} + +bool ON_ComponentAttributes::EdgeIsEligible( + unsigned int edge_attributes_filter, + unsigned int edge_attributes +) +{ + if (0U == edge_attributes_filter) + return true; // no restrictions + + if (0U == edge_attributes) + return false; // edge has no properties - it's null or unset + + if (0 != (edge_attributes_filter & ON_ComponentAttributes::Damaged)) + { + if (0 == (edge_attributes & ON_ComponentAttributes::Damaged)) + return false; // edge_flags_filter specifies damaged edges and this edge is not damaged. + } + + unsigned filter; + + // Test subsets of mutually exclusive properties + const unsigned int masks[] = + { + // open/closed test + // open = edge has distict start/end vertices (they might be located at the same location) + // closed = edge has identical start/end vertices. + ON_ComponentAttributes::EdgeAttributes::Open + | ON_ComponentAttributes::EdgeAttributes::Closed, + + // wire/boundary/interior/nonmanifold + // wire = 0 faces, boundary = 1 face, interior = 2 faces, nonmanifold = 3 or more faces + ON_ComponentAttributes::EdgeAttributes::Wire + | ON_ComponentAttributes::EdgeAttributes::Boundary + | ON_ComponentAttributes::EdgeAttributes::Interior + | ON_ComponentAttributes::EdgeAttributes::Nonmanifold, + + // length test, + // ZeroLength locations for start/end are equal and the curve connecting them has zero length. + // NonzeroLength the curve connecting the start/end has nonzero length. + ON_ComponentAttributes::EdgeAttributes::ZeroLength + | ON_ComponentAttributes::EdgeAttributes::NonzeroLength, + }; + + for (size_t i = 0; i < sizeof(masks) / sizeof(masks[0]); ++i) + { + filter = masks[i] & edge_attributes_filter; + // Each masks[i] is a set of mutually exclusive edge properties. + // + // Example: + // masks[0] = ON_ComponentAttributes::EdgeAttributes::Open | ON_ComponentAttributes::EdgeAttributes::Closed. + // if (0 == (masks[0] & edge_flags_filter)), neither is set, so don't check the filter. + // if (masks[0] == (masks[0] & edge_flags_filter)), both are set, so don't check the filter. + // Note that if and edge is damaged or has unset vertex information, + // then 0 = (edge & masks[0]). + if (0 == filter) + continue; // nothing to check for this set of properties. + + if (0 == (filter & edge_attributes)) + return false; // edge does not have desired properties + } + + if (0 != (ON_ComponentAttributes::EdgeAttributes::Interior & edge_attributes)) + { + // tests that apply to interior edges + + // Mutually exclusive attributes for interior edges + const unsigned int interior_edge_masks[] = + { + // smooth/crease test + ON_ComponentAttributes::EdgeAttributes::InteriorSmooth + | ON_ComponentAttributes::EdgeAttributes::InteriorCrease, + + // oriented/not oriented + ON_ComponentAttributes::EdgeAttributes::InteriorOriented + | ON_ComponentAttributes::EdgeAttributes::InteriorNotOriented, + + // seam/slit + ON_ComponentAttributes::EdgeAttributes::InteriorTwoFaced + | ON_ComponentAttributes::EdgeAttributes::InteriorSeam + | ON_ComponentAttributes::EdgeAttributes::InteriorSlit, + }; + + for (size_t i = 0; i < sizeof(interior_edge_masks) / sizeof(interior_edge_masks[0]); ++i) + { + filter = interior_edge_masks[i] & edge_attributes_filter; + if (0 == filter) + continue; // nothing to check + + if (0 == (filter & edge_attributes)) + return false; // edge does not have desired properties + } + } + + return true; // edge passed filter tests +} + bool ON_ComponentAttributes::IsSolid( unsigned int aggregate_edge_component_attributes ) { const unsigned int required_bits - = ON_ComponentAttributes::EdgeFlags::Oriented + = ON_ComponentAttributes::EdgeAttributes::InteriorOriented ; if ( required_bits != (required_bits & aggregate_edge_component_attributes) ) return false; const unsigned int forbidden_bits - = ON_ComponentAttributes::EdgeFlags::Wire - | ON_ComponentAttributes::EdgeFlags::Boundary - | ON_ComponentAttributes::EdgeFlags::Nonmanifold - | ON_ComponentAttributes::EdgeFlags::NotOriented - | ON_ComponentAttributes::EdgeFlags::Damaged + = ON_ComponentAttributes::EdgeAttributes::Wire + | ON_ComponentAttributes::EdgeAttributes::Boundary + | ON_ComponentAttributes::EdgeAttributes::Nonmanifold + | ON_ComponentAttributes::EdgeAttributes::InteriorNotOriented + | ON_ComponentAttributes::Damaged ; if ( 0 != (forbidden_bits & aggregate_edge_component_attributes) ) return false; @@ -53,7 +156,7 @@ bool ON_ComponentAttributes::HasBoundary( ) { const unsigned int required_bits - = ON_ComponentAttributes::EdgeFlags::Boundary + = ON_ComponentAttributes::EdgeAttributes::Boundary ; if ( required_bits != (required_bits & aggregate_edge_component_attributes) ) return false; @@ -66,13 +169,13 @@ bool ON_ComponentAttributes::IsOriented( ) { const unsigned int required_bits - = ON_ComponentAttributes::EdgeFlags::Oriented + = ON_ComponentAttributes::EdgeAttributes::InteriorOriented ; if ( required_bits != (required_bits & aggregate_edge_component_attributes) ) return false; const unsigned int forbidden_bits - = ON_ComponentAttributes::EdgeFlags::NotOriented + = ON_ComponentAttributes::EdgeAttributes::InteriorNotOriented ; if ( 0 != (forbidden_bits & aggregate_edge_component_attributes) ) return false; @@ -85,7 +188,7 @@ bool ON_ComponentAttributes::IsNotOriented( ) { const unsigned int required_bits - = ON_ComponentAttributes::EdgeFlags::NotOriented + = ON_ComponentAttributes::EdgeAttributes::InteriorNotOriented ; if ( required_bits != (required_bits & aggregate_edge_component_attributes) ) return false; @@ -99,16 +202,15 @@ bool ON_ComponentAttributes::IsManifold( ) { const unsigned int require_at_least_one_bit - = ON_ComponentAttributes::EdgeFlags::Boundary - | ON_ComponentAttributes::EdgeFlags::Interior - | ON_ComponentAttributes::EdgeFlags::Seam + = ON_ComponentAttributes::EdgeAttributes::Boundary + | ON_ComponentAttributes::EdgeAttributes::Interior ; if ( 0 == (require_at_least_one_bit & aggregate_edge_component_attributes) ) return false; const unsigned int forbidden_bits - = ON_ComponentAttributes::EdgeFlags::Wire - | ON_ComponentAttributes::EdgeFlags::Nonmanifold + = ON_ComponentAttributes::EdgeAttributes::Wire + | ON_ComponentAttributes::EdgeAttributes::Nonmanifold ; if ( 0 != (forbidden_bits & aggregate_edge_component_attributes) ) return false; @@ -121,8 +223,8 @@ bool ON_ComponentAttributes::IsNotManifold( ) { const unsigned int require_at_least_one_bit - = ON_ComponentAttributes::EdgeFlags::Wire - | ON_ComponentAttributes::EdgeFlags::Nonmanifold + = ON_ComponentAttributes::EdgeAttributes::Wire + | ON_ComponentAttributes::EdgeAttributes::Nonmanifold ; if ( 0 == (require_at_least_one_bit & aggregate_edge_component_attributes) ) return false; diff --git a/opennurbs_topology.h b/opennurbs_topology.h index 7bacfb34..401a9dc9 100644 --- a/opennurbs_topology.h +++ b/opennurbs_topology.h @@ -22,109 +22,136 @@ class ON_CLASS ON_ComponentAttributes { public: -#pragma region RH_C_SHARED_ENUM [ON_ComponentAttributes::EdgeFlags] [Rhino.Geometry.EdgeAttributeFlags] [int] + enum : unsigned int + { + /// + /// The Damaged bit is set when an vertex, edge, or face component has a critical data structure flaw. + /// + Damaged = 0x80000000U + }; + +#pragma region RH_C_SHARED_ENUM [ON_ComponentAttributes::EdgeAttributes] [Rhino.Geometry.EdgeAttributes] [int] /// /// ON_EdgeAttributeFlags are used to report attributes of single edge objects, like /// ON_SubDEdge and ON_BrepEdge, and aggregate edge demographics in objects with topology /// like ON_SubD, ON_Mesh and ON_Brep. - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// /// - enum EdgeFlags : unsigned int + enum EdgeAttributes : unsigned int { /// /// The Open bit is set when an edge has distinct start and end vertices. + /// Open and Closed are mutually exclusive edge attributes. + /// Note that an open edge can have zero length when two distict vertices + /// have identical locations and the edge's geometry is a zero length curve. /// Open = 1, /// /// The Closed bit is set when an edge begins and ends at the same vertex. + /// Open and Closed are mutually exclusive edge attributes. + /// Note that a closed edge can have zero length or nonzero length. /// Closed = 2, /// - /// The Wire bit when an edge has no faces. + /// The ZeroLength edge has zero length. It can be open or closed. + /// ZeroLength and NonzeroLength are mutually exclusive edge attributes. /// - Wire = 4, + ZeroLength = 4, /// - /// The Boundary bit is set when an edge has one face. + /// The NonzeroLength edge has non zero length. It can be open or closed. + /// ZeroLength and NonzeroLength are mutually exclusive edge attributes. /// - Boundary = 8, + NonzeroLength = 8, /// - /// The Interior bit is set when an edge has two distinct faces. + /// The Wire edge has no faces. + /// Wire, Boundary, Interior, and Nonmanifold are mutually exclusive edge attributes. /// - Interior = 16, + Wire = 16, /// - /// The Nonmanifold bit is set when an edge has three or more faces. + /// The Boundary edge has one face. + /// Wire, Boundary, Interior, and Nonmanifold are mutually exclusive edge attributes. /// - Nonmanifold = 32, + Boundary = 32, /// - /// The Oriented bit is set when an edge has two faces with compatible orientations. + /// The Interior edge has two faces. + /// Wire, Boundary, Interior, and Nonmanifold are mutually exclusive edge attributes. /// - Oriented = 64, + Interior = 64, /// - /// The NotOriented bit is set when an edge has two faces with opposited orientations. + /// The Nonmanifold edge has three or more faces. + /// Wire, Boundary, Interior, and Nonmanifold are mutually exclusive edge attributes. /// - NotOriented = 128, + Nonmanifold = 128, /// - /// The Smooth bit is set when an an edge has two faces with a guaranteed surface tangent continuity. + /// The InteriorOriented edge has two faces with compatible face orientations. + /// InteriorOriented and InteriorNotOriented are mutually exclusive interior edge attributes. /// - Smooth = 256, + InteriorOriented = 256, /// - /// The Crease bit is set when an edge has two faces with a possible surface tangent discontinuity + /// The InteriorNotOriented edge has two faces with opposite face orientations. + /// InteriorOriented and InteriorNotOriented are mutually exclusive interior edge attributes. /// - Crease = 512, + InteriorNotOriented = 512, /// - /// The Dart bit is set when an edge has two faces with a possible surface tangent discontinuity - /// at one end and guaranteed surface tangent space continuity at the other end. + /// The InteriorSmooth edge has two faces with a guaranteed surface tangent continuity. + /// InteriorSmooth and InteriorCrease are mutually exclusive interior edge attributes. /// - Dart = 1024, + InteriorSmooth = 1024, /// - /// The Seam bit is set when an edge has two faces that are identical and the edge is on the parametric boundary of the face's surface. + /// The InteriorCrease edge has two faces with a possible surface tangent discontinuity + /// InteriorSmooth and InteriorCrease are mutually exclusive interior edge attributes. + /// + InteriorCrease = 2048, + + /// + /// The InteriorTwoFaced edge has two distinct faces. + /// InteriorTwoFaced, InteriorSeam, and InteriorSlit are mutually exclusive interior edge attributes. + /// + InteriorTwoFaced = 4096, + + /// + /// The InteriorSeam edge has two faces that are identical and the edge is on the parametric boundary of the face's surface. + /// InteriorTwoFaced, InteriorSeam, and InteriorSlit are mutually exclusive interior edge attributes. /// /// /// Parametric surfaces that are cylinders are an example of a situation where seam edges occur. /// - Seam = 2048, + InteriorSeam = 8192, /// - /// The Slit bit is set when edge has two faces that are identical and the edges is not a seam. + /// The InteriorSlit edge has two faces that are identical and the edges is not a seam. + /// InteriorTwoFaced, InteriorSeam, and InteriorSlit are mutually exclusive interior edge attributes. /// - Slit = 4096, + InteriorSlit = 16384, + /// - /// The Slit bit is set when an edge has zero length. - /// - Degenerate = 4096, - - /// - /// The Damaged bit is set when an edge has a critical flaw like missing vertex information. - /// - Damaged = 32768, - - /// - /// Mask can be used to isolate EdgeFlags bits from an unsigned int bit field containing other information. + /// ON_ComponentAttributes::EdgeAttributes::Mask can be used to isolate EdgeAttributes bits + /// from an unsigned int bit field containing other information. /// /// - /// Determine if two unsigned ints have identical EdgeFlags settings. + /// Determine if two unsigned ints have identical ON_ComponentAttributes::EdgeAttributes settings. /// /// unsigned int combined_flags1 = ...; /// unsigned int combined_flags2 = ...; - /// unsigned int edge_flags1 = (ON_ComponentAttributes::EdgeFlags::Mask & combined_flags1); - /// unsigned int edge_flags2 = (ON_ComponentAttributes::EdgeFlags::Mask & combined_flags2); + /// unsigned int edge_flags1 = (ON_ComponentAttributes::EdgeAttributes::Mask & combined_flags1); + /// unsigned int edge_flags2 = (ON_ComponentAttributes::EdgeAttributes::Mask & combined_flags2); /// if ( edge_flags1 == edge_flags1) /// { /// ... edges flags are identical ... @@ -136,6 +163,21 @@ public: #pragma endregion + + /// + /// Inspects edge attributes to determine if the edge is has the attributes + /// specified by the edge_attributes_filter. + /// + static bool EdgeIsEligible( + unsigned int edge_attributes_filter, + const class ON_SubDEdge* edge + ); + + static bool EdgeIsEligible( + unsigned int edge_attributes_filter, + unsigned int edge_attributes + ); + /// /// Inspects aggregate edge demographics to determine if every edge has exactly two faces and all /// the faces have a compatible orientations. @@ -152,7 +194,7 @@ public: /// /// /// - /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// Value made by bitwise or of ON_ComponentAttributes::EdgeAttributes values for every edge in the object. /// /// True if every edge has exactly two faces. /// @@ -173,7 +215,7 @@ public: /// Inspects aggregate edge demographics to determine if there is a boundary edge. /// /// - /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// Value made by bitwise or of ON_ComponentAttributes::EdgeAttributes values for every edge in the object. /// /// True if there is at least one edge that has exactly one face. /// Otherwise, false is returned. @@ -186,7 +228,7 @@ public: /// Inspects aggregate edge demographics to determine if the faces have a compatible orientations. /// /// - /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// Value made by bitwise or of ON_ComponentAttributes::EdgeAttributes values for every edge in the object. /// /// If for every edge edge with exactly two faces, those two faces have compatible orientations, then true is returned. /// Otherwise, false is returned. @@ -199,7 +241,7 @@ public: /// Inspects aggregate edge demographics to determine if the faces have a compatible orientations. /// /// - /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// Value made by bitwise or of ON_ComponentAttributes::EdgeAttributes values for every edge in the object. /// /// If there is an edge edge with exactly two faces and those faces have incompatible orientations, /// then true is returned. Otherwise, false is returned. @@ -213,7 +255,7 @@ public: /// Face orientation is ignored. /// /// - /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// Value made by bitwise or of ON_ComponentAttributes::EdgeAttributes values for every edge in the object. /// /// If every edge has one or two faces, then true is returned. /// If bAllowBoundaryEdges is true and every edge has one or two faces, then true is returned. @@ -227,7 +269,7 @@ public: /// Inspects aggregate edge demographics to determine if the object is a not manifold. /// /// - /// Value made by bitwise or of ON_ComponentAttributes::EdgeFlags values for every edge in the object. + /// Value made by bitwise or of ON_ComponentAttributes::EdgeAttributes values for every edge in the object. /// /// True if there is at least one edge with 3 or more faces or at least one wire edge. static bool IsNotManifold( diff --git a/opennurbs_unicode.cpp b/opennurbs_unicode.cpp index 7ca17681..1f02b748 100644 --- a/opennurbs_unicode.cpp +++ b/opennurbs_unicode.cpp @@ -8,7 +8,7 @@ // 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 . // //////////////////////////////////////////////////////////////// @@ -19,7 +19,7 @@ #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 +// 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 @@ -125,7 +125,7 @@ enum ON_UnicodeEncoding ON_IsUTFByteOrderMark( const void* buffer, size_t sizeof_buffer ) -{ +{ if ( 0 != buffer && sizeof_buffer >= 2 ) { const unsigned char* b = static_cast(buffer); @@ -345,7 +345,7 @@ static int ON_DecodeUTF8Helper( char c; c = sUTF8[0]; - + if ( 0 == (0x80 & c) ) { // 1 byte ASCII encoding: 0xxxxxxx @@ -452,7 +452,7 @@ static int ON_DecodeUTF8Helper( *value = u; return 4; } - + if ( 0xF8 == ( 0xFC & c) ) { // 5 byte character encoding: 111110xx, 10xxxxxx, 10xxxxxx, 10xxxxxx, 10xxxxxx @@ -601,7 +601,7 @@ int ON_DecodeUTF8( *unicode_code_point = sUTF8[0]; return 1; } - + c = sUTF8[0]; if ( 0xC0 == ( 0xE0 & c) && sUTF8_count >= 2 ) { @@ -705,7 +705,7 @@ int ON_DecodeUTF8( // skip to next UTF-8 start elemement for ( /*empty for initializer*/; i0 < sUTF8_count; i0++ ) { - // Search for the next element of sUTF8[] that is the + // Search for the next element of sUTF8[] that is the // start of a UTF-8 encoding sequence. c = sUTF8[i0]; if ( 0 == (0x80 & c) // ASCII 0 - 127 @@ -731,14 +731,14 @@ int ON_DecodeUTF8( *unicode_code_point = u0; return i0; } - - if ( i0 < sUTF8_count - && u0 >= 0xD800 && u0 <= 0xDBFF + + if ( i0 < sUTF8_count + && u0 >= 0xD800 && u0 <= 0xDBFF && (0 == error_status || 8 == error_status) - && 0 != (4 & e->m_error_mask) + && 0 != (4 & e->m_error_mask) ) { - // See if a UFT-16 surrogate pair was incorrectly encoded + // See if a UFT-16 surrogate pair was incorrectly encoded // as two consecutive UTF-8 sequences. u1 = 0xFFFFFFFF; i1 = ON_DecodeUTF8Helper(sUTF8+i0,sUTF8_count-i0,&u1,&error_status); @@ -748,8 +748,8 @@ int ON_DecodeUTF8( sUTF16[0] = (ON__UINT16)u0; sUTF16[1] = (ON__UINT16)u1; u0 = 0xFFFFFFFF; - if ( 2 == ON_ConvertUTF16ToUTF32(false,sUTF16,2,&u0,1,&error_status,0,0,0) - && 0 == error_status + if ( 2 == ON_ConvertUTF16ToUTF32(false,sUTF16,2,&u0,1,&error_status,0,0,0) + && 0 == error_status && ON_IsValidUnicodeCodePoint(u0) ) { @@ -860,7 +860,7 @@ int ON_DecodeUTF16( return 0; } - // Search for the next element of sUTF16[] that is a + // Search for the next element of sUTF16[] that is a // valid UTF-16 encoding sequence. int i; for ( i = 1; i < sUTF16_count; i++ ) @@ -870,8 +870,8 @@ int ON_DecodeUTF16( // valid single UTF-16 code unit break; } - if ( i+1 < sUTF16_count - && sUTF16[i] >= 0xD800 && sUTF16[i] < 0xDC00 + if ( i+1 < sUTF16_count + && sUTF16[i] >= 0xD800 && sUTF16[i] < 0xDC00 && sUTF16[i+1] >= 0xDC00 && sUTF16[i+1] < 0xE000 ) { @@ -1136,7 +1136,7 @@ int ON_DecodeSwapByteUTF16( return 0; } - // Search for the next element of sUTF16[] that is a + // Search for the next element of sUTF16[] that is a // valid UTF-16 encoding sequence. p1 = (ON__UINT8*)&w1; p += sizeof(sUTF16[0]); @@ -1256,7 +1256,7 @@ int ON_ConvertUTF8ToUTF8( *sNextInputUTF8 = sInputUTF8+i; if ( error_status ) *error_status = e.m_error_status; - + return output_count; } @@ -1351,7 +1351,7 @@ int ON_ConvertUTF8ToUTF16( *sNextUTF8 = sUTF8+i; if ( error_status ) *error_status = e.m_error_status; - + return output_count; } @@ -1441,7 +1441,7 @@ int ON_ConvertUTF8ToUTF32( *sNextUTF8 = sUTF8+i; if ( error_status ) *error_status = e.m_error_status; - + return output_count; } @@ -1566,7 +1566,7 @@ int ON_ConvertUTF16ToUTF8( *sNextUTF16 = sUTF16+i; if ( error_status ) *error_status = e.m_error_status; - + return output_count; } @@ -1691,7 +1691,7 @@ int ON_ConvertUTF16ToUTF16( *sNextInputUTF16 = sInputUTF16+i; if ( error_status ) *error_status = e.m_error_status; - + return output_count; } @@ -1814,7 +1814,7 @@ int ON_ConvertUTF16ToUTF32( *sNextUTF16 = sUTF16+i; if ( error_status ) *error_status = e.m_error_status; - + return output_count; } @@ -2020,7 +2020,7 @@ int ON_ConvertUTF32ToUTF8( *sNextUTF32 = sUTF32+i; if ( error_status ) *error_status = e.m_error_status; - + return output_count; } @@ -2132,7 +2132,7 @@ int ON_ConvertUTF32ToUTF16( *sNextUTF32 = sUTF32+i; if ( error_status ) *error_status = e.m_error_status; - + return output_count; } @@ -2256,7 +2256,7 @@ int ON_ConvertUTF32ToUTF32( *sNextUTF16 = sUTF16+i; if ( error_status ) *error_status = e.m_error_status; - + return output_count; } @@ -2619,7 +2619,7 @@ const ON_wString ON_wString::FromUnicodeCodePoints( ) { const bool bErrorCodePointIsValid = ON_IsValidUnicodeCodePoint(error_code_point); - + if (nullptr == code_points) return ON_wString::EmptyString; @@ -2675,7 +2675,7 @@ const ON_wString ON_wString::FromUnicodeCodePoints( error_code_point, nullptr ); - + if (wchar_count <= 0) return ON_wString::EmptyString; @@ -2707,22 +2707,22 @@ ON__UINT32 ON_MapRTFcharsetToWindowsCodePage( { // From the Microsoft version of the RTF ver 1.9 spec available on MSDN // - // \fcharsetN: Specifies the character set of a font in the font table.If this appears, it implies that bytes in runs + // \fcharsetN: Specifies the character set of a font in the font table.If this appears, it implies that bytes in runs // tagged with the associated \fN are character codes in the codepage corresponding to the charset N. // Use this codepage to convert the codes to Unicode using a function like the Windows MultiByteToWideChar(). - // See also the \cpgN control word, which, if it appears, supersedes the codepage given by \fcharsetN.Values for N are defined, - // for example, in the Windows header file wingdi.h(e.g., see ANSI_CHARSET) and are repeated here together with the corresponding + // See also the \cpgN control word, which, if it appears, supersedes the codepage given by \fcharsetN.Values for N are defined, + // for example, in the Windows header file wingdi.h(e.g., see ANSI_CHARSET) and are repeated here together with the corresponding // Windows or Mac codepages for convenience:charset codepage Windows / Mac name - // A font may have a different character set from the character set of the document. For example, the Symbol font has the - // same characters in the same code positions both on the Macintosh and in Windows. Typically, RTF fonts use the code page - // corresponding to the \fcharsetN control word in their \fonttbl description. If the charset doesnt exist, the codepage - // may be given by the \cpgN control word, for which the code page is N. If the \cpgN does appear, it supersedes the code - // page corresponding to the \fcharsetN. - // For such cases, codepage conversions can be avoided altogether by using the Unicode \uN notation for characters. - // In addition, file names (used in field instructions and in embedded fonts) may not necessarily be the same as the character - // set of the document; the \cpgN control word can change the character set for these file names as well. - // + // A font may have a different character set from the character set of the document. For example, the Symbol font has the + // same characters in the same code positions both on the Macintosh and in Windows. Typically, RTF fonts use the code page + // corresponding to the \fcharsetN control word in their \fonttbl description. If the charset doesn’t exist, the codepage + // may be given by the \cpgN control word, for which the code page is N. If the \cpgN does appear, it supersedes the code + // page corresponding to the \fcharsetN. + // For such cases, codepage conversions can be avoided altogether by using the Unicode \uN notation for characters. + // In addition, file names (used in field instructions and in embedded fonts) may not necessarily be the same as the character + // set of the document; the \cpgN control word can change the character set for these file names as well. + // ON__UINT32 cp; switch (rtf_charset) @@ -2752,7 +2752,7 @@ ON__UINT32 ON_MapRTFcharsetToWindowsCodePage( case 162: cp = 1254; break; // Turkish case 163: cp = 1258; break; // Vietnamese case 177: cp = 1255; break; // Hebrew - case 178: cp = 1256; break; // Arabic + case 178: cp = 1256; break; // Arabic case 179: cp = default_code_page; break; // Arabic Traditional (old) case 180: cp = default_code_page; break; // Arabic user (old) case 181: cp = default_code_page; break; // Hebrew user (old) @@ -2805,7 +2805,7 @@ static int ON_Internal_ConvertMSSBCPToWideChar( ON__UINT32 unicode_code_point; if (c < 0x80) unicode_code_point = c; - else + else { if (c <= 0xFF && nullptr != sb_code_page_0x80_to_0xFF_to_unicode ) { @@ -2837,7 +2837,7 @@ static int ON_Internal_ConvertMSSBCPToWideChar( while (s < s1) *s++ = *w++; } - + while (s < sWideCharMax) { *s++ = 0; @@ -2938,7 +2938,7 @@ int ON_ConvertMSMBCPToWideChar( #if defined(ON_RUNTIME_WIN) // Starting with Windows Vista, the function does not drop illegal code points when dwFlags=0. // It replaces illegal sequences with U+FFFD (encoded as appropriate for the specified codepage). - DWORD dwFlags = 0; + DWORD dwFlags = 0; int sWideChar_count = ::MultiByteToWideChar(windows_code_page, dwFlags, sMBCS, sMBCS_count, sWideChar, sWideChar_capacity); if (sWideChar_count < 0) sWideChar_count = 0; diff --git a/opennurbs_unicode.h b/opennurbs_unicode.h index 06f1efa7..81506b7c 100644 --- a/opennurbs_unicode.h +++ b/opennurbs_unicode.h @@ -55,6 +55,8 @@ enum ON_UnicodeCodePoint ON_Underscore = 0x5F, // Unicode LOW LINE U+005F ON_Pipe = 0x7C, // VERTICAL LINE U+007C (decimal 124) ON_Tilde = 0x7E, // TILDE U+007E (decimal 126) + ON_Period = 0x2E, // PERIOD U+002E (decimal 46) + ON_Comma = 0x2C, // COMMA U+002C (decimal 44) // // NOTE: UTF-8 encodings of the codepoint values below this comment require multiple bytes. diff --git a/opennurbs_uuid.h b/opennurbs_uuid.h index 8c7b7407..1784647c 100644 --- a/opennurbs_uuid.h +++ b/opennurbs_uuid.h @@ -63,6 +63,7 @@ extern ON_EXTERN_DECL const ON_UUID ON_rhino3_id; extern ON_EXTERN_DECL const ON_UUID ON_rhino4_id; extern ON_EXTERN_DECL const ON_UUID ON_rhino5_id; extern ON_EXTERN_DECL const ON_UUID ON_rhino6_id; +extern ON_EXTERN_DECL const ON_UUID ON_rhino7_id; extern ON_EXTERN_DECL const ON_UUID ON_rhino_id; // Application ids for usedata written by versions @@ -85,6 +86,7 @@ extern ON_EXTERN_DECL const ON_UUID ON_v4_userdata_id; extern const ON_UUID ON_opennurbs4_id; extern const ON_UUID ON_opennurbs5_id; extern const ON_UUID ON_opennurbs6_id; +extern const ON_UUID ON_opennurbs7_id; #endif extern ON_EXTERN_DECL const ON_UUID ON_opennurbs_id; @@ -98,6 +100,7 @@ Returns: 0: id is not a Rhino application id N: id == ON_rhinoN_id, where "N" = 2,3,4,5,6,... */ +ON_DECL unsigned int ON_IsRhinoApplicationId( ON_UUID id ); @@ -112,6 +115,7 @@ Returns: 0: id is not an opennurbs application id N: id == ON_opennurbsN_id, where "N" = 4,5,6,... */ +ON_DECL unsigned int ON_IsOpennurbsApplicationId( ON_UUID id ); diff --git a/opennurbs_version.cpp b/opennurbs_version.cpp index f89e3815..e95b9609 100644 --- a/opennurbs_version.cpp +++ b/opennurbs_version.cpp @@ -160,6 +160,14 @@ const wchar_t* ON::VersionQuartetAsWideString() return OPENNURBS_VERSION_QUARTET_WSTRING; } +int ON_BinaryArchive::CurrentArchiveVersion() +{ + // Latest version of opennurbs binary archives supported by + // this version of opennurbs. + return OPENNURBS_CURRENT_ARCHIVE_VERSION; +} + + bool ON_TestVersionNumber( unsigned int major, unsigned int minor, diff --git a/opennurbs_version.h b/opennurbs_version.h index 0802b98e..36125ad8 100644 --- a/opennurbs_version.h +++ b/opennurbs_version.h @@ -1,4 +1,4 @@ -/* $NoKeywords: $ */ +/* $NoKeywords: $ */ /* // // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. @@ -8,7 +8,7 @@ // 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 . // //////////////////////////////////////////////////////////////// @@ -97,16 +97,16 @@ // // The build process modifies version.h and sets // RMA_SRC_SVN_REVISION = "" -// before compiling applications. -// +// before compiling applications. +// -#define OPENNURBS_GIT_REVISION_HASH RMA_GIT_REVISION_HASH_STRING +#define OPENNURBS_GIT_REVISION_HASH RMA_GIT_REVISION_HASH_STRING #define OPENNURBS_GIT_BRANCH_NAME RMA_GIT_BRANCH_NAME_STRING //////////////////////////////////////////////////////////////// // -// OPENNURBS_VERSION_QUARTET_STRING is a macro whose value is the -// opennurbs version quartet as a string. +// OPENNURBS_VERSION_QUARTET_STRING is a macro whose value is the +// opennurbs version quartet as a string. // #define OPENNURBS_VERSION_QUARTET_STRING RMA_VERSION_WITH_PERIODS_STRING #define OPENNURBS_VERSION_QUARTET_WSTRING RMA_VERSION_WITH_PERIODS_WSTRING @@ -115,11 +115,11 @@ //////////////////////////////////////////////////////////////// // -// ON_VERSION_NUMBER_FEBDAYS(year) is a macro whose value is -// the number of days in the month of February in a specified -// year. +// ON_VERSION_NUMBER_FEBDAYS(year) is a macro whose value is +// the number of days in the month of February in a specified +// year. // -// In almost every situation, it is best to used the function +// In almost every situation, it is best to used the function // call ON_DaysInMonthOfGregorianYear(year,2) to get this value. // The ON_VERSION_NUMBER_FEBDAYS macro is for rare and unusual // situations where the C preprocessor needs this value. @@ -132,8 +132,8 @@ //////////////////////////////////////////////////////////////// // -// ON_VERSION_NUMBER_DAYOFYEAR(year, month, day_of_month) is a macro -// whose value is the cardinal day of the year for the +// ON_VERSION_NUMBER_DAYOFYEAR(year, month, day_of_month) is a macro +// whose value is the cardinal day of the year for the // specified year, month and day_of_month. // // In almost every situation, it is best to used the function call @@ -176,7 +176,7 @@ // 1: Windows build // 2: Mac build // -// NOTE WELL: +// NOTE WELL: // ON_VERSION_NUMBER_PLATFORM_ID(branch) must be a value between 0 and 3. #define ON_VERSION_NUMBER_PLATFORM_ID(branch) \ (((branch) > 0x0U) ? (0x02U - ((branch) % 0x02U)) : 0x0U) @@ -190,8 +190,8 @@ // // In almost every situation, it is best to used the function call // ON_VersionNumberConstruct(major,minor,year,month,day_of_month,branch) -// to get this value. The ON_VERSION_NUMBER_CTOR macro is for -// rare and unusual situations where the C preprocessor needs +// to get this value. The ON_VERSION_NUMBER_CTOR macro is for +// rare and unusual situations where the C preprocessor needs // this value. // #define ON_VERSION_NUMBER_CTOR(major,minor,year,month,day_of_month,branch) \ @@ -202,10 +202,10 @@ //////////////////////////////////////////////////////////////// // -// OPENNURBS_VERSION_NUMBER is a macro whose value is the -// opennurbs version number. +// OPENNURBS_VERSION_NUMBER is a macro whose value is the +// opennurbs version number. // -// Always use ON::Version() when you need this value. +// Always use ON::Version() when you need this value. // The OPENNURBS_VERSION_NUMBER macro is for rare and unusual // situations where the C preprocessor needs this value. // @@ -214,4 +214,14 @@ OPENNURBS_VERSION_YEAR, OPENNURBS_VERSION_MONTH, OPENNURBS_VERSION_DAY_OF_MONTH, \ OPENNURBS_VERSION_BRANCH ) +// Jan 2017 +// November 2016 Rhino 7 WIP writes version 6 files. +// October 23 1019, Rhino 7 WIP writes version 7 files. +// +// More generally, in a stable release product, OPENNURBS_CURRENT_ARCHIVE_VERSION = OPENNURBS_VERSION_MAJOR*10. +// But for some period of time at the beginning of the Rhino (N+1) WIP developement cycle, +// Rhino (N+1) WIP writes Rhino N files. That's why OPENNURBS_CURRENT_ARCHIVE_VERSION +// is sometimes (OPENNURBS_VERSION_MAJOR*10) and is sometimes ((OPENNURBS_VERSION_MAJOR-1)*10) +#define OPENNURBS_CURRENT_ARCHIVE_VERSION 70 + #endif diff --git a/opennurbs_viewport.cpp b/opennurbs_viewport.cpp index 866a7f0b..52f1368f 100644 --- a/opennurbs_viewport.cpp +++ b/opennurbs_viewport.cpp @@ -4943,4 +4943,194 @@ int ON_Viewport::InViewFrustum( return cr.InViewFrustum(count,p); } +class ON_CLASS ON_ViewportUserData : public ON_UserData +{ + ON_OBJECT_DECLARE(ON_ViewportUserData); +public: + ON_ViewportUserData(); + ~ON_ViewportUserData() = default; + ON_ViewportUserData(const ON_ViewportUserData& src); + ON_ViewportUserData& operator=(const ON_ViewportUserData& src); + + static ON_ViewportUserData* FromObject(const ON_Object* obj); + static ON_ViewportUserData* FromObject(ON_Object* obj, bool createAndAttachIfMissing); + + // override virtual ON_UserData::GetDescription function + bool GetDescription(ON_wString& description) override; + + // override virtual ON_Object::SizeOf function + unsigned int SizeOf() const override; + + // override virtual ON_Object::DataCRC function + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + + // override virtual ON_Object::Dump function + void Dump(ON_TextLog& text_log) const override; + + // override virtual ON_UserData::Archive function + bool WriteToArchive( + const class ON_BinaryArchive& archive, + const class ON_Object* parent_object + ) const override; + + // override virtual ON_Object::Write function + bool Write(ON_BinaryArchive& binary_archive) const override; + + // override virtual ON_Object::Read function + bool Read(ON_BinaryArchive& binary_archive) override; + +public: + // The description, or contents, of the viewport. + ON_wString m_description; +}; + +ON_OBJECT_IMPLEMENT(ON_ViewportUserData, ON_UserData, "DA2B97F8-5B76-4C50-8504-9A7CFC8AD62C"); + +ON_ViewportUserData::ON_ViewportUserData() +{ + m_userdata_uuid = ON_CLASS_ID(ON_ViewportUserData); + m_application_uuid = ON_opennurbs6_id; + m_userdata_copycount = 1; +} + +ON_ViewportUserData::ON_ViewportUserData(const ON_ViewportUserData& src) + : ON_UserData(src) + , m_description(src.m_description) +{ + m_userdata_uuid = ON_CLASS_ID(ON_ViewportUserData); + m_application_uuid = ON_opennurbs6_id; + m_userdata_copycount = 1; +} + +ON_ViewportUserData& ON_ViewportUserData::operator=(const ON_ViewportUserData& src) +{ + if (this != &src) + { + ON_UserData::operator=(src); + m_description = src.m_description; + } + return *this; +} + +ON_ViewportUserData* ON_ViewportUserData::FromObject(const ON_Object* obj) +{ + if (nullptr == obj) + return nullptr; + return ON_ViewportUserData::Cast(obj->GetUserData(ON_CLASS_ID(ON_ViewportUserData))); +} + +ON_ViewportUserData* ON_ViewportUserData::FromObject(ON_Object* obj, bool createAndAttachIfMissing) +{ + if (nullptr == obj) + return nullptr; + ON_ViewportUserData* rc = ON_ViewportUserData::Cast(obj->GetUserData(ON_CLASS_ID(ON_ViewportUserData))); + if (nullptr == rc && createAndAttachIfMissing) + { + rc = new ON_ViewportUserData(); + if (!obj->AttachUserData(rc)) + { + delete rc; + return nullptr; + } + } + return rc; +} + +bool ON_ViewportUserData::GetDescription(ON_wString& description) +{ + description = L"Viewport UserData"; + return true; +} + +unsigned int ON_ViewportUserData::SizeOf() const +{ + unsigned int sz = ON_UserData::SizeOf(); + sz += sizeof(*this) - sizeof(ON_UserData); + sz += m_description.SizeOf(); + return sz; +} + +ON__UINT32 ON_ViewportUserData::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = m_description.DataCRC(current_remainder); + return current_remainder; +} + +void ON_ViewportUserData::Dump(ON_TextLog& text_log) const +{ + const wchar_t* pszDescription = static_cast(m_description); + if (nullptr == pszDescription) + pszDescription = L""; + text_log.Print("Description = \"%ls\"\n", pszDescription); +} + +bool ON_ViewportUserData::WriteToArchive(const ON_BinaryArchive & archive, const ON_Object * parent_object) const +{ + return (archive.Archive3dmVersion() >= 60); +} + +bool ON_ViewportUserData::Write(ON_BinaryArchive& archive) const +{ + bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0); + if (!rc) + return false; + + for (;;) + { + // 1.0 fields + rc = archive.WriteString(m_description); + if (!rc) break; + + break; + } + + if (!archive.EndWrite3dmChunk()) + rc = false; + + return rc; +} + +bool ON_ViewportUserData::Read(ON_BinaryArchive& archive) +{ + int major_version = 0; + int minor_version = 0; + bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version); + if (!rc) + return false; + + for (;;) + { + rc = (1 == major_version); + if (!rc) break; + + // 1.0 fields + rc = archive.ReadString(m_description); + if (!rc) break; + + break; + } + + if (!archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +ON_wString ON_Viewport::Description() const +{ + ON_wString rc; + ON_ViewportUserData* data = ON_ViewportUserData::FromObject(this); + if (data) + rc = data->m_description; + return rc; +} + +bool ON_Viewport::SetDescription(const wchar_t* description) +{ + ON_ViewportUserData* data = ON_ViewportUserData::FromObject(this, true); + if (nullptr == data) + return false; + data->m_description = description; + return true; +} diff --git a/opennurbs_viewport.h b/opennurbs_viewport.h index 60801584..c278049b 100644 --- a/opennurbs_viewport.h +++ b/opennurbs_viewport.h @@ -1616,6 +1616,13 @@ public: const ON_Xform* bbox_xform ) const; + /* + Description: + Gets or sets the description, or contents, of the viewport. + */ + ON_wString Description() const; + bool SetDescription(const wchar_t* description); + protected: // These boolean status flags are set to true when diff --git a/opennurbs_wip.h b/opennurbs_wip.h index 8a07e022..b6f22fcf 100644 --- a/opennurbs_wip.h +++ b/opennurbs_wip.h @@ -17,29 +17,17 @@ #if !defined OPENNURBS_WIP_INC__ #define OPENNURBS_WIP_INC__ -#if !defined(OPENNURBS_SUBD_WIP) - -#if defined(ON_COMPILING_OPENNURBS) || defined(TL_INC_) || defined(RHINO_WIP_BUILD) || defined(RHINO_BETA_BUILD) || defined(RHINO_SUBD_WIP) -// SubD classes and .3dm support for reading it. -// SubD is alwasy available internally (ON_COMPILING_OPENNURBS is defined) -// so .3dm files can be read and written by all versions of opennurbs. +// These SubD WIP defines can be deleted once we are 100% certain +// they are not used in any code. They existed when SubD was a WIP +// project that had to be conditionally added/removed from Rhino. #define OPENNURBS_SUBD_WIP - -#elif !defined(RHINO_COMMERCIAL_BUILD) -// SubD is available in the Rhino WIP C++ SDK. -// SubD is not avaialable in the Rhino 6 commercial C++ SDK. -#define OPENNURBS_SUBD_WIP - -#elif defined(RHINO_CORE_COMPONENT) -// SubD is available to core Rhino 6 and core Rhino WIP code -#define OPENNURBS_SUBD_WIP - -#endif - -#endif +#define RHINO_SUBD_WIP +#define RHINO_SUBD_GRIPS_WIP // Annotation table is being prototyped and on hold // until V6 ships. //#define OPENNURBS_ANNOTATION_TABLE_WIP +#define OPENNURBS_GRADIENT_WIP + #endif diff --git a/opennurbs_xform.cpp b/opennurbs_xform.cpp index b7fefb32..4eef7c80 100644 --- a/opennurbs_xform.cpp +++ b/opennurbs_xform.cpp @@ -867,6 +867,9 @@ const ON_Xform operator*(const ON_Xform& xform, double c) ON_2dPoint ON_Xform::operator*( const ON_2dPoint& p ) const { + // Note well: The right hand column and bottom row have an important effect + // when transforming a Euclidean point and have no effect when transforming a vector. + // Be sure you understand the differences between vectors and points when applying a 4x4 transformation. const double x = p.x; // optimizer should put x,y in registers const double y = p.y; double xh[2], w; @@ -880,6 +883,9 @@ ON_2dPoint ON_Xform::operator*( const ON_2dPoint& p ) const ON_3dPoint ON_Xform::operator*( const ON_3dPoint& p ) const { + // Note well: The right hand column and bottom row have an important effect + // when transforming a Euclidean point and have no effect when transforming a vector. + // Be sure you understand the differences between vectors and points when applying a 4x4 transformation. const double x = p.x; // optimizer should put x,y,z in registers const double y = p.y; const double z = p.z; @@ -910,6 +916,9 @@ ON_4dPoint ON_Xform::operator*( const ON_4dPoint& h ) const ON_2dVector ON_Xform::operator*( const ON_2dVector& v ) const { + // Note well: The right hand column and bottom row have an important effect + // when transforming a Euclidean point and have no effect when transforming a vector. + // Be sure you understand the differences between vectors and points when applying a 4x4 transformation. const double x = v.x; // optimizer should put x,y in registers const double y = v.y; double xh[2]; @@ -921,6 +930,9 @@ ON_2dVector ON_Xform::operator*( const ON_2dVector& v ) const ON_3dVector ON_Xform::operator*( const ON_3dVector& v ) const { + // Note well: The right hand column and bottom row have an important effect + // when transforming a Euclidean point and have no effect when transforming a vector. + // Be sure you understand the differences between vectors and points when applying a 4x4 transformation. const double x = v.x; // optimizer should put x,y,z in registers const double y = v.y; const double z = v.z; diff --git a/opennurbs_xform.h b/opennurbs_xform.h index 19638995..9a2b5254 100644 --- a/opennurbs_xform.h +++ b/opennurbs_xform.h @@ -122,10 +122,17 @@ public: // All non-commutative operations have "this" as left hand side and // argument as right hand side. + + // Note well: The right hand column and bottom row have an important effect + // when transforming a Euclidean point and have no effect when transforming a vector. + // Be sure you understand the differences between vectors and points when applying a 4x4 transformation. ON_2dPoint operator*( const ON_2dPoint& ) const; ON_3dPoint operator*( const ON_3dPoint& ) const; ON_4dPoint operator*( const ON_4dPoint& ) const; + // Note well: The right hand column and bottom row have an important effect + // when transforming a Euclidean point and have no effect when transforming a vector. + // Be sure you understand the differences between vectors and points when applying a 4x4 transformation. ON_2dVector operator*( const ON_2dVector& ) const; ON_3dVector operator*( const ON_3dVector& ) const; @@ -1494,7 +1501,7 @@ public: const double* world_points ); - /* + /* Description: Append the clipping point and clipping flag value. */