diff --git a/example_brep/example_brep.vcxproj b/example_brep/example_brep.vcxproj index c9f91ece..a2b825ce 100644 --- a/example_brep/example_brep.vcxproj +++ b/example_brep/example_brep.vcxproj @@ -22,32 +22,32 @@ {765B902B-4562-4035-8BBF-EBAB2A9602A3} Win32Proj example_brep - 8.1 + 10.0 Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode diff --git a/example_convert/example_convert.vcxproj b/example_convert/example_convert.vcxproj index bf84115e..c70575ba 100644 --- a/example_convert/example_convert.vcxproj +++ b/example_convert/example_convert.vcxproj @@ -22,32 +22,32 @@ {15C98F21-2AC9-44C8-8752-25DAFA1C739F} Win32Proj example_convert - 8.1 + 10.0 Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode diff --git a/example_convert/example_convert.vcxproj.filters b/example_convert/example_convert.vcxproj.filters index 7b21b6b2..1c46c9b6 100644 --- a/example_convert/example_convert.vcxproj.filters +++ b/example_convert/example_convert.vcxproj.filters @@ -10,7 +10,7 @@ - + Source Files diff --git a/example_read/example_read.vcxproj b/example_read/example_read.vcxproj index 639f6cca..7cdbd02d 100644 --- a/example_read/example_read.vcxproj +++ b/example_read/example_read.vcxproj @@ -22,32 +22,32 @@ {14A32F02-5A5D-49F7-9156-7EA3608C5900} Win32Proj example_read - 8.1 + 10.0 Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode diff --git a/example_roundtrip/example_roundtrip.vcxproj b/example_roundtrip/example_roundtrip.vcxproj index 5e70411e..37a00d01 100644 --- a/example_roundtrip/example_roundtrip.vcxproj +++ b/example_roundtrip/example_roundtrip.vcxproj @@ -22,32 +22,32 @@ {0AFC8D30-5E7B-429D-82D2-F26868BF3CA6} Win32Proj example_roundtrip - 8.1 + 10.0 Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode diff --git a/example_test/example_test.vcxproj b/example_test/example_test.vcxproj index 902b2161..7ca6b01c 100644 --- a/example_test/example_test.vcxproj +++ b/example_test/example_test.vcxproj @@ -23,31 +23,32 @@ {865E8D8D-8E03-4601-A7B5-A8C4094ECB8B} Win32Proj exampletest + 10.0 Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode diff --git a/example_userdata/example_userdata.vcxproj b/example_userdata/example_userdata.vcxproj index 0f359217..645f3099 100644 --- a/example_userdata/example_userdata.vcxproj +++ b/example_userdata/example_userdata.vcxproj @@ -22,32 +22,32 @@ {F6FC693F-2EDB-4DEC-936A-C15BE1195EC4} Win32Proj example_userdata - 8.1 + 10.0 Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode diff --git a/example_write/example_write.vcxproj b/example_write/example_write.vcxproj index b70b1642..95ebee2f 100644 --- a/example_write/example_write.vcxproj +++ b/example_write/example_write.vcxproj @@ -22,32 +22,32 @@ {75A90363-D54A-4C56-B4FC-900E7540331C} Win32Proj example_write - 8.1 + 10.0 Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode diff --git a/freetype263/freetype263.vcxproj b/freetype263/freetype263.vcxproj index 16211359..1c32a087 100644 --- a/freetype263/freetype263.vcxproj +++ b/freetype263/freetype263.vcxproj @@ -22,31 +22,32 @@ {426B0F99-1100-4CD6-9CB3-78C460817951} Win32Proj freetype263 + 10.0 DynamicLibrary true - v141 + v142 Unicode DynamicLibrary false - v141 + v142 true Unicode DynamicLibrary true - v141 + v142 Unicode DynamicLibrary false - v141 + v142 true Unicode diff --git a/freetype263/freetype263_staticlib.vcxproj b/freetype263/freetype263_staticlib.vcxproj index 8fb46aaf..c2a30b51 100644 --- a/freetype263/freetype263_staticlib.vcxproj +++ b/freetype263/freetype263_staticlib.vcxproj @@ -22,31 +22,32 @@ {F28EFCCD-948B-425C-B9FC-112D84A6498D} Win32Proj freetype263_staticlib + 10.0 StaticLibrary true - v141 + v142 Unicode StaticLibrary false - v141 + v142 true Unicode StaticLibrary true - v141 + v142 Unicode StaticLibrary false - v141 + v142 true Unicode diff --git a/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj b/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj index 7d7c312e..4d7ca33f 100644 --- a/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj +++ b/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj @@ -238,7 +238,7 @@ 1DB028251ED6433600FA9144 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1120; ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; TargetAttributes = { 1DB0282C1ED6433600FA9144 = { @@ -252,8 +252,8 @@ developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, + Base, ); mainGroup = 1DB028241ED6433600FA9144; productRefGroup = 1DB0282E1ED6433600FA9144 /* Products */; @@ -322,6 +322,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; @@ -378,6 +379,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; diff --git a/makefile b/makefile index 3a48e468..3a4c457d 100644 --- a/makefile +++ b/makefile @@ -195,6 +195,7 @@ ON_INC = opennurbs.h \ opennurbs_sumsurface.h \ opennurbs_surface.h \ opennurbs_surfaceproxy.h \ + opennurbs_symmetry.h \ opennurbs_system.h \ opennurbs_system_compiler.h \ opennurbs_system_runtime.h \ @@ -373,6 +374,7 @@ ON_SRC = opennurbs_3dm_attributes.cpp \ opennurbs_sumsurface.cpp \ opennurbs_surface.cpp \ opennurbs_surfaceproxy.cpp \ + opennurbs_symmetry.cpp \ opennurbs_terminator.cpp \ opennurbs_text.cpp \ opennurbs_text_style.cpp \ @@ -549,6 +551,7 @@ ON_OBJ = opennurbs_3dm_attributes.o \ opennurbs_sumsurface.o \ opennurbs_surface.o \ opennurbs_surfaceproxy.o \ + opennurbs_symmetry.o \ opennurbs_terminator.o \ opennurbs_text.o \ opennurbs_text_style.o \ diff --git a/opennurbs.h b/opennurbs.h index e91c76f3..4c10917a 100644 --- a/opennurbs.h +++ b/opennurbs.h @@ -82,6 +82,7 @@ #include "opennurbs_line.h" // simple line +#include "opennurbs_symmetry.h" #include "opennurbs_polyline.h" // simple polyline #include "opennurbs_cylinder.h" // simple 3d elliptical cylinder #include "opennurbs_cone.h" // simple 3d right circular cone diff --git a/opennurbs_annotationbase.cpp b/opennurbs_annotationbase.cpp index 7a197890..e55a78d9 100644 --- a/opennurbs_annotationbase.cpp +++ b/opennurbs_annotationbase.cpp @@ -1160,6 +1160,11 @@ bool ON_Annotation::EqualTextPositionProperties( : text->EqualTextPositionProperties( Type(), dimstyle ); } +static bool DimstyleHasMask(const ON_DimStyle* dimstyle) +{ + return (nullptr == dimstyle || dimstyle->DrawTextMask() || dimstyle->MaskFrameType() != ON_TextMask::MaskFrame::NoFrame); +} + const ON_SHA1_Hash ON_Annotation::Internal_GetBBox_InputHash( const ON_Viewport* vp, const ON_DimStyle* dimstyle, @@ -1185,7 +1190,12 @@ const ON_SHA1_Hash ON_Annotation::Internal_GetBBox_InputHash( { // This return ON_TextContent.m_mbox // which is a cached value - sha1.AccumulateBoundingBox(m_text->BoundingBox()); + const ON_BoundingBox textbbox = m_text->BoundingBox(); + sha1.AccumulateBoundingBox(textbbox); + if (textbbox.IsNotEmpty() && DimstyleHasMask(dimstyle)) + { + sha1.AccumulateDouble(dimstyle->MaskBorder()); + } } sha1.Accumulate2dPoint(text_point); @@ -1206,12 +1216,27 @@ bool ON_Annotation::Internal_GetBBox_TextGlyphBox( text_glyph_box = ON_BoundingBox::UnsetBoundingBox; if (m_text) { + text_glyph_box = m_text->BoundingBox(); + + // if mask, grow 2d bbox + if (text_glyph_box.IsNotEmpty() && DimstyleHasMask(dimstyle)) + { + ON_3dPoint bmin = text_glyph_box.Min(); + ON_3dPoint bmax = text_glyph_box.Max(); + double d = dimstyle->MaskBorder(); + bmin.x -= d; + bmin.y -= d; + bmax.x += d; + bmax.y += d; + text_glyph_box.m_min = bmin; + text_glyph_box.m_max = bmax; + } + ON_Xform txf; // 20 June 2017 S. Baer (RH-39835) // GetTextXform can change cached information. Make sure this // is called before m_text->BoundingBox() bool b = GetTextXform(vp, dimstyle, dimscale, txf); - text_glyph_box = m_text->BoundingBox(); if (b) { text_glyph_box.Transform(txf); @@ -2060,6 +2085,23 @@ void ON_Annotation::SetMaskFillType(const ON_DimStyle* parent_style, ON_TextMask } } +ON_TextMask::MaskFrame ON_Annotation::MaskFrameType(const ON_DimStyle* parent_style) const +{ + return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::MaskFrameType).MaskFrameType(); +} + +void ON_Annotation::SetMaskFrameType(const ON_DimStyle* parent_style, ON_TextMask::MaskFrame value) +{ + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + bool bCreate = (value != parent_style->MaskFrameType()); + ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate); + if (nullptr != override_style) + { + override_style->SetMaskFrameType(value); + override_style->SetFieldOverride(ON_DimStyle::field::MaskFrameType, bCreate); + } +} + ON_Color ON_Annotation::MaskColor(const ON_DimStyle* parent_style) const { return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::MaskColor).MaskColor(); @@ -2103,6 +2145,7 @@ void ON_Annotation::SetTextMask(const ON_DimStyle* parent_style, const ON_TextMa SetMaskColor(parent_style, local_mask.MaskColor()); SetMaskFillType(parent_style, local_mask.MaskFillType()); SetMaskBorder(parent_style, local_mask.MaskBorder()); + SetMaskFrameType(parent_style, local_mask.MaskFrameType()); } double ON_Annotation::FixedExtensionLength(const ON_DimStyle* parent_style) const @@ -3325,6 +3368,49 @@ const ON_Font* ON_Annotation::FirstCharFont() const return &ON_Font::Default; } +bool ON_Annotation::IsAllBold() const +{ + return IsAllFormat(&ON_Font::IsBold); +} + +bool ON_Annotation::IsAllItalic() const +{ + return IsAllFormat(&ON_Font::IsItalic); +} + +bool ON_Annotation::IsAllUnderlined() const +{ + return IsAllFormat(&ON_Font::IsUnderlined); +} + +bool ON_Annotation::IsAllFormat(bool (ON_Font::*func)() const) const +{ + if (nullptr == func) + return false; + const ON_TextContent* text = Text(); + if (nullptr == text) + return false; + + const ON_TextRunArray* runs = text->TextRuns(true); + if (nullptr == runs) + return false; + + for (int i = 0; i < runs->Count(); i++) + { + const ON_TextRun* run = (*runs)[i]; + if (nullptr == run) + continue; + ON_TextRun::RunType type = run->Type(); + if (ON_TextRun::RunType::kText == type || + ON_TextRun::RunType::kField == type || + ON_TextRun::RunType::kFieldValue == type) + { + if (!(run->Font()->*func)()) + return false; + } + } + return true; +} diff --git a/opennurbs_annotationbase.h b/opennurbs_annotationbase.h index c299d608..1fb38864 100644 --- a/opennurbs_annotationbase.h +++ b/opennurbs_annotationbase.h @@ -619,6 +619,10 @@ public: ON_TextMask::MaskType MaskFillType(const ON_DimStyle* parent_style) const; void SetMaskFillType(const ON_DimStyle* parent_style, ON_TextMask::MaskType source); + // Determines whether to draw a frame around a text mask + ON_TextMask::MaskFrame MaskFrameType(const ON_DimStyle* parent_style) const; + void SetMaskFrameType(const ON_DimStyle* parent_style, ON_TextMask::MaskFrame source); + ON_Color MaskColor(const ON_DimStyle* parent_style) const; // Only works right if MaskColorSource returns 1. void SetMaskColor(const ON_DimStyle* parent_style, ON_Color color); // Does not return viewport background color @@ -877,6 +881,19 @@ public: const ON_Font* FirstCharFont() const; +private: + bool IsAllFormat(bool (ON_Font::*func)() const) const; + +public: + // true if all of the text is bold + bool IsAllBold() const; + + // true if all of the text is italic + bool IsAllItalic() const; + + // true if all of the text is underlined + bool IsAllUnderlined() const; + friend class ON_Dimension; }; diff --git a/opennurbs_array_defs.h b/opennurbs_array_defs.h index a77a4e2d..9d1ad077 100644 --- a/opennurbs_array_defs.h +++ b/opennurbs_array_defs.h @@ -799,6 +799,7 @@ bool ON_SimpleArray::QuickSortAndRemoveDuplicates( int (*compar)(const T*,con continue; // duplicate if (i > clean_count) m_a[clean_count] = m_a[i]; + prev_ele = &m_a[clean_count]; ++clean_count; } if (clean_count < m_count) diff --git a/opennurbs_curve.cpp b/opennurbs_curve.cpp index 203fdb5e..9c73173b 100644 --- a/opennurbs_curve.cpp +++ b/opennurbs_curve.cpp @@ -2278,7 +2278,7 @@ ON_Curve* ON_TrimCurve( else if ( trim_parameters.IsIncreasing() ) { trimmed_curve = curve.DuplicateCurve(); - if( !trimmed_curve->Trim(trim_parameters) ) + if(!trimmed_curve || !trimmed_curve->Trim(trim_parameters) ) { delete trimmed_curve; trimmed_curve = 0; diff --git a/opennurbs_defines.h b/opennurbs_defines.h index d61426d0..dbd83ca2 100644 --- a/opennurbs_defines.h +++ b/opennurbs_defines.h @@ -1976,6 +1976,34 @@ public: // ///////////////////////////////////////////////////////////////////////////// + + /// + /// Rich text style + /// + /// The way rich text specifies fonts and other information depends on what + /// created the rich text. The interpretation of the rich text "specification" + /// varies widely and depends on the application, platform, and operating system. + /// + enum class RichTextStyle : unsigned char + { + /// Unset" + Unset = 0, + + /// Rich text for use with the Windows 10 SDK. The font table uses Windows LOGFONT names. + Windows10SDK = 1, + + /// Rich text for use with the Apple OS X SDK. The font table uses Postscript names. + AppleOSXSDK = 2, + }; + static ON::RichTextStyle RichTextStyleFromUnsigned(unsigned int u); + + /* + Returns: + ON::RichTextStyle::Windows10SDK on Windows and ON::RichTextStyle::AppleOSXSDK on OS X. + */ + static ON::RichTextStyle RichTextStyleFromCurrentPlatform(); + + //// object_type /////////////////////////////////////////////////// enum object_type { diff --git a/opennurbs_dimensionstyle.cpp b/opennurbs_dimensionstyle.cpp index 41f80718..5788aa83 100644 --- a/opennurbs_dimensionstyle.cpp +++ b/opennurbs_dimensionstyle.cpp @@ -321,6 +321,7 @@ ON_DimStyle::field ON_DimStyle::FieldFromUnsigned( ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextGap); ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextHeight); ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimTextLocation); + ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::MaskFrameType); // OBSOLETE // //ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::OBSOLETE_LengthFormat_); ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LengthResolution); ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AngleFormat); @@ -844,12 +845,14 @@ const ON_SHA1_Hash& ON_TextMask::ContentHash() const if (m_content_hash.IsZeroDigest()) { ON_SHA1 sha1; - unsigned int u[2] = { + unsigned int u[3] = { (m_bDrawMask ? 1U : 0U), - (unsigned int)(static_cast(m_mask_type)) + (unsigned int)(static_cast(m_mask_type)), + (unsigned int)(static_cast(m_mask_frame)) }; sha1.AccumulateUnsigned32(u[0]); sha1.AccumulateUnsigned32(u[1]); + sha1.AccumulateUnsigned32(u[2]); sha1.AccumulateUnsigned32(m_mask_color); sha1.AccumulateDouble(m_mask_border); m_content_hash = sha1.Hash(); @@ -910,6 +913,20 @@ void ON_TextMask::SetMaskFillType(ON_TextMask::MaskType type) } } +ON_TextMask::MaskFrame ON_TextMask::MaskFrameType() const +{ + return m_mask_frame; +} + +void ON_TextMask::SetMaskFrameType(ON_TextMask::MaskFrame frame) +{ + if (m_mask_frame != frame) + { + m_mask_frame = frame; + m_content_hash = ON_SHA1_Hash::ZeroDigest; + } +} + ON_Color ON_TextMask::MaskColor() const { return m_mask_color; @@ -951,12 +968,25 @@ ON_TextMask::MaskType ON_TextMask::MaskTypeFromUnsigned( return ON_TextMask::MaskType::BackgroundColor; } +ON_TextMask::MaskFrame ON_TextMask::MaskFrameFromUnsigned( + unsigned int mask_frame_as_unsigned +) +{ + switch (mask_frame_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextMask::MaskFrame::NoFrame); + ON_ENUM_FROM_UNSIGNED_CASE(ON_TextMask::MaskFrame::RectFrame); + } + ON_ERROR("mask_type_as_unsigned parameter is not valid"); + return ON_TextMask::MaskFrame::NoFrame; +} + bool ON_TextMask::Write( ON_BinaryArchive& archive ) const { - const int chunk_version = 0; + const int chunk_version = 1; // Oct. 9, 2019 - mask_frame if (!archive.BeginWrite3dmAnonymousChunk(chunk_version)) return false; @@ -976,6 +1006,11 @@ bool ON_TextMask::Write( // DO NOT write m_content_hash // END of chunk_version = 0 information + const unsigned int mask_frame_as_unsigned = (unsigned int)(static_cast(m_mask_frame)); + if (!archive.WriteInt(mask_frame_as_unsigned)) + break; + // END of chunk_version = 1 information + rc = true; break; } @@ -1011,6 +1046,14 @@ bool ON_TextMask::Read( break; // END of chunk_version = 0 information + if (chunk_version > 0) + { + unsigned int mask_frame_as_unsigned = (unsigned int)(static_cast(m_mask_frame)); + if (!archive.ReadInt(&mask_frame_as_unsigned)) + break; + m_mask_frame = ON_TextMask::MaskFrameFromUnsigned(mask_frame_as_unsigned); + } + rc = true; break; } @@ -5223,6 +5266,9 @@ void ON_DimStyle::OverrideFields(const ON_DimStyle& source, const ON_DimStyle& p case ON_DimStyle::field::DimTextLocation: ON_INTERNAL_UPDATE_PROPERTY(DimTextLocation); break; + case ON_DimStyle::field::MaskFrameType: + ON_INTERNAL_UPDATE_PROPERTY(MaskFrameType); + break; case ON_DimStyle::field::LengthResolution: ON_INTERNAL_UPDATE_PROPERTY(LengthResolution); break; @@ -5687,6 +5733,7 @@ void ON_DimStyle::SetTextMask(const ON_TextMask& mask) SetMaskColor(local_mask.MaskColor()); SetMaskFillType(local_mask.MaskFillType()); SetMaskBorder(local_mask.MaskBorder()); + SetMaskFrameType(local_mask.MaskFrameType()); } void ON_DimStyle::Internal_SetTextMask( @@ -5747,6 +5794,29 @@ void ON_DimStyle::SetMaskFillType(ON_TextMask::MaskType source) Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::MaskColorSource); } +ON_TextMask::MaskFrame ON_DimStyle::MaskFrameType() const +{ + // This function is for legacy compatibility. + // In October 2016, text mask information was moved from + // a collection of individual values on ON_DimStyle to + // an ON_TextMask class and a single ON_TextMask m_text_mask member + // on ON_DimStyle. + return TextMask().MaskFrameType(); +} + +void ON_DimStyle::SetMaskFrameType(ON_TextMask::MaskFrame source) +{ + // This function is for legacy compatibility. + // In October 2016, text mask information was moved from + // a collection of individual values on ON_DimStyle to + // an ON_TextMask class and a single ON_TextMask m_text_mask member + // on ON_DimStyle. + ON_TextMask text_mask = TextMask(); + text_mask.SetMaskFrameType(source); + Internal_SetTextMask(text_mask); + Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::MaskFrameType); +} + ON_Color ON_DimStyle::MaskColor() const { // This function is for legacy compatibility. diff --git a/opennurbs_dimensionstyle.h b/opennurbs_dimensionstyle.h index ed596bd1..96a2c46d 100644 --- a/opennurbs_dimensionstyle.h +++ b/opennurbs_dimensionstyle.h @@ -114,7 +114,28 @@ public: #pragma endregion static ON_TextMask::MaskType MaskTypeFromUnsigned( - unsigned int mask_type_as_unsigned + unsigned int mask_border_as_unsigned + ); + +#pragma region RH_C_SHARED_ENUM [ON_TextMask::MaskFrame] [Rhino.DocObjects.DimensionStyle.MaskFrame] [nested:byte] + /// + /// Draw a frame stroke around the text mask area + /// + enum class MaskFrame : unsigned char + { + /// + /// Text mask frame not drawn + /// + NoFrame = 0, + /// + /// Text mask frame outline rectangle drawn + /// + RectFrame = 1, + }; +#pragma endregion + + static ON_TextMask::MaskFrame MaskFrameFromUnsigned( + unsigned int mask_frame_as_unsigned ); public: @@ -156,6 +177,10 @@ public: // Can be background color or a specific color ON_TextMask::MaskType MaskFillType() const; void SetMaskFillType(ON_TextMask::MaskType source); + + // Determines whether or not to draw a rectangular frame around a text mask + ON_TextMask::MaskFrame MaskFrameType() const; + void SetMaskFrameType(ON_TextMask::MaskFrame frame); /* Returns: @@ -196,8 +221,8 @@ public: private: bool m_bDrawMask = false; ON_TextMask::MaskType m_mask_type = ON_TextMask::MaskType::BackgroundColor; - - unsigned char m_reserved1 = 0; + ON_TextMask::MaskFrame m_mask_frame = ON_TextMask::MaskFrame::NoFrame; + unsigned char m_reserved2 = 0; ON_Color m_mask_color = ON_Color::White; @@ -630,6 +655,8 @@ public: DimTextLocation = 10, //OBSOLETE_LengthFormat_ = 11, + /// Text mask frame + MaskFrameType = 11, /// LengthResolution = 12, @@ -1722,6 +1749,10 @@ public: ON_TextMask::MaskType MaskFillType() const; void SetMaskFillType(ON_TextMask::MaskType source); + // Determines whether to draw a frame around a Text Mask + ON_TextMask::MaskFrame MaskFrameType() const; + void SetMaskFrameType(ON_TextMask::MaskFrame source); + ON_Color MaskColor() const; // Only works right if MaskColorSource returns 1. // Does not return viewport background color void SetMaskColor(ON_Color color); diff --git a/opennurbs_font.cpp b/opennurbs_font.cpp index 8c7902d1..d77a189f 100644 --- a/opennurbs_font.cpp +++ b/opennurbs_font.cpp @@ -1632,6 +1632,110 @@ int ON_FontFaceQuartet::CompareQuartetName( return ON_wString::CompareOrdinal(lhs->m_quartet_name, rhs->m_quartet_name, true); } +ON::RichTextStyle ON::RichTextStyleFromUnsigned(unsigned int u) +{ + switch (u) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON::RichTextStyle::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON::RichTextStyle::Windows10SDK); + ON_ENUM_FROM_UNSIGNED_CASE(ON::RichTextStyle::AppleOSXSDK); + }; + ON_ERROR("Invalid ON::RichTextStyle value."); + return ON::RichTextStyle::Unset; +} + +ON::RichTextStyle ON::RichTextStyleFromCurrentPlatform() +{ + return +#if defined(ON_RUNTIME_WIN) + ON::RichTextStyle::Windows10SDK +#elif defined(ON_RUNTIME_APPLE) + ON::RichTextStyle::AppleOSXSDK +#else + ON::RichTextStyle::Unset +#endif + ; +} + +const ON_wString ON_FontFaceQuartet::RichTextSample( + ON::RichTextStyle rich_text_style +) const +{ + const wchar_t* quartet_name = static_cast(m_quartet_name); + if (nullptr == quartet_name) + return ON_wString::EmptyString; + if ( nullptr == m_regular && nullptr == m_bold && nullptr == m_italic && nullptr == m_bold_italic) + return ON_wString::EmptyString; + + const ON_wString regular_ps = (nullptr != m_regular) ? m_regular->PostScriptName() : ON_wString::EmptyString; + const ON_wString bold_ps = (nullptr != m_bold) ? m_bold->PostScriptName() : ON_wString::EmptyString; + const ON_wString italic_ps = (nullptr != m_italic) ? m_italic->PostScriptName() : ON_wString::EmptyString; + const ON_wString bold_italic_ps = (nullptr != m_bold_italic) ? m_bold_italic->PostScriptName() : ON_wString::EmptyString; + + const ON_wString not_available(L"Not available."); + const ON_wString regular = regular_ps.IsNotEmpty() ? regular_ps : not_available; + const ON_wString bold = bold_ps.IsNotEmpty() ? bold_ps : not_available; + const ON_wString italic = italic_ps.IsNotEmpty() ? italic_ps : not_available; + const ON_wString bold_italic = bold_italic_ps.IsNotEmpty() ? bold_italic_ps : not_available; + + ON_wString sample; + switch (rich_text_style) + { + case ON::RichTextStyle::Unset: + break; + + case ON::RichTextStyle::Windows10SDK: + // font table uses Windows LOGFONT name and \b \i to select faces + sample = ON_wString::FormatToString(L"{\\rtf1\\deff0{\\fonttbl{\\f0 %ls;}}\\fs40", quartet_name); + sample += ON_wString::FormatToString(L"{\\f0 Windows 10 LOGFONT Quartet: %ls}{\\par}", quartet_name); + if (nullptr != m_regular) + sample += ON_wString::FormatToString(L"{\\f0 Regular: %ls}{\\par}", static_cast(regular_ps)); + if (nullptr != m_bold) + sample += ON_wString::FormatToString(L"{\\f0\\b Bold: %ls}{\\par}", static_cast(bold_ps)); + if (nullptr != m_italic) + sample += ON_wString::FormatToString(L"{\\f0\\i Italic: %ls}{\\par}", static_cast(italic_ps)); + if (nullptr != m_bold_italic) + sample += ON_wString::FormatToString(L"{\\f0\\b\\i Bold Italic: %ls}{\\par}", static_cast(bold_italic_ps)); + sample += ON_wString(L"\\par}"); + break; + + case ON::RichTextStyle::AppleOSXSDK: + // font table uses unique PostScript names for each face + if (regular_ps.IsNotEmpty() || bold_ps.IsNotEmpty() || italic_ps.IsNotEmpty() || bold_italic_ps.IsNotEmpty()) + { + sample = ON_wString::FormatToString(L"{\\rtf1\\deff0{\\fonttbl"); + int fdex = 0; + if (regular_ps.IsNotEmpty()) + sample += ON_wString::FormatToString(L"{\\f%d %ls;}", fdex++, static_cast(regular_ps)); + if (bold_ps.IsNotEmpty()) + sample += ON_wString::FormatToString(L"{\\f%d %ls;}", fdex++, static_cast(bold_ps)); + if (italic_ps.IsNotEmpty()) + sample += ON_wString::FormatToString(L"{\\f%d %ls;}", fdex++, static_cast(italic_ps)); + if (bold_italic_ps.IsNotEmpty()) + sample += ON_wString::FormatToString(L"{\\f%d %ls;}", fdex++, static_cast(bold_italic_ps)); + sample += ON_wString(L"}\\fs40"); + + sample += ON_wString::FormatToString(L"{\\f0 Apple OS X Fake Quartet: %ls}{\\par}", quartet_name); + fdex = 0; + if (nullptr != m_regular) + sample += ON_wString::FormatToString(L"{\\f%d Regular: %ls}{\\par}", fdex++, static_cast(regular_ps)); + if (nullptr != m_bold) + sample += ON_wString::FormatToString(L"{\\f%d\\b Bold: %ls}{\\par}", fdex++, static_cast(bold_ps)); + if (nullptr != m_italic) + sample += ON_wString::FormatToString(L"{\\f%d\\i Italic: %ls}{\\par}", fdex++, static_cast(italic_ps)); + if (nullptr != m_bold_italic) + sample += ON_wString::FormatToString(L"{\\f%d\\b\\i Bold Italic: %ls}{\\par}", fdex++, static_cast(bold_italic_ps)); + sample += ON_wString(L"\\par}"); + } + break; + + default: + break; + } + return sample; +} + + bool ON_FontFaceQuartet::HasRegularFace() const { return (nullptr != RegularFace()); @@ -6504,8 +6608,29 @@ const ON_wString ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames( 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), + // DINPro https://mcneel.myjetbrains.com/youtrack/issue/RH-54627 + // DO NOT try making a fake quartet that combines regular/bold. + // The last parameter for all DINPro "fake quartets" must be ON_FontFaceQuartet::Member::Regular. + // When text is created and then later edited the, the Mac RTF used by Eto and the "composed" RTF used by Rhino + // are not currently capable looking at the "fake quartet" information and preserving the font. So the Mac users get + // to see 5 "fake quartets" for DINPro. The "fix" is to return to the Rhino 5 Mac font UI. + Internal_FakeWindowsLogfontName(L"DINPro", L"DINPro-Regular", L"DINPro Regular", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"DINPro", L"DINPro-Bold", L"DINPro Bold", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"DINPro", L"DINPro-Light", L"DINPro Light", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"DINPro", L"DINPro-Medium", L"DINPro Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"DINPro", L"DINPro-Black", L"DINPro Black", ON_FontFaceQuartet::Member::Regular), + + // Futura https://mcneel.myjetbrains.com/youtrack/issue/RH-56085 + // DO NOT try making a fake quartet that combines regular/bold. + // The last parameter for all Futura "fake quartets" must be ON_FontFaceQuartet::Member::Regular. + // When text is created and then later edited the, the Mac RTF used by Eto and the "composed" RTF used by Rhino + // are not currently capable looking at the "fake quartet" information and preserving the font. So the Mac users get + // to see 5 "fake quartets" for Futura. The "fix" is to return to the Rhino 5 Mac font UI. + Internal_FakeWindowsLogfontName(L"Futura", L"Futura-Medium", L"Futura Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Futura", L"Futura-Bold", L"Futura Bold", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Futura", L"Futura-MediumItalic", L"Futura Medium Italic", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Futura", L"Futura-CondensedMedium", L"Futura Condensed Medium", ON_FontFaceQuartet::Member::Regular), + Internal_FakeWindowsLogfontName(L"Futura", L"Futura-CondensedExtraBold", L"Futura Condensed ExtraBold", ON_FontFaceQuartet::Member::Regular), 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), diff --git a/opennurbs_font.h b/opennurbs_font.h index 4b47a4a5..cd6e65f3 100644 --- a/opennurbs_font.h +++ b/opennurbs_font.h @@ -2180,6 +2180,13 @@ public: const ON_FontFaceQuartet* rhs ); + /* + Returns a sample rich text string demonstrating the faces in the quartet. + */ + const ON_wString RichTextSample( + ON::RichTextStyle rich_text_style + ) const; + public: static const ON_FontFaceQuartet Empty; diff --git a/opennurbs_glyph_outline.cpp b/opennurbs_glyph_outline.cpp index b7ec56f3..e764c2c8 100644 --- a/opennurbs_glyph_outline.cpp +++ b/opennurbs_glyph_outline.cpp @@ -619,26 +619,23 @@ const ON_BoundingBox ON_OutlineFigure::BoundingBox() const ON_2fPoint bbox_cv_min = bbox_min; ON_2fPoint bbox_cv_max = bbox_max; - for (ON__UINT32 i = 0; i <= figure_end_dex; i++) + for (ON__UINT32 i = 0U; i < figure_end_dex; ++i) { const ON__UINT32 degree = ON_OutlineFigure::Internal_SegmentDegree(i); - if (0 == degree) + if (degree < 1U || degree > 3U) continue; - - ON_OutlineFigurePoint p = a[i]; - if (false == p.IsOnFigure()) - continue; + // a[i] = start of bezier (line,quadratic,or cubic) + ON_OutlineFigurePoint p = a[i+degree]; // p = a[i+degree] = end of bezier (line,quadratic,or cubic) Internal_GrowBBox(p.m_point, p.m_point, bbox_min, bbox_max); - if (degree < 2) - continue; - - p = a[++i]; - Internal_GrowBBox(p.m_point, p.m_point, bbox_cv_min, bbox_cv_max); - - if (3 == degree) + if (degree >= 2U) { - p = a[++i]; + p = a[++i]; // p = 1st interior cv of a quadratic or cubic bezier Internal_GrowBBox(p.m_point, p.m_point, bbox_cv_min, bbox_cv_max); + if (3U == degree) + { + p = a[++i]; // p = 2nd interior cv of a cubic bezier + Internal_GrowBBox(p.m_point, p.m_point, bbox_cv_min, bbox_cv_max); + } } } @@ -660,21 +657,21 @@ const ON_BoundingBox ON_OutlineFigure::BoundingBox() const ON_OutlineFigurePoint cv[4]; cv[3] = a[0]; // to suppress potintial uninitialized memory warning - for (ON__UINT32 i = 0; i <= figure_end_dex; i++) + for (ON__UINT32 i = 0U; i < figure_end_dex; ++i) { const ON__UINT32 degree = ON_OutlineFigure::Internal_SegmentDegree(i); - if (degree < 1 || degree > 3) - continue; - cv[0] = a[i++]; - cv[1] = a[i]; - cv[2] = a[i+1]; + if (degree < 2U || degree > 3U) + continue; // correct to continue when degree = 1 + cv[0] = a[i]; // [a[i] = start of quadratic or cubic bezier + cv[1] = a[++i]; // 1st interior cv of a quadratic or cubic bezer bbox_cv_min = cv[1].m_point; bbox_cv_max = bbox_cv_min; - if (3 == degree) + cv[2] = a[i + 1]; // end of quadratic bezier or penultimate cv of a cubic bezier + if (3U == degree) { Internal_GrowBBox(cv[2].m_point, cv[2].m_point, bbox_cv_min, bbox_cv_max); - i++; - cv[3] = a[i+1]; + ++i; + cv[3] = a[i + 1]; // end of a cubic bezier } if ( diff --git a/opennurbs_hash_table.cpp b/opennurbs_hash_table.cpp index f9ed79d3..e6fada60 100644 --- a/opennurbs_hash_table.cpp +++ b/opennurbs_hash_table.cpp @@ -41,6 +41,16 @@ ON__UINT32 ON_Hash32TableItem::HashTableSerialNumber() const return m_internal_hash_table_sn; } +ON__UINT32 ON_Hash32TableItem::HashTableItemHash() const +{ + return m_internal_hash32; +} + +void ON_Hash32TableItem::ClearHashTableSerialNumberForExperts() +{ + m_internal_hash_table_sn = 0; +} + //#define ON_DEBUG_ON_Hash32Table_ValidateEachTransaction #if defined(ON_DEBUG_ON_Hash32Table_ValidateEachTransaction) diff --git a/opennurbs_hash_table.h b/opennurbs_hash_table.h index 75650886..d6a736be 100644 --- a/opennurbs_hash_table.h +++ b/opennurbs_hash_table.h @@ -41,6 +41,37 @@ public: const ON_UUID& id ); + /* + Returns: + If this item has been added to an ON_Hash32Table.AddItem(hash32,item pointer) then the + value of hash3d passed as the first argument to ON_Hash32Table.AddItem(hash32,item pointer) + is returned. This is the value the ON_Hash32Table uses for this item. + Othewise 0 is returned. + Remarks: + This function is useful when copying hash tables. + + count = src_hash_table.ItemCount(); + MyHashTableItems src_items[count]; // items added to src_hash_table + + // copy src_hash_table + MyHashTableItems copied_items[count]; + copied_items = src_items; + for (unsigned i = 0; i < count; ++i) + { + ON_SubDSurfaceInterpolatortHash32TableItem& hitem = copied_items[i]; + hitem.ClearHashTableSerialNumberForExperts(); + m_htable.AddItem(hitem.HashTableItemHash(), &hitem); + } + */ + ON__UINT32 HashTableItemHash() const; + + /* + Description: + Useful when copying hash tables to remove the hash table reference from + a copied hash item. Never remove the hash table reference from an item + that is still in a hash table. + */ + void ClearHashTableSerialNumberForExperts(); private: friend class ON_Hash32Table; mutable ON_Hash32TableItem* m_internal_next = nullptr; diff --git a/opennurbs_line.cpp b/opennurbs_line.cpp index 5e3d776b..1b9faea7 100644 --- a/opennurbs_line.cpp +++ b/opennurbs_line.cpp @@ -95,7 +95,10 @@ double ON_Line::Length() const ON_3dVector ON_Line::Direction() const { - return (to-from); + return (ON_UNSET_VALUE < to.x && to.x < ON_UNSET_POSITIVE_VALUE && ON_UNSET_VALUE GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData)); + return nullptr != pUD; + } + + void RemoveUserData(void) const + { + auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData)); + + if (material->DetachUserData(pUD)) + { + delete pUD; + } + } + const ON_PhysicallyBasedMaterialUserData& UserData() const { const auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData)); @@ -6944,19 +6960,22 @@ void ON_PhysicallyBasedMaterial::SetEmission(ON_4fColor d) } ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_Material& src) - : _pImpl(new Impl(const_cast(src))) { + //Placement new - create the impl in the stack allocated space. + new (&_impl) Impl(const_cast(src)); } ON_PhysicallyBasedMaterial::~ON_PhysicallyBasedMaterial() { - delete _pImpl; + //delete _pImpl; + //Call the dtor, don't release the memory + Implementation().~Impl(); } ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_PhysicallyBasedMaterial& src) - : _pImpl(new Impl(*src._pImpl->material)) { + new (&_impl) Impl(*src.Implementation().material); } ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void) @@ -6972,20 +6991,32 @@ const ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void) const const ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(void) const { - return *_pImpl; + return *reinterpret_cast(_impl); } ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(void) { - return *_pImpl; + return *reinterpret_cast(_impl); } bool ON_PhysicallyBasedMaterial::Supported(void) const { + if (!Implementation().UserDataExists()) + return false; + return BaseColor().IsValid(); } +void ON_PhysicallyBasedMaterial::Destroy(void) +{ + ON_Material& mat = *Implementation().material; + Implementation().~Impl(); + new (&_impl) Impl(const_cast(mat)); + + Implementation().RemoveUserData(); +} + void ON_PhysicallyBasedMaterial::SynchronizeLegacyMaterial(void) { auto& mat = *Implementation().material; diff --git a/opennurbs_material.h b/opennurbs_material.h index 8ad7e28c..ead2871a 100644 --- a/opennurbs_material.h +++ b/opennurbs_material.h @@ -673,50 +673,54 @@ public: 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); + virtual int FindTexture(const wchar_t* filename, ON_Texture::TYPE type, int i0 = -1) const; + virtual int AddTexture(const ON_Texture& tx); + virtual int AddTexture( const wchar_t* filename, ON_Texture::TYPE type); + virtual 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; + virtual ON_Material& Material(void); + virtual 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); + virtual void SynchronizeLegacyMaterial(void); + + //Expert function to remove all PBR data from a material + virtual void Destroy(void); public: - ON_DEPRECATED class ON_CLASS ParametersNames + 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); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString BaseColor(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString BRDF(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Subsurface(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString SubsurfaceScatteringColor(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString SubsurfaceScatteringRadius(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Specular(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString SpecularTint(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Metallic(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Roughness(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Anisotropic(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString AnisotropicRotation(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Sheen(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString SheenTint(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Clearcoat(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString ClearcoatRoughness(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString ClearcoatBump(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString OpacityIor(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Opacity(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString OpacityRoughness(void); + ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Emission(void); }; private: class Impl; - Impl* _pImpl; const Impl& Implementation(void) const; Impl& Implementation(void); + unsigned char _impl[64]; + //Ban copying - usage should be material.PhysicallyBased().Function() ON_PhysicallyBasedMaterial& operator=(const ON_Material& src) = delete; ON_PhysicallyBasedMaterial& operator=(const ON_PhysicallyBasedMaterial& src) = delete; diff --git a/opennurbs_mesh.cpp b/opennurbs_mesh.cpp index 10b80ed7..60008a15 100644 --- a/opennurbs_mesh.cpp +++ b/opennurbs_mesh.cpp @@ -2726,7 +2726,25 @@ bool ON_Mesh::Transform( if (false == bIsValid_fV) m_V.SetCount(0); - const bool bIsValid_dV = (vertex_count == m_dV.UnsignedCount()); + bool bIsValid_dV = (vertex_count == m_dV.UnsignedCount()); + + // 2 Jan 2020 S. Baer (RH-54464) + // If the transform is moving the mesh into or out of "beyond single precision", + // set up double precision vertices in order to get our best possible precision + // after the transform. + if (false == bIsValid_dV) + { + ON_BoundingBox bbox = BoundingBox(); + ON_BoundingBox transformed_bbox = bbox; + transformed_bbox.Transform(xform); + if (ON_BeyondSinglePrecision(bbox, nullptr) || ON_BeyondSinglePrecision(transformed_bbox, nullptr)) + { + UpdateDoublePrecisionVertices(); + bIsValid_dV = (vertex_count == m_dV.UnsignedCount()); + } + } + + if (false == bIsValid_dV) m_dV.SetCount(0); diff --git a/opennurbs_nurbssurface.cpp b/opennurbs_nurbssurface.cpp index 74fbd870..1b49df49 100644 --- a/opennurbs_nurbssurface.cpp +++ b/opennurbs_nurbssurface.cpp @@ -3304,3 +3304,432 @@ bool ON_NurbsSurface::ConvertSpanToBezier( } return true; } + +static bool ValidateHermiteData( + const ON_SimpleArray& u_Parameters, + const ON_SimpleArray& v_Parameters, + const ON_ClassArray>& GridPoints, + const ON_ClassArray>& u_Tangents, + const ON_ClassArray>& v_Tangents, + const ON_ClassArray>& TwistVectors) +{ + int n = u_Parameters.Count(); + int m = v_Parameters.Count(); + if (n < 2 || m < 2) + return false; + + for (int i = 0; i < u_Parameters.Count() - 1; i++) + if (u_Parameters[i] >= u_Parameters[i + 1]) + return false; + + for (int j = 0; j < v_Parameters.Count() - 1; j++) + if (v_Parameters[j] >= v_Parameters[j + 1]) + return false; + + if (GridPoints.Count() != n) + return false; + for (int i = 0; i < GridPoints.Count(); i++) + if (GridPoints[i].Count() != m) + return false; + + if (u_Tangents.Count() != n) + return false; + for (int i = 0; i < u_Tangents.Count(); i++) + if (u_Tangents[i].Count() != m) + return false; + + if (v_Tangents.Count() != n) + return false; + for (int i = 0; i < v_Tangents.Count(); i++) + if (v_Tangents[i].Count() != m) + return false; + + if (TwistVectors.Count() != n) + return false; + for (int i = 0; i < TwistVectors.Count(); i++) + if (TwistVectors[i].Count() != m) + return false; + + return true; +} + +class ON_NurbsSurface* ON_NurbsSurface::CreateHermiteSurface( + const ON_SimpleArray& u, + const ON_SimpleArray& v, + const ON_ClassArray>& GridPoints, + const ON_ClassArray>& u_Tan, + const ON_ClassArray>& v_Tan, + const ON_ClassArray>& Twist, + class ON_NurbsSurface* hsrf ) +{ + if (!ValidateHermiteData( u, v, GridPoints, u_Tan, v_Tan, Twist )) + return nullptr; + + int n = u.Count(); + int m = v.Count(); + + if (hsrf == nullptr) + { + hsrf = ON_NurbsSurface::New(); + } + + bool rc = hsrf->Create(3, false, 4, 4, 3 * n - 2, 3 * m - 2); + if (rc) + { + // Set the knots + for (int i = 0; i < n; i++) + { + hsrf->SetKnot(0, 3 * i, u[i]); + hsrf->SetKnot(0, 3 * i + 1, u[i]); + hsrf->SetKnot(0, 3 * i + 2, u[i]); + } + for (int j = 0; j < m; j++) + { + hsrf->SetKnot(1, 3 * j, v[j]); + hsrf->SetKnot(1, 3 * j + 1, v[j]); + hsrf->SetKnot(1, 3 * j + 2, v[j]); + } + + // Set GridPoints + for (int i = 0; i < n; i++) + for (int j = 0; j < m ; j++) + hsrf->SetCV(3*i, 3*j, GridPoints[i][j]); + + // set the points on v - isos between grid points + for (int j = 0; j < m-1; j++) + for (int i = 0; i < n; i++) + { + double delv = 1.0/3.0 * (v[j + 1] - v[j]); + hsrf->SetCV(3 * i, 3 * j + 1, GridPoints[i][j] + delv * v_Tan[i][j]); + hsrf->SetCV(3 * i, 3 * j + 2, GridPoints[i][j+1] - delv * v_Tan[i][j+1]); + } + + // set the points on u - isos between grid points + for (int i = 0; i < n - 1; i++) + for (int j= 0; j < m; j++) + { + double delu = 1.0/3.0 * (u[i + 1] - u[i]); + hsrf->SetCV(3 * i + 1, 3 * j, GridPoints[i][j] + delu * u_Tan[i][j]); + hsrf->SetCV(3 * i + 2, 3 * j, GridPoints[i+1][j] - delu * u_Tan[i+1][j]); + } + + // set the interior points off the grid iso's + for( int i=0; iSetCV(3 * i + 1, 3 * j + 1, GridPoints[i][j] + ( delu * u_Tan[i][j] + delv * v_Tan[i][j]) + deluv * Twist[i][j]); + hsrf->SetCV(3 * i + 2, 3 * j + 1, GridPoints[i+1][j] + (-delu * u_Tan[i+1][j] + delv * v_Tan[i+1][j]) - deluv * Twist[i+1][j]); + hsrf->SetCV(3 * i + 1, 3 * j + 2, GridPoints[i][j+1] + ( delu * u_Tan[i][j+1] - delv * v_Tan[i][j+1]) - deluv * Twist[i][j+1]); + hsrf->SetCV(3 * i + 2, 3 * j + 2, GridPoints[i+1][j+1]+ (-delu * u_Tan[i+1][j+1]- delv * v_Tan[i+1][j+1])+ deluv * Twist[i+1][j+1]); + } + } + if (!rc) + hsrf = nullptr; + return hsrf; +} + + +ON_HermiteSurface::ON_HermiteSurface() + : m_u_count(0) + , m_v_count(0) +{ +} + +ON_HermiteSurface::ON_HermiteSurface(int u_count, int v_count) + : m_u_count(u_count) + , m_v_count(v_count) +{ + Create(u_count, v_count); +} + +ON_HermiteSurface::~ON_HermiteSurface() +{ + Destroy(); +} + +bool ON_HermiteSurface::Create(int u_count, int v_count) +{ + Destroy(); + + if (u_count < 2 || v_count < 2) + return false; + + m_u_count = u_count; + m_v_count = v_count; + + m_u_parameters.SetCapacity(m_u_count); + m_u_parameters.SetCount(m_u_count); + for (int i = 0; i < m_u_count; i++) + m_u_parameters[i] = ON_UNSET_VALUE; + + m_v_parameters.SetCapacity(m_v_count); + m_v_parameters.SetCount(m_v_count); + for (int i = 0; i < m_v_count; i++) + m_v_parameters[i] = ON_UNSET_VALUE; + + m_grid_points.SetCapacity(m_v_count); + for (int i = 0; i < m_v_count; i++) + { + ON_SimpleArray& arr = m_grid_points.AppendNew(); + arr.SetCapacity(m_u_count); + arr.SetCount(m_u_count); + arr.Zero(); + } + + m_u_tangents.SetCapacity(m_v_count); + for (int i = 0; i < m_v_count; i++) + { + ON_SimpleArray& arr = m_u_tangents.AppendNew(); + arr.SetCapacity(m_u_count); + arr.SetCount(m_u_count); + for (int j = 0; j < m_u_count; j++) + arr[j] = ON_3dPoint::UnsetPoint; + } + + m_v_tangents.SetCapacity(m_v_count); + for (int i = 0; i < m_v_count; i++) + { + ON_SimpleArray& arr = m_v_tangents.AppendNew(); + arr.SetCapacity(m_u_count); + arr.SetCount(m_u_count); + for (int j = 0; j < m_u_count; j++) + arr[j] = ON_3dVector::UnsetVector; + } + + m_twists.SetCapacity(m_v_count); + for (int i = 0; i < m_v_count; i++) + { + ON_SimpleArray& arr = m_twists.AppendNew(); + arr.SetCapacity(m_u_count); + arr.SetCount(m_u_count); + for (int j = 0; j < m_u_count; j++) + arr[j] = ON_3dVector::UnsetVector; + } + + return true; +} + +void ON_HermiteSurface::Destroy() +{ + m_u_parameters.Destroy(); + m_v_parameters.Destroy(); + + for (int i = 0; i < m_grid_points.Count(); i++) + m_grid_points[i].Destroy(); + m_grid_points.Destroy(); + + for (int i = 0; i < m_u_tangents.Count(); i++) + m_u_tangents[i].Destroy(); + m_u_tangents.Destroy(); + + for (int i = 0; i < m_v_tangents.Count(); i++) + m_v_tangents[i].Destroy(); + m_v_tangents.Destroy(); + + for (int i = 0; i < m_twists.Count(); i++) + m_twists[i].Destroy(); + m_twists.Destroy(); +} + +bool ON_HermiteSurface::IsValid() const +{ + for (int i = 0; i < m_u_parameters.Count(); i++) + { + if (!ON_IsValid(m_u_parameters[i])) + return false; + } + + for (int i = 0; i < m_v_parameters.Count(); i++) + { + if (!ON_IsValid(m_v_parameters[i])) + return false; + } + + for (int i = 0; i < m_grid_points.Count(); i++) + { + for (int j = 0; j < m_grid_points[i].Count(); j++) + { + if (m_grid_points[i][j].IsUnset()) + return false; + } + } + + for (int i = 0; i < m_u_tangents.Count(); i++) + { + for (int j = 0; j < m_u_tangents[i].Count(); j++) + { + if (m_u_tangents[i][j].IsUnset()) + return false; + } + } + + for (int i = 0; i < m_v_tangents.Count(); i++) + { + for (int j = 0; j < m_v_tangents[i].Count(); j++) + { + if (m_v_tangents[i][j].IsUnset()) + return false; + } + } + + for (int i = 0; i < m_twists.Count(); i++) + { + for (int j = 0; j < m_twists[i].Count(); j++) + { + if (m_twists[i][j].IsUnset()) + return false; + } + } + + return ValidateHermiteData( + UParameters(), + VParameters(), + GridPoints(), + UTangents(), + VTangents(), + Twists() + ); +} + +bool ON_HermiteSurface::InBounds(int u, int v) const +{ + return ( + 0 <= u && + u < m_u_count && + 0 <= v && + v < m_v_count + ); +} + +double ON_HermiteSurface::UParameterAt(int u) const +{ + double rc = ON_UNSET_VALUE; + if (0 <= u && u < m_u_count) + rc = m_u_parameters[u]; + return rc; +} + +void ON_HermiteSurface::SetUParameterAt(int u, double param) +{ + if (0 <= u && u < m_u_count) + m_u_parameters[u] = param; +} + +double ON_HermiteSurface::VParameterAt(int v) const +{ + double rc = ON_UNSET_VALUE; + if (0 <= v && v < m_v_count) + rc = m_v_parameters[v]; + return rc; +} + +void ON_HermiteSurface::SetVParameterAt(int v, double param) +{ + if (0 <= v && v < m_v_count) + m_v_parameters[v] = param; +} + +ON_3dPoint ON_HermiteSurface::PointAt(int u, int v) const +{ + ON_3dPoint rc = ON_3dPoint::UnsetPoint; + if (InBounds(u, v)) + rc = m_grid_points[v][u]; + return rc; +} + +void ON_HermiteSurface::SetPointAt(int u, int v, const ON_3dPoint& point) +{ + if (InBounds(u, v)) + m_grid_points[v][u] = point; +} + +ON_3dVector ON_HermiteSurface::UTangentAt(int u, int v) const +{ + ON_3dVector rc = ON_3dVector::UnsetVector; + if (InBounds(u, v)) + rc = m_u_tangents[v][u]; + return rc; +} + +void ON_HermiteSurface::SetUTangentAt(int u, int v, const ON_3dVector& dir) +{ + if (InBounds(u, v)) + m_u_tangents[v][u] = dir; +} + +ON_3dVector ON_HermiteSurface::VTangentAt(int u, int v) const +{ + ON_3dVector rc = ON_3dVector::UnsetVector; + if (InBounds(u, v)) + rc = m_v_tangents[v][u]; + return rc; +} + +void ON_HermiteSurface::SetVTangentAt(int u, int v, const ON_3dVector& dir) +{ + if (InBounds(u, v)) + m_v_tangents[v][u] = dir; +} + +ON_3dVector ON_HermiteSurface::TwistAt(int u, int v) const +{ + ON_3dVector rc = ON_3dVector::UnsetVector; + if (InBounds(u, v)) + rc = m_twists[v][u]; + return rc; +} + +void ON_HermiteSurface::SetTwistAt(int u, int v, const ON_3dVector& dir) +{ + if (InBounds(u, v)) + m_twists[v][u] = dir; +} + +const ON_SimpleArray& ON_HermiteSurface::UParameters() const +{ + return m_u_parameters; +} + +const ON_SimpleArray& ON_HermiteSurface::VParameters() const +{ + return m_v_parameters; +} + +const ON_ClassArray>& ON_HermiteSurface::GridPoints() const +{ + return m_grid_points; +} + +const ON_ClassArray>& ON_HermiteSurface::UTangents() const +{ + return m_u_tangents; +} + +const ON_ClassArray>& ON_HermiteSurface::VTangents() const +{ + return m_v_tangents; +} + +const ON_ClassArray>& ON_HermiteSurface::Twists() const +{ + return m_twists; +} + +ON_NurbsSurface* ON_HermiteSurface::NurbsSurface(ON_NurbsSurface* pNurbsSurface) +{ + if (!IsValid()) + return nullptr; + + return ON_NurbsSurface::CreateHermiteSurface( + UParameters(), + VParameters(), + GridPoints(), + UTangents(), + VTangents(), + Twists(), + pNurbsSurface + ); +} diff --git a/opennurbs_nurbssurface.h b/opennurbs_nurbssurface.h index a13f734b..194c0130 100644 --- a/opennurbs_nurbssurface.h +++ b/opennurbs_nurbssurface.h @@ -1046,6 +1046,54 @@ public: ON_BezierSurface& bezier_surface ) const; + /* + Description: + Create an ON_NurbsSurface satisfying interpolation conditions at a grid of points. + Parameters: + u_Parameters + v_Parameters - [in] Specifies the "u" parameters defining the grid of parameter values + u_Parameters.Count()>1 + u_Parameters are strictly increasing, i.e. u_Parameters[i] < u_Parameters[i+1] + same conditions on v_Parameters + Let n = u_Parameters.Count() and m = v_Parameters.Count(). + + Each of GridPoints, u_Tangents, v_Tangents and TwistVectors are data on a grid of points. + The size of each of these arrays must be n x m, so + GridPoints.Count() == n and GridPoints[i].Count() == m. + + GridPoints - [in] Grid of points to interpolate. + u_Tangents - [in] Grid of Tangent directions to interpolate. + v_Tangents - [in] Grid of Tangent directions to interpolate. + TwistVectors - [in] Grid of twist vectors to interpolate. + + hermite_surface -[in] optional existing ON_NurbsSurface returned here. + Returns: + A hermite-surface satisfying interpolation conditions. Null if error. + Notes: + The Hermite surface, H, is bicubic on each patch [u_i, u_(i+1)] x [v_j, v_(j+1)] + and satisfies + H( u_i, v_j) = GridData[i][j] + The first derivatives may be discontinuous at the knots + H_u_+/- ( u_i, v_j) = (Del_+/- u)_i * u_Tangents[i][j] + H_v_+/-( u_i, v_j) = (Del_+/- v)_i * v_Tangents[i][j] + Here the forward and backward difference operators + (Del_+ u)_i = u_(i+1) - u_i and (Del_- u)_i = u_i - u_(i-1) + are used for the forward (H_u_+) and backward (H_u_-) derivatives respectively. + Similarly the mixed partial derivative is defined by + H_uv_++ ( u_i, v_j) = (Del_+ u)_i * (Del_+ v)_j * TwistVector[i][j] + with 3 other possible variation in signs + */ + static + class ON_NurbsSurface* CreateHermiteSurface( + const ON_SimpleArray& u_Parameters, + const ON_SimpleArray& v_Parameters, + const ON_ClassArray>& GridPoints, + const ON_ClassArray>& u_Tangents, + const ON_ClassArray>& v_Tangents, + const ON_ClassArray>& TwistVectors, + class ON_NurbsSurface* hermite_surface = 0); + + ///////////////////////////////////////////////////////////////// // Implementation public: @@ -1993,6 +2041,92 @@ ON_NurbsSurface* ON_NurbsSurfaceQuadrilateral( ON_NurbsSurface* nurbs_surface = nullptr ); + +#if defined(ON_DLL_TEMPLATE) +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray>; +ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray>; +#endif + + +/* +Description: + Create an ON_NurbsSurface satisfying interpolation conditions at a grid of points. +Remarks: + See static ON_NurbsSurface::CreateHermiteSurface for details. +*/ +class ON_CLASS ON_HermiteSurface +{ +public: + ON_HermiteSurface(); + // Constructs a u_count by v_count grid. + ON_HermiteSurface(int u_count, int v_count); + ~ON_HermiteSurface(); + + // Constructs a u_count by v_count grid. + bool Create(int u_count, int v_count); + bool IsValid() const; + + // Specifies the "u" parameters defining the grid of parameter values. + // These parameters are strictly increasing. + double UParameterAt(int u) const; + void SetUParameterAt(int u, double param); + + // Specifies the "v" parameters defining the grid of parameter values. + // These parameters are strictly increasing. + double VParameterAt(int v) const; + void SetVParameterAt(int v, double param); + + // Grid of points to interpolate. + ON_3dPoint PointAt(int u, int v) const; + void SetPointAt(int u, int v, const ON_3dPoint& point); + + // Grid of "u" tangent directions to interpolate. + ON_3dVector UTangentAt(int u, int v) const; + void SetUTangentAt(int u, int v, const ON_3dVector& dir); + + // Grid of "v" tangent directions to interpolate. + ON_3dVector VTangentAt(int u, int v) const; + void SetVTangentAt(int u, int v, const ON_3dVector& dir); + + // Grid of twist vectors to interpolate. + ON_3dVector TwistAt(int u, int v) const; + void SetTwistAt(int u, int v, const ON_3dVector& dir); + + // Create an ON_NurbsSurface satisfying interpolation conditions at a grid of points + ON_NurbsSurface* NurbsSurface(ON_NurbsSurface* pNurbsSurface = nullptr); + +public: + // The "u" parameters defining the grid of parameter values. + const ON_SimpleArray& UParameters() const; + // The "v" parameters defining the grid of parameter values. + const ON_SimpleArray& VParameters() const; + // Grid of points to interpolate. + const ON_ClassArray>& GridPoints() const; + // Grid of tangents in "u" direction to interpolate. + const ON_ClassArray>& UTangents() const; + // Grid of tangents in "v" direction to interpolate. + const ON_ClassArray>& VTangents() const; + // Grid of twist vectors to interpolate. + const ON_ClassArray>& Twists() const; + +private: + int m_u_count; + int m_v_count; + ON_SimpleArray m_u_parameters; + ON_SimpleArray m_v_parameters; + ON_ClassArray> m_grid_points; + ON_ClassArray> m_u_tangents; + ON_ClassArray> m_v_tangents; + ON_ClassArray> m_twists; + +private: + ON_HermiteSurface(const ON_HermiteSurface&) = delete; + ON_HermiteSurface& operator=(const ON_HermiteSurface&) = default; + bool InBounds(int u, int v) const; + void Destroy(); +}; + + #if defined(ON_DLL_TEMPLATE) ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray; ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray; diff --git a/opennurbs_object.cpp b/opennurbs_object.cpp index 2cfb7247..ce331961 100644 --- a/opennurbs_object.cpp +++ b/opennurbs_object.cpp @@ -1389,7 +1389,7 @@ bool ON_Object::DetachUserData( ON_UserData* p ) ON_UserData* ON_Object::GetUserData( const ON_UUID& userdata_uuid ) const -{ + { ON_UserData* prev = nullptr; ON_UserData* p; for ( p = m_userdata_list; p; prev = p, p = p->m_userdata_next ) diff --git a/opennurbs_plane.h b/opennurbs_plane.h index dae820cf..6e9b213e 100644 --- a/opennurbs_plane.h +++ b/opennurbs_plane.h @@ -512,9 +512,14 @@ public: */ bool Flip(); -// world plane coordinate system ON_Plane(ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis); - const static - ON_Plane World_xy; + // world coordinate system ON_Plane(ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis); + const static ON_Plane World_xy; + + // world coordinate system ON_Plane(ON_3dPoint::Origin, ON_3dVector::YAxis, ON_3dVector::ZAxis); + const static ON_Plane World_yz; + + // world coordinate system ON_Plane(ON_3dPoint::Origin, ON_3dVector::ZAxis, ON_3dVector::XAxis); + const static ON_Plane World_zx; // All values are ON_UNSET_VALUE. const static diff --git a/opennurbs_point.h b/opennurbs_point.h index 2c8b7abb..231757d1 100644 --- a/opennurbs_point.h +++ b/opennurbs_point.h @@ -1924,6 +1924,8 @@ public: double y; double z; double d; // 4th coefficient of the plane equation. + + void Dump(class ON_TextLog&) const; }; #if defined(ON_DLL_TEMPLATE) diff --git a/opennurbs_public.vcxproj b/opennurbs_public.vcxproj index 98fa894b..8e73f6c6 100644 --- a/opennurbs_public.vcxproj +++ b/opennurbs_public.vcxproj @@ -22,31 +22,31 @@ {1356641D-0B22-4123-B519-A69EE5CDC7F8} Win32Proj opennurbs_public - 8.1 + 10.0 DynamicLibrary true - v141 + v142 Unicode DynamicLibrary false - v141 + v142 Unicode DynamicLibrary true - v141 + v142 Unicode DynamicLibrary false - v141 + v142 Unicode @@ -258,6 +258,7 @@ + @@ -442,6 +443,7 @@ + diff --git a/opennurbs_public.xcodeproj/project.pbxproj b/opennurbs_public.xcodeproj/project.pbxproj index 3e3bf542..53fd52c4 100644 --- a/opennurbs_public.xcodeproj/project.pbxproj +++ b/opennurbs_public.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 1D4452FF1ED6454500CD7FC1 /* opennurbs.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D4452FE1ED6454500CD7FC1 /* opennurbs.h */; }; 1D455699216D9DA100BC992F /* opennurbs_apple_nsfont.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D455698216D9DA100BC992F /* opennurbs_apple_nsfont.cpp */; }; 1D45569B216D9DAE00BC992F /* opennurbs_apple_nsfont.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D45569A216D9DAE00BC992F /* opennurbs_apple_nsfont.h */; }; + 1D54D9D02388A90B0053ECCD /* opennurbs_symmetry.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D54D9CF2388A90B0053ECCD /* opennurbs_symmetry.h */; }; + 1D54D9D22388A9310053ECCD /* opennurbs_symmetry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D54D9D12388A9310053ECCD /* opennurbs_symmetry.cpp */; }; 1D741C1B21B9E3C700AA10E5 /* opennurbs_sleeplock.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D741C1A21B9E3C600AA10E5 /* opennurbs_sleeplock.h */; }; 1D741C1D21B9E3D700AA10E5 /* opennurbs_sleeplock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D741C1C21B9E3D600AA10E5 /* opennurbs_sleeplock.cpp */; }; 1D7B99581FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D7B99571FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp */; }; @@ -345,6 +347,8 @@ 1D4452FE1ED6454500CD7FC1 /* opennurbs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs.h; sourceTree = ""; }; 1D455698216D9DA100BC992F /* opennurbs_apple_nsfont.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_apple_nsfont.cpp; sourceTree = ""; }; 1D45569A216D9DAE00BC992F /* opennurbs_apple_nsfont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_apple_nsfont.h; sourceTree = ""; }; + 1D54D9CF2388A90B0053ECCD /* opennurbs_symmetry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_symmetry.h; sourceTree = ""; }; + 1D54D9D12388A9310053ECCD /* opennurbs_symmetry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_symmetry.cpp; sourceTree = ""; }; 1D741C1A21B9E3C600AA10E5 /* opennurbs_sleeplock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_sleeplock.h; sourceTree = ""; }; 1D741C1C21B9E3D600AA10E5 /* opennurbs_sleeplock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_sleeplock.cpp; sourceTree = ""; }; 1D7B99571FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_unicode_cpsb.cpp; sourceTree = ""; }; @@ -710,6 +714,7 @@ 1DC319EF1ED653EE00DE6D26 /* Header Files */ = { isa = PBXGroup; children = ( + 1D54D9CF2388A90B0053ECCD /* opennurbs_symmetry.h */, 1D741C1A21B9E3C600AA10E5 /* opennurbs_sleeplock.h */, 1D97149E218BC3B5008B4D65 /* opennurbs_testclass.h */, 1D45569A216D9DAE00BC992F /* opennurbs_apple_nsfont.h */, @@ -870,6 +875,7 @@ 1DC319F01ED6546C00DE6D26 /* Source Files */ = { isa = PBXGroup; children = ( + 1D54D9D12388A9310053ECCD /* opennurbs_symmetry.cpp */, 1D741C1C21B9E3D600AA10E5 /* opennurbs_sleeplock.cpp */, 1D97149C218BC3A1008B4D65 /* opennurbs_testclass.cpp */, 1D455698216D9DA100BC992F /* opennurbs_apple_nsfont.cpp */, @@ -1069,6 +1075,7 @@ 1DC317E51ED652B800DE6D26 /* opennurbs_bitmap.h in Headers */, 1DC3182D1ED652B800DE6D26 /* opennurbs_function_list.h in Headers */, 1DC318081ED652B800DE6D26 /* opennurbs_cylinder.h in Headers */, + 1D54D9D02388A90B0053ECCD /* opennurbs_symmetry.h in Headers */, 1DC319981ED6534E00DE6D26 /* opennurbs_std_string.h in Headers */, 1DC318C21ED652F800DE6D26 /* opennurbs_leader.h in Headers */, 1DC3180C1ED652B800DE6D26 /* opennurbs_defines.h in Headers */, @@ -1326,6 +1333,7 @@ 1DC318C11ED652F800DE6D26 /* opennurbs_leader.cpp in Sources */, 1DC318C51ED652F800DE6D26 /* opennurbs_line.cpp in Sources */, 1DC318E21ED652F800DE6D26 /* opennurbs_model_component.cpp in Sources */, + 1D54D9D22388A9310053ECCD /* opennurbs_symmetry.cpp in Sources */, 1DC319AF1ED6534E00DE6D26 /* opennurbs_subd.cpp in Sources */, 1DC317DD1ED652B800DE6D26 /* opennurbs_base64.cpp in Sources */, 1DBFBF3C1EDF333C005B50AF /* opennurbs_memory_util.cpp in Sources */, diff --git a/opennurbs_public_staticlib.vcxproj b/opennurbs_public_staticlib.vcxproj index 042b70ab..bfdb5da6 100644 --- a/opennurbs_public_staticlib.vcxproj +++ b/opennurbs_public_staticlib.vcxproj @@ -22,32 +22,32 @@ {23288C65-E3EB-4D09-A648-22E1636EB40F} Win32Proj opennurbs_public_staticlib - 8.1 + 10.0 StaticLibrary true - v141 + v142 Unicode StaticLibrary false - v141 + v142 true Unicode StaticLibrary true - v141 + v142 Unicode StaticLibrary false - v141 + v142 true Unicode @@ -258,6 +258,7 @@ + @@ -441,6 +442,7 @@ + diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index 55d195f7..cf9bd6a2 100644 --- a/opennurbs_public_version.h +++ b/opennurbs_public_version.h @@ -13,11 +13,11 @@ // These are set automatically by the build system as the // first step in each build. // -#define RMA_VERSION_YEAR 2019 -#define RMA_VERSION_MONTH 11 -#define RMA_VERSION_DATE 5 -#define RMA_VERSION_HOUR 17 -#define RMA_VERSION_MINUTE 46 +#define RMA_VERSION_YEAR 2020 +#define RMA_VERSION_MONTH 1 +#define RMA_VERSION_DATE 16 +#define RMA_VERSION_HOUR 10 +#define RMA_VERSION_MINUTE 47 //////////////////////////////////////////////////////////////// // @@ -35,9 +35,9 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#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 VERSION_WITH_COMMAS 7,0,20016,10470 +#define VERSION_WITH_PERIODS 7.0.20016.10470 +#define COPYRIGHT "Copyright (C) 1993-2020, Robert McNeel & Associates. All Rights Reserved." #define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library." #define RMA_VERSION_NUMBER_MAJOR_STRING "7" @@ -47,8 +47,8 @@ #define RMA_VERSION_NUMBER_SR_STRING "SR0" #define RMA_VERSION_NUMBER_SR_WSTRING L"SR0" -#define RMA_VERSION_WITH_PERIODS_STRING "7.0.19309.17460" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"7.0.19309.17460" +#define RMA_VERSION_WITH_PERIODS_STRING "7.0.20016.10470" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"7.0.20016.10470" diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp index 9b379a06..45d251b8 100644 --- a/opennurbs_statics.cpp +++ b/opennurbs_statics.cpp @@ -651,6 +651,22 @@ const ON_3fVector ON_3fVector::ZAxis(0.0f, 0.0f, 1.0f); const ON_WindingNumber ON_WindingNumber::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_WindingNumber); +const double ON_Symmetry::ZeroTolerance = 1.0e-8; + +const ON_Symmetry ON_Symmetry::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Symmetry); + +// {3C6C7ABD-F3D5-41C2-96C9-DB9AEAF06E90} +const ON_UUID ON_Symmetry::ReflectId = +{ 0x3c6c7abd, 0xf3d5, 0x41c2, { 0x96, 0xc9, 0xdb, 0x9a, 0xea, 0xf0, 0x6e, 0x90 } }; + +// {C1592254-DEAC-4E8E-B01E-0522450E03F7} +const ON_UUID ON_Symmetry::RotateId = +{ 0xc1592254, 0xdeac, 0x4e8e, { 0xb0, 0x1e, 0x5, 0x22, 0x45, 0xe, 0x3, 0xf7 } }; + +// {9133927D-5A4E-4DDD-9924-EF3A6360C19A} +const ON_UUID ON_Symmetry::ReflectAndRotateId = +{ 0x9133927d, 0x5a4e, 0x4ddd, { 0x99, 0x24, 0xef, 0x3a, 0x63, 0x60, 0xc1, 0x9a } }; + static ON_BoundingBox BoundingBoxInit(double x) { ON_BoundingBox bbox; @@ -848,10 +864,14 @@ const ON_PlaneEquation ON_PlaneEquation::UnsetPlaneEquation(ON_UNSET_VALUE, ON_U const ON_PlaneEquation ON_PlaneEquation::ZeroPlaneEquation(0.0, 0.0, 0.0, 0.0); const ON_PlaneEquation ON_PlaneEquation::NanPlaneEquation(ON_DBL_QNAN, ON_DBL_QNAN, ON_DBL_QNAN, ON_DBL_QNAN); -const ON_Plane ON_xy_plane(ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis); -const ON_Plane ON_yz_plane(ON_3dPoint::Origin, ON_3dVector::YAxis, ON_3dVector::ZAxis); -const ON_Plane ON_zx_plane(ON_3dPoint::Origin, ON_3dVector::ZAxis, ON_3dVector::XAxis); -const ON_Plane ON_Plane::World_xy = ON_xy_plane; +const ON_Plane ON_Plane::World_xy(ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis); +const ON_Plane ON_Plane::World_yz(ON_3dPoint::Origin, ON_3dVector::YAxis, ON_3dVector::ZAxis); +const ON_Plane ON_Plane::World_zx(ON_3dPoint::Origin, ON_3dVector::ZAxis, ON_3dVector::XAxis); + +// obsolete names for world planes +const ON_Plane ON_xy_plane = ON_Plane::World_xy; +const ON_Plane ON_yz_plane = ON_Plane::World_yz; +const ON_Plane ON_zx_plane = ON_Plane::World_zx; static ON_Plane ON_Plane_UnsetPlane() { @@ -2330,6 +2350,8 @@ 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_SubDComponentList ON_SubDComponentList::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDComponentList); + const ON_SubDEdgeChain ON_SubDEdgeChain::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDEdgeChain); @@ -2373,9 +2395,9 @@ const double ON_SubDSectorType::SmoothSectorTheta = 0.5*ON_PI; const double ON_SubDSectorType::UnsetSectorTheta = -8882.0; const double ON_SubDSectorType::ErrorSectorTheta = -9992.0; -const double ON_SubDSectorType::IgnoredSectorWeight = 0.0; -const double ON_SubDSectorType::UnsetSectorWeight = -8883.0; -const double ON_SubDSectorType::ErrorSectorWeight = -9993.0; +const double ON_SubDSectorType::IgnoredSectorCoefficient = 0.0; +const double ON_SubDSectorType::UnsetSectorCoefficient = -8883.0; +const double ON_SubDSectorType::ErrorSectorCoefficient = -9993.0; const ON_SubDComponentRegionIndex ON_SubDComponentRegionIndex::Zero ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDComponentRegionIndex); @@ -2526,6 +2548,11 @@ const ON_ToSubDParameters ON_ToSubDParameters::InteriorCreaseAtMeshCrease = ON_S const ON_ToSubDParameters ON_ToSubDParameters::InteriorCreaseAtMeshEdge = ON_SubDCreaseParameters_CreaseAt(ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge); const ON_ToSubDParameters ON_ToSubDParameters::ConvexCornerAtMeshCorner = ON_SubDCreaseParameters_ConvexCorners(); +const ON_SubDComponentFilter ON_SubDComponentFilter::Unset = ON_SubDComponentFilter::Create(true, true, true); +const ON_SubDComponentFilter ON_SubDComponentFilter::OnlyVertices = ON_SubDComponentFilter::Create(true, false, false); +const ON_SubDComponentFilter ON_SubDComponentFilter::OnlyEdges = ON_SubDComponentFilter::Create(false, true, false); +const ON_SubDComponentFilter ON_SubDComponentFilter::OnlyFaces = ON_SubDComponentFilter::Create(false, false, true); + unsigned int ON_ModelComponent::Internal_SystemComponentHelper() { static unsigned int rc = 0; diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp index 49f73126..6cd4b238 100644 --- a/opennurbs_subd.cpp +++ b/opennurbs_subd.cpp @@ -10,7 +10,6 @@ #include "opennurbs_subd_data.h" -/* $NoKeywords: $ */ /* // // Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved. @@ -389,6 +388,42 @@ const class ON_SubDVertex* ON_SubDEdgePtr::RelativeVertex( return nullptr; } +const ON_3dPoint ON_SubDEdgePtr::RelativeControlNetPoint( + int relative_vertex_index +) const +{ + const ON_SubDVertex* v = RelativeVertex(relative_vertex_index); + return (nullptr != v) ? v->ControlNetPoint() : ON_3dPoint::NanPoint; +} + +const ON_Line ON_SubDEdgePtr::RelativeControlNetLine() const +{ + return ON_Line(RelativeControlNetPoint(0), RelativeControlNetPoint(1)); +} + +const ON_3dVector ON_SubDEdgePtr::RelativeControlNetDirection() const +{ + return RelativeControlNetLine().Direction(); +} + +double ON_SubDEdgePtr::RelativeSectorCoefficient( + int relative_vertex_index +) const +{ + for (;;) + { + if (relative_vertex_index < 0 || relative_vertex_index>1) + break; + const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr); + if (nullptr == edge) + break; + if (0 != ON_SUBD_EDGE_DIRECTION(m_ptr)) + relative_vertex_index = 1 - relative_vertex_index; + return edge->m_sector_coefficient[relative_vertex_index]; + } + return ON_SubDSectorType::ErrorSectorCoefficient; +} + const ON_3dVector ON_SubDEdgePtr::RelativeDirection() const { for (;;) @@ -441,6 +476,38 @@ const ON_SubDEdgePtr ON_SubDEdgePtr::Create( return ON_SubDEdgePtr::Create(edge_element.Edge(), edge_element.ComponentDirection()); } +const ON_SubDEdgePtr ON_SubDEdgePtr::CreateFromStartVertex( + const class ON_SubDEdge* edge, + const ON_SubDVertex* start_vertex +) +{ + for (;;) + { + if (nullptr == edge || nullptr == start_vertex) + break; + if (edge->m_vertex[0] == edge->m_vertex[1]) + break; + + ON__UINT_PTR dir; + if (start_vertex == edge->m_vertex[0]) + dir = 0; + else if (start_vertex == edge->m_vertex[1]) + dir = 1; + else + break; + return ON_SubDEdgePtr::Create(edge, dir); + } + return ON_SubDEdgePtr::Null; +} + +const ON_SubDEdgePtr ON_SubDEdgePtr::CreateFromEndVertex( + const class ON_SubDEdge* edge, + const ON_SubDVertex* end_vertex +) +{ + return CreateFromStartVertex(edge,end_vertex).Reversed(); +} + ////////////////////////////////////////////////////////////////////////// // // ON_SubDFacePtr @@ -484,6 +551,11 @@ const ON_ComponentStatus ON_SubDFacePtr::Status() const return (nullptr == face) ? ON_ComponentStatus::NoneSet : face->m_status; } +const ON_SubDFacePtr ON_SubDFacePtr::Reversed() const +{ + return ON_SubDFacePtr::Create(ON_SUBD_FACE_POINTER(m_ptr), 1 - (m_ptr & 1)); +} + const ON_SubDFacePtr ON_SubDFacePtr::Create( const class ON_SubDFace* face, ON__UINT_PTR direction @@ -1333,6 +1405,21 @@ const ON_SubDComponentPtr ON_SubDComponentPtrPair::Second() const return m_pair[1]; } +bool ON_SubDComponentPtrPair::FirstIsNull() const +{ + return (0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[0].m_ptr)); +} + +bool ON_SubDComponentPtrPair::SecondIsNull() const +{ + return (0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[1].m_ptr)); +} + +bool ON_SubDComponentPtrPair::BothAreNull() const +{ + return (0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[0].m_ptr)) && 0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[1].m_ptr); +} + ////////////////////////////////////////////////////////////////////////// // // ON_ToSubDParameters @@ -1625,6 +1712,54 @@ unsigned int ON_SubDVertex::MarkedFaceCount() const return mark_count; } +unsigned int ON_SubDVertex::MinimumFaceEdgeCount() const +{ + unsigned short min_count = 0xFFFFU; + for (unsigned short vfi = 0; vfi < m_face_count; ++vfi) + { + const ON_SubDFace* f = m_faces[vfi]; + if (nullptr != f && f->m_edge_count < min_count) + min_count = f->m_edge_count; + } + return min_count < 0xFFFFU ? min_count : 0; +} + +unsigned int ON_SubDVertex::MaximumFaceEdgeCount() const +{ + unsigned short max_count = 0; + for (unsigned short vfi = 0; vfi < m_face_count; ++vfi) + { + const ON_SubDFace* f = m_faces[vfi]; + if (nullptr != f && f->m_edge_count < max_count) + max_count = f->m_edge_count; + } + return max_count; +} + + +unsigned int ON_SubDVertex::MinimumEdgeFaceCount() const +{ + unsigned short min_count = 0xFFFFU; + 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_face_count < min_count) + min_count = e->m_face_count; + } + return min_count < 0xFFFFU ? min_count : 0; +} + +unsigned int ON_SubDVertex::MaximumEdgeFaceCount() const +{ + unsigned short max_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_face_count > max_count) + max_count = e->m_face_count; + } + return max_count; +} unsigned int ON_SubDEdge::MarkedVertexCount() const { @@ -1761,13 +1896,69 @@ bool ON_SubDVertex::IsSmoothOrCrease() const return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Crease == m_vertex_tag); } +bool ON_SubDVertex::GetBoundaryVertexEdges( + ON_SubDEdgePtr* eptr0, + ON_SubDEdgePtr* eptr1 +) const +{ + unsigned int vbi[2] = {}; + const bool rc = GetBoundaryVertexEdgeIndices(&vbi[0], &vbi[1]); + if (rc) + { + if (nullptr != eptr0) + *eptr0 = m_edges[vbi[0]]; + if (nullptr != eptr1) + *eptr1 = m_edges[vbi[1]]; + } + else + { + if (nullptr != eptr0) + *eptr0 = ON_SubDEdgePtr::Null; + if (nullptr != eptr1) + *eptr1 = ON_SubDEdgePtr::Null; + } + return rc; +} + +bool ON_SubDVertex::GetBoundaryVertexEdgeIndices( + unsigned* vei0, + unsigned* vei1 +) const +{ + unsigned int vbi_count = 0; + unsigned int vbi[2] = {}; + for (unsigned short vei = 0; vei < m_edge_count; vei++) + { + const ON_SubDEdge* e = m_edges[vei].Edge(); + if (1 == e->m_face_count) + { + if (vbi_count < 2) + vbi[vbi_count++] = vei; + else + { + vbi_count = 0; + break; + } + } + } + if (2 != vbi_count) + vbi[0] = vbi[1] = ON_UNSET_UINT_INDEX; + if (nullptr != vei0) + *vei0 = vbi[0]; + if (nullptr != vei1) + *vei1 = vbi[1]; + return (2 == vbi_count); +} + const ON_SubDVertexEdgeProperties ON_SubDVertex::EdgeProperties() const { ON_SubDVertexEdgeProperties ep; + ep.m_edge_count = m_edge_count; + ep.m_face_count = m_face_count; + bool bFirstEdge = true; - const unsigned short edge_count = m_edge_count; - for (unsigned short vei = 0; vei < edge_count; vei++) + for (unsigned short vei = 0; vei < ep.m_edge_count; vei++) { const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); if (nullptr == e) @@ -1974,6 +2165,41 @@ bool ON_SubDVertex::HasBoundaryVertexTopology() const return false; } + +bool ON_SubDVertexEdgeProperties::HasInteriorVertexTopology() const +{ + return + m_null_edge_count == 0 + && m_edge_count == m_face_count + && m_boundary_edge_count == 0 + && m_interior_edge_count >= 2 + && m_nonmanifold_edge_count == 0 + ; +} + +bool ON_SubDVertexEdgeProperties::HasBoundaryVertexTopology() const +{ + return + m_null_edge_count == 0 + && m_edge_count == m_face_count+1 + && m_boundary_edge_count == 2 + && m_nonmanifold_edge_count == 0 + ; +} + +bool ON_SubDVertexEdgeProperties::HasManifoldVertexTopology() const +{ + return HasInteriorVertexTopology() || HasBoundaryVertexTopology(); +} + +bool ON_SubDVertexEdgeProperties::HasNonmanifoldVertexTopology() const +{ + return + (m_null_edge_count == 0) + && (m_wire_edge_count > 0 || m_nonmanifold_edge_count > 0) + ; +} + bool ON_SubDVertex::IsStandard() const { if (nullptr == m_edges) @@ -2481,7 +2707,7 @@ unsigned int ON_SubDEdge::FaceArrayIndex( { if (f == ON_SUBD_FACE_POINTER(m_face2[0].m_ptr)) return 0; - if (face_count >= 1) + if (face_count > 1) { if (f == ON_SUBD_FACE_POINTER(m_face2[1].m_ptr)) return 1; @@ -2691,6 +2917,11 @@ const ON_3dPoint ON_SubDEdge::ControlNetPoint( unsigned int i) const return (ON_3dPoint(m_vertex[i]->m_P)); } +const ON_Line ON_SubDEdge::ControlNetLine() const +{ + return ON_Line(ControlNetPoint(0), ControlNetPoint(1)); +} + const ON_3dVector ON_SubDEdge::ControlNetDirection() const { if (nullptr == m_vertex[0] || nullptr == m_vertex[1]) @@ -2786,6 +3017,13 @@ void ON_SubDFace::CopyFrom( else m_edge_count = 0; } + + // RH-56133 need to copy texture coordinate information that was recently added. + m_texture_coordinate_origin[0] = src->m_texture_coordinate_origin[0]; + m_texture_coordinate_origin[1] = src->m_texture_coordinate_origin[1]; + m_texture_coordinate_delta[0] = src->m_texture_coordinate_delta[0]; + m_texture_coordinate_delta[1] = src->m_texture_coordinate_delta[1]; + m_texture_coordinate_bits = src->m_texture_coordinate_bits; } const ON_SubDEdgePtr ON_SubDFace::EdgePtr( @@ -3575,11 +3813,11 @@ static bool ON_SubDIsNotValid(bool bSilentError) return bSilentError ? false : ON_IsNotValid(); } -static bool EdgeVertexWeightIsSet( - double edge_vertex_weight +static bool EdgeSectorCoefficientIsSet( + double edge_sector_coefficient ) { - return (0.0 < edge_vertex_weight && edge_vertex_weight < 1.0); + return (0.0 < edge_sector_coefficient && edge_sector_coefficient < 1.0); } static bool EdgeSectorWeightIsValid( @@ -3590,7 +3828,7 @@ static bool EdgeSectorWeightIsValid( if (0.0 <= edge_vertex_weight && edge_vertex_weight < 1.0) return true; - if (ON_SubDSectorType::UnsetSectorWeight == edge_vertex_weight && nullptr != edge && 0 == edge->SubdivisionLevel()) + if (ON_SubDSectorType::UnsetSectorCoefficient == edge_vertex_weight && nullptr != edge && 0 == edge->SubdivisionLevel()) return true; return false; @@ -3644,11 +3882,11 @@ static bool IsValidVertexEdgeLink( 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])) + const double expected_sector_coefficient = st.SectorCoefficient(); + if (false == (expected_sector_coefficient == edge->m_sector_coefficient[end_index])) return ON_SubDIsNotValid(bSilentError); - if (false == EdgeVertexWeightIsSet(expected_vertex_weight)) + if (false == EdgeSectorCoefficientIsSet(expected_sector_coefficient)) return ON_SubDIsNotValid(bSilentError); } } @@ -3891,10 +4129,18 @@ static bool IsValidSubDVertexTag( if (false == bValidEdgeTags) break; // invalid edge tags detected in IsValidSubDEdgeTag(); - if ( crease_edge_count < 2 ) + if (0 == crease_edge_count) { + // currently, isolated vertices are not permitted - may change in the future return ON_SubDIsNotValid(bSilentError); } + if (1 == crease_edge_count) + { + // must be a single wire crease edge ending at this vertex + if ( 1 != vertex_edge_count || 0 != vertex->m_face_count) + return ON_SubDIsNotValid(bSilentError); + + } break; case ON_SubD::VertexTag::Dart: @@ -4341,9 +4587,11 @@ bool ON_SubDimple::IsValidLevel( if ( e_id_range[1] > m_max_edge_id ) return ON_SubDIsNotValid(bSilentError); - // currently, wire edges are not permitted - if (wire_edge_count > 0) - return ON_SubDIsNotValid(bSilentError); + // As of NOvember 12, 2019 + // Wire edges are permitted. THey exist in subds being edited. + ////// currently, wire edges are not permitted + ////if (wire_edge_count > 0) + //// return ON_SubDIsNotValid(bSilentError); // simple face validation if (level_index == subd.ActiveLevelIndex()) @@ -4596,6 +4844,8 @@ unsigned int ON_SubD::DumpTopology( text_log.Print(L"SubD[%" PRIu64 "]: texture domain type = %ls.\n", runtime_sn, static_cast(subd_texture_domain)); + text_log.Print(L"Levels:\n"); + ON_SubDLevelIterator lit(subdimple->LevelIterator()); const ON_2udex empty_id_range(ON_UNSET_UINT_INDEX, 0); @@ -4671,7 +4921,67 @@ unsigned int ON_SubDLevel::DumpTopology( unsigned int edge_error_count = 0; unsigned int face_error_count = 0; - text_log.Print(L"SubD level %u topology: %u vertices, %u edges, ", m_level_index, m_vertex_count, m_edge_count); + text_log.Print(L"SubD level %u topology: %u vertices, %u edges", m_level_index, m_vertex_count, m_edge_count); + + + unsigned int wire_edge_count = 0U; + unsigned int boundary_edge_count = 0U; + unsigned int interior_edge_count = 0U; + unsigned int nonmanifold_edge_count = 0U; + for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) + { + if (0 == e->m_face_count) + ++wire_edge_count; + else if (1 == e->m_face_count) + ++boundary_edge_count; + else if (2 == e->m_face_count) + ++interior_edge_count; + else if (e->m_face_count >= 3) + ++nonmanifold_edge_count; + } + + if (wire_edge_count > 0U) + { + if (nonmanifold_edge_count > 0U) + { + if (boundary_edge_count > 0U && interior_edge_count > 0U) + text_log.Print(L" (%u boundary, %u interior, %u wire, %u nonmanifold)", boundary_edge_count, interior_edge_count, wire_edge_count, nonmanifold_edge_count); + else if (boundary_edge_count > 0U) + text_log.Print(L" (%u boundary, %u wire, %u nonmanifold)", boundary_edge_count, wire_edge_count, nonmanifold_edge_count); + else if (interior_edge_count > 0U) + text_log.Print(L" (%u interior, %u wire, %u nonmanifold)", interior_edge_count, wire_edge_count, nonmanifold_edge_count); + else + text_log.Print(L" (%u wire, %u nonmanifold)", wire_edge_count, nonmanifold_edge_count); + } + else + { + if (boundary_edge_count > 0U && interior_edge_count > 0U) + text_log.Print(L" (%u boundary, %u interior, %u wire)", boundary_edge_count, interior_edge_count, wire_edge_count); + else if (boundary_edge_count > 0U) + text_log.Print(L" (%u boundary, %u wire)", boundary_edge_count, wire_edge_count); + else if (interior_edge_count > 0U) + text_log.Print(L" (%u interior, %u wire)", interior_edge_count, wire_edge_count); + else + text_log.Print(L" (%u wire)", wire_edge_count); + } + } + else if (nonmanifold_edge_count > 0U) + { + if (boundary_edge_count > 0U && interior_edge_count > 0U) + text_log.Print(L" (%u boundary, %u interior, %u nonmanifold)", boundary_edge_count, interior_edge_count, nonmanifold_edge_count); + else if (boundary_edge_count > 0U) + text_log.Print(L" (%u boundary, %u nonmanifold)", boundary_edge_count, nonmanifold_edge_count); + else if (interior_edge_count > 0U) + text_log.Print(L" (%u interior, %u nonmanifold)", interior_edge_count, nonmanifold_edge_count); + else + text_log.Print(L" (%u nonmanifold)", nonmanifold_edge_count); + } + else if (boundary_edge_count > 0U && interior_edge_count > 0U) + { + text_log.Print(L" (%u boundary, %u interior)", boundary_edge_count, interior_edge_count); + } + + text_log.Print(L", "); unsigned int ngon_count[65] = {}; unsigned int maxN = (unsigned int)(sizeof(ngon_count) / sizeof(ngon_count[0])) - 1; @@ -4729,6 +5039,80 @@ unsigned int ON_SubDLevel::DumpTopology( if (IsEmpty()) return 0; + unsigned int damaged_vertex_count = 0; + unsigned int damaged_edge_count = 0; + unsigned int damaged_face_count = 0; + enum : unsigned int + { + damaged_id_list_capacity = 8 + }; + for (;;) + { + unsigned int damaged_vertex_id[damaged_id_list_capacity] = {}; + unsigned int damaged_edge_id[damaged_id_list_capacity] = {}; + unsigned int damaged_face_id[damaged_id_list_capacity] = {}; + for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) + { + if (false == v->m_status.IsDamaged()) + continue; + if (damaged_vertex_count < damaged_id_list_capacity) + damaged_vertex_id[damaged_vertex_count] = v->m_id; + ++damaged_vertex_count; + } + for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) + { + if (false == e->m_status.IsDamaged()) + continue; + if (damaged_edge_count < damaged_id_list_capacity) + damaged_edge_id[damaged_edge_count] = e->m_id; + ++damaged_edge_count; + } + for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) + { + if (false == f->m_status.IsDamaged()) + continue; + if (damaged_face_count < damaged_id_list_capacity) + damaged_face_id[damaged_face_count] = f->m_id; + ++damaged_face_count; + } + + if (0U == damaged_vertex_count && 0U == damaged_edge_count && 0U == damaged_face_count) + break; + text_log.Print("DAMAGED SubD level:\n"); + ON_TextLogIndent indent1(text_log); + if (damaged_vertex_count > 0) + { + text_log.Print(L"%u DAMAGED vertices: ", damaged_vertex_count); + text_log.Print("v%u", damaged_vertex_id[0]); + for (unsigned i = 1U; i < damaged_vertex_count; ++i) + text_log.Print(", v%u", damaged_vertex_id[i]); + if (damaged_vertex_count > damaged_id_list_capacity) + text_log.Print(", ..."); + text_log.PrintNewLine(); + } + if (damaged_edge_count > 0) + { + text_log.Print(L"%u DAMAGED edges: ", damaged_edge_count); + text_log.Print("e%u", damaged_edge_id[0]); + for (unsigned i = 1U; i < damaged_edge_count; ++i) + text_log.Print(", e%u", damaged_edge_id[i]); + if (damaged_edge_count > damaged_id_list_capacity) + text_log.Print(", ..."); + text_log.PrintNewLine(); + } + if (damaged_face_count > 0) + { + text_log.Print(L"%u DAMAGED faces: ", damaged_face_count); + text_log.Print("f%u", damaged_face_id[0]); + for (unsigned i = 1U; i < damaged_face_count; ++i) + text_log.Print(", f%u", damaged_face_id[i]); + if (damaged_face_count > damaged_id_list_capacity) + text_log.Print(", ..."); + text_log.PrintNewLine(); + } + break; + } + /////////////////////////////////////////////////////////////////// // // Vertex Topology @@ -4736,10 +5120,12 @@ unsigned int ON_SubDLevel::DumpTopology( // vEdges[vertex_edge_count] = { +eA, -eB, ... } // vFaces[vertex_edge_count] = { fP, fQ, fR, ... } // + damaged_vertex_count = 0U; unsigned int vertex_count = 0; unsigned int vertex_dump_count = 0; ON_2udex skipped_vertex_id = ON_2udex::Zero; unsigned int max_vertex_id = 0; + bool bSkippedPreviousComponent = false; for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex) { if (vertex_count >= m_vertex_count && v->SubdivisionLevel() != level_index) @@ -4748,8 +5134,11 @@ unsigned int ON_SubDLevel::DumpTopology( max_vertex_id = v->m_id; vertex_count++; + const bool bIsDamaged = v->m_status.IsDamaged(); + if (bIsDamaged) + ++damaged_vertex_count; - if (bVertexIdTest) + if (bVertexIdTest && (false == bIsDamaged || damaged_vertex_count > damaged_id_list_capacity)) { bool bSkip = true; for (;;) @@ -4780,6 +5169,7 @@ unsigned int ON_SubDLevel::DumpTopology( skipped_vertex_id.i = v->m_id; else if (v->m_id > skipped_vertex_id.j) skipped_vertex_id.j = v->m_id; + bSkippedPreviousComponent = true; continue; } } @@ -4814,12 +5204,29 @@ unsigned int ON_SubDLevel::DumpTopology( break; } - text_log.Print( - "v%u: %ls (%g, %g, %g)\n", - v->m_id, - static_cast(vtag), - P0.x, P0.y, P0.z - ); + if (bSkippedPreviousComponent) + { + text_log.Print(L"...\n"); + bSkippedPreviousComponent = false; + } + if (bIsDamaged) + { + text_log.Print( + "v%u: (DAMAGED) %ls (%g, %g, %g)\n", + v->m_id, + static_cast(vtag), + P0.x, P0.y, P0.z + ); + } + else + { + text_log.Print( + "v%u: %ls (%g, %g, %g)\n", + v->m_id, + static_cast(vtag), + P0.x, P0.y, P0.z + ); + } text_log.PushIndent(); @@ -4916,10 +5323,14 @@ unsigned int ON_SubDLevel::DumpTopology( // eN (+vA, -vB) // eFaces[edge_face_count] = { fP, fQ, fR, ... } // + damaged_edge_count = 0U; + wire_edge_count = 0U; + nonmanifold_edge_count = 0U; unsigned int edge_count = 0; unsigned int edge_dump_count = 0; ON_2udex skipped_edge_id = ON_2udex::Zero; unsigned int max_edge_id = 0; + bSkippedPreviousComponent = false; for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge) { if (edge_count >= m_edge_count && e->SubdivisionLevel() != level_index) @@ -4928,7 +5339,22 @@ unsigned int ON_SubDLevel::DumpTopology( max_edge_id = e->m_id; edge_count++; - if (bEdgeIdTest) + const bool bIsDamaged = e->m_status.IsDamaged(); + if (bIsDamaged) + ++damaged_edge_count; + const bool bIsWireEdge = (0U == e->m_face_count); + if (bIsWireEdge) + ++wire_edge_count; + const bool bIsNonmanifoldEdge = (e->m_face_count >= 3U); + if (bIsNonmanifoldEdge) + ++nonmanifold_edge_count; + + if ( + bEdgeIdTest + && (false == bIsDamaged || damaged_edge_count > damaged_id_list_capacity) + && (false == bIsWireEdge || wire_edge_count > damaged_id_list_capacity) + && (false == bIsNonmanifoldEdge || nonmanifold_edge_count > damaged_id_list_capacity) + ) { bool bSkip = true; for (;;) @@ -4959,6 +5385,7 @@ unsigned int ON_SubDLevel::DumpTopology( skipped_edge_id.i = e->m_id; else if (e->m_id > skipped_edge_id.j) skipped_edge_id.j = e->m_id; + bSkippedPreviousComponent = true; continue; } } @@ -4988,11 +5415,65 @@ unsigned int ON_SubDLevel::DumpTopology( break; } - text_log.Print( - "e%u: %ls (", - e->m_id, - static_cast(etag) - ); + if (bSkippedPreviousComponent) + { + text_log.Print(L"...\n"); + bSkippedPreviousComponent = false; + } + if (bIsDamaged) + { + if (bIsWireEdge) + { + text_log.Print( + "e%u: (DAMAGED) %ls wire (", + e->m_id, + static_cast(etag) + ); + } + else if (bIsNonmanifoldEdge) + { + text_log.Print( + "e%u: (DAMAGED) %ls nonmanifold (", + e->m_id, + static_cast(etag) + ); + } + else + { + text_log.Print( + "e%u: (DAMAGED) %ls (", + e->m_id, + static_cast(etag) + ); + } + } + else + { + if (bIsWireEdge) + { + text_log.Print( + "e%u: wire %ls (", + e->m_id, + static_cast(etag) + ); + } + else if (bIsNonmanifoldEdge) + { + text_log.Print( + "e%u: nonmanifold %ls (", + e->m_id, + static_cast(etag) + ); + } + else + { + text_log.Print( + "e%u: %ls (", + e->m_id, + static_cast(etag) + ); + } + } prefix[0] = ON_String::Space; prefix[1] = error_code_point; @@ -5013,7 +5494,7 @@ unsigned int ON_SubDLevel::DumpTopology( } if (error_code_point == prefix[1]) edge_error_count++; - text_log.Print("%s%u", prefix, vid); + text_log.Print("%s%u", (0==evi)?(prefix+1):(prefix), vid); } text_log.Print(")\n"); @@ -5085,10 +5566,12 @@ unsigned int ON_SubDLevel::DumpTopology( // fEdges[face_edge_count] = { +eA, -eB, +eC, ...} // fVertices[face_edge_count] = { vP, vQ, vR, ... } // + damaged_face_count = 0U; face_count = 0; unsigned int face_dump_count = 0; ON_2udex skipped_face_id = ON_2udex::Zero; unsigned int max_face_id = 0; + bSkippedPreviousComponent = false; for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face) { if (face_count >= m_face_count && f->SubdivisionLevel() != level_index) @@ -5097,7 +5580,11 @@ unsigned int ON_SubDLevel::DumpTopology( max_face_id = f->m_id; face_count++; - if (bFaceIdTest) + const bool bIsDamaged = f->m_status.IsDamaged(); + if (bIsDamaged) + ++damaged_face_count; + + if (bFaceIdTest && (false == bIsDamaged || damaged_face_count > damaged_id_list_capacity)) { bool bSkip = true; for (;;) @@ -5128,6 +5615,7 @@ unsigned int ON_SubDLevel::DumpTopology( skipped_face_id.i = f->m_id; else if (f->m_id > skipped_face_id.j) skipped_face_id.j = f->m_id; + bSkippedPreviousComponent = true; continue; } } @@ -5137,10 +5625,25 @@ unsigned int ON_SubDLevel::DumpTopology( face_dump_count++; ON_TextLogIndent eindent(text_log); - text_log.Print( - "f%u:\n", - f->m_id - ); + if (bSkippedPreviousComponent) + { + text_log.Print(L"...\n"); + bSkippedPreviousComponent = false; + } + if (bIsDamaged) + { + text_log.Print( + "f%u (DAMAGED):\n", + f->m_id + ); + } + else + { + text_log.Print( + "f%u:\n", + f->m_id + ); + } text_log.PushIndent(); @@ -5683,10 +6186,10 @@ class ON_SubDEdge* ON_SubDimple::AddEdge( double v1_sector_weight ) { - if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v0_sector_weight,true) ) + if ( false == ON_SubDSectorType::IsValidSectorCoefficientValue(v0_sector_weight,true) ) return ON_SUBD_RETURN_ERROR(nullptr); - if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v1_sector_weight,true) ) + if ( false == ON_SubDSectorType::IsValidSectorCoefficientValue(v1_sector_weight,true) ) return ON_SUBD_RETURN_ERROR(nullptr); if ( nullptr != v0 && nullptr != v1 && v0->SubdivisionLevel() != v1->SubdivisionLevel() ) @@ -5695,25 +6198,25 @@ class ON_SubDEdge* ON_SubDimple::AddEdge( const bool bEdgeTagSet = ON_SubD::EdgeTagIsSet(edge_tag); if ( bEdgeTagSet - && ON_SubDSectorType::IgnoredSectorWeight != v0_sector_weight - && ON_SubDSectorType::UnsetSectorWeight != v0_sector_weight + && ON_SubDSectorType::IgnoredSectorCoefficient != v0_sector_weight + && ON_SubDSectorType::UnsetSectorCoefficient != v0_sector_weight && nullptr != v0 && ON_SubD::VertexTag::Smooth == v0->m_vertex_tag ) { // minimizes checking when building subds because constant crease weights can be passed in - v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + v0_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient; } if ( bEdgeTagSet - && ON_SubDSectorType::IgnoredSectorWeight != v1_sector_weight - && ON_SubDSectorType::UnsetSectorWeight != v1_sector_weight + && ON_SubDSectorType::IgnoredSectorCoefficient != v1_sector_weight + && ON_SubDSectorType::UnsetSectorCoefficient != v1_sector_weight && nullptr != v1 && ON_SubD::VertexTag::Smooth == v1->m_vertex_tag ) { // minimizes checking when building subds because constant crease weights can be passed in - v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + v1_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient; } class ON_SubDEdge* e = AllocateEdge(edge_tag); @@ -5890,9 +6393,9 @@ class ON_SubDEdge* ON_SubD::AddEdge( return AddEdgeWithSectorCoefficients( edge_tag, v0, - ON_SubDSectorType::UnsetSectorWeight, + ON_SubDSectorType::UnsetSectorCoefficient, v1, - ON_SubDSectorType::UnsetSectorWeight + ON_SubDSectorType::UnsetSectorCoefficient ); } @@ -6045,14 +6548,14 @@ bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCo ) return false; } - m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; - m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; + m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; 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(); + m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::Create( this, tagged_end_index).SectorCoefficient(); } else if (2 == tagged_end_index) { @@ -6063,8 +6566,8 @@ bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCo 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(); + m_sector_coefficient[0] = ON_SubDSectorType::Create( this, 0).SectorCoefficient(); + m_sector_coefficient[1] = ON_SubDSectorType::Create( this, 1).SectorCoefficient(); } } } @@ -6179,7 +6682,7 @@ class ON_SubDFace* ON_SubD::AddFace( } ON_SubDFace* face - = (eptr[edge_count - 1].IsNull() && (eptr[0].RelativeVertex(0) != eptr[edge_count - 1].RelativeVertex(1))) + = (eptr[edge_count - 1].IsNotNull() && eptr[0].RelativeVertex(0) == eptr[edge_count - 1].RelativeVertex(1)) ? AddFace(eptr, edge_count) : nullptr; onfree(eptr); @@ -6390,13 +6893,22 @@ bool ON_SubD::RemoveFaceConnections( static bool ON_SubDFace_GetSubdivisionPointError( const class ON_SubDFace* face, - double face_point[3], + double subdivision_point[3], bool bDamagedState ) { - if (nullptr == face || nullptr == face_point) - return ON_SUBD_RETURN_ERROR(false); // caller passed a null pointer - edge is not necessarily damaged + if (nullptr == subdivision_point) + return ON_SUBD_RETURN_ERROR(false); // caller passed a null pointer - face is not necessarily damaged + // make sure returned point is not used by a caller who doesn't bother to check return codes. + subdivision_point[0] = ON_DBL_QNAN; + subdivision_point[1] = ON_DBL_QNAN; + subdivision_point[2] = ON_DBL_QNAN; + + if (nullptr == face) + return ON_SUBD_RETURN_ERROR(false); + + // face is damaged in some way - mark it face->m_status.SetDamagedState(bDamagedState); return ON_SUBD_RETURN_ERROR(false); @@ -6452,12 +6964,16 @@ bool ON_SubDFace::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ // special treatment will run noticably faster. e_ptr = edge_ptr[0].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); + if ( nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] ) + return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); edir = ON_SUBD_EDGE_DIRECTION(e_ptr); vertexP[0] = e->m_vertex[edir]->m_P; vertexP[1] = e->m_vertex[1 - edir]->m_P; e_ptr = edge_ptr[2].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) + return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); edir = ON_SUBD_EDGE_DIRECTION(e_ptr); vertexP[2] = e->m_vertex[edir]->m_P; vertexP[3] = e->m_vertex[1 - edir]->m_P; @@ -6515,6 +7031,8 @@ bool ON_SubDFace::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ { e_ptr = edge_ptr[i].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) + return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); edir = ON_SUBD_EDGE_DIRECTION(e_ptr); vertexP[0] = e->m_vertex[edir]->m_P; vertexP[1] = e->m_vertex[1 - edir]->m_P; @@ -6533,6 +7051,8 @@ bool ON_SubDFace::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ // odd number of edges and vertices e_ptr = edge_ptr[count - 1].m_ptr; e = ON_SUBD_EDGE_POINTER(e_ptr); + if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1]) + return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true); edir = ON_SUBD_EDGE_DIRECTION(e_ptr); vertexP[0] = e->m_vertex[edir]->m_P; faceP[0] += vertexP[0][0]; @@ -6831,11 +7351,13 @@ void ON_SubDVertex::VertexModifiedNofification() const for (unsigned short vei = 0; vei < m_edge_count; vei++) { const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); - if (nullptr != edge) - { - edge->ClearSavedSubdivisionPoints(); - edge->UnsetSectorCoefficientsForExperts(); - } + if (nullptr == edge) + continue; + edge->ClearSavedSubdivisionPoints(); + edge->UnsetSectorCoefficientsForExperts(); + const ON_SubDVertex* v1 = edge->m_vertex[1-ON_SUBD_EDGE_DIRECTION(m_edges[vei].m_ptr)]; + if (nullptr != v1) + v1->ClearSavedSubdivisionPoints(); } // This is needed to clear cached information in the Catmull-Clark @@ -6856,7 +7378,6 @@ void ON_SubDEdge::EdgeModifiedNofification() const UnsetSectorCoefficientsForExperts(); for (unsigned int evi = 0; evi < 2; evi++) { - const_cast(this)->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorWeight; if (nullptr != m_vertex[evi]) m_vertex[evi]->VertexModifiedNofification(); } @@ -6882,8 +7403,8 @@ void ON_SubDEdge::EdgeModifiedNofification() 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; + const_cast(this)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; + const_cast(this)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; } void ON_SubDVertex::UnsetSectorCoefficientsForExperts(unsigned int relative_edge_end_dex) const @@ -6899,11 +7420,11 @@ void ON_SubDVertex::UnsetSectorCoefficientsForExperts(unsigned int relative_edge ? ((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; + e->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorCoefficient; else { - e->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; - e->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; + e->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; + e->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; } } } @@ -6949,7 +7470,6 @@ void ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags() { 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 @@ -6967,20 +7487,9 @@ bool ON_SubDComponentBase::Internal_SurfacePointFlag() const return (0 != ON_SUBD_CACHE_LIMITLOC_FLAG(m_saved_points_flags)); } -bool ON_SubDComponentBase::Internal_ControlNetFragmentFlag() const -{ - return (0 != ON_SUBD_CACHE_CTRLNETFRAG_FLAG(m_saved_points_flags)); -} - 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 @@ -7074,12 +7583,30 @@ void ON_SubDComponentBase::Internal_SetSavedSurfacePointFlag(bool bSavedSurfaceP Internal_ClearSurfacePointFlag(); } -void ON_SubDComponentBase::Internal_SetSavedControlNetFragmentFlag(bool bSavedControlNetFragmentFlag) const + +void ON_SubDComponentBase::Internal_SetModified1Flag() const { - if (bSavedControlNetFragmentFlag) - m_saved_points_flags |= ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT; - else - Internal_ClearSurfacePointFlag(); + m_saved_points_flags |= ON_SubDComponentBase::ModifiedFlags::Modified1Bit; +} + +void ON_SubDComponentBase::Internal_SetModified2Flag() const +{ + m_saved_points_flags |= ON_SubDComponentBase::ModifiedFlags::Modified1Bit; +} + +void ON_SubDComponentBase::Internal_ClearModifiedFlags() const +{ + m_saved_points_flags &= ~ON_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask; +} + +bool ON_SubDComponentBase::Internal_Modified1IsSet() const +{ + return (0 != (m_saved_points_flags & ON_SubDComponentBase::ModifiedFlags::Modified1Bit)); +} + +bool ON_SubDComponentBase::Internal_Modified1or2IsSet() const +{ + return (0 != (m_saved_points_flags & ON_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask)); } bool ON_SubDFace::ReverseEdgeList() @@ -7335,6 +7862,7 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV); const double* edgeP[2] = { edge_vertex[0]->m_P, edge_vertex[1]->m_P }; + const double edgePsum[3] = { edgeP[0][0] + edgeP[1][0], edgeP[0][1] + edgeP[1][1], edgeP[0][2] + edgeP[1][2] }; if ( IsSmooth() ) { @@ -7361,7 +7889,7 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ = (ON_SubD::VertexTag::Smooth != edge_vertex[0]->m_vertex_tag) ? 0 : ((ON_SubD::VertexTag::Smooth != edge_vertex[1]->m_vertex_tag) ? 1 : ON_UNSET_UINT_INDEX); - double edgePsum[3]; + double EP[3]; if ( ON_UNSET_UINT_INDEX == tagged_end || 0.5 == m_sector_coefficient[tagged_end] @@ -7369,9 +7897,9 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ ) { // ignore edge weights - edgePsum[0] = 0.375*(edgeP[0][0] + edgeP[1][0]); - edgePsum[1] = 0.375*(edgeP[0][1] + edgeP[1][1]); - edgePsum[2] = 0.375*(edgeP[0][2] + edgeP[1][2]); + EP[0] = 0.375*edgePsum[0]; + EP[1] = 0.375*edgePsum[1]; + EP[2] = 0.375*edgePsum[2]; } else if (ON_SubD::VertexTag::Smooth == edge_vertex[1 - tagged_end]->m_vertex_tag && m_sector_coefficient[tagged_end] > 0.0 @@ -7381,9 +7909,9 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ double w[2]; w[tagged_end] = m_sector_coefficient[tagged_end]; w[1 - tagged_end] = 1.0 - w[tagged_end]; - edgePsum[0] = 0.75*(w[0] * edgeP[0][0] + w[1] * edgeP[1][0]); - edgePsum[1] = 0.75*(w[0] * edgeP[0][1] + w[1] * edgeP[1][1]); - edgePsum[2] = 0.75*(w[0] * edgeP[0][2] + w[1] * edgeP[1][2]); + EP[0] = 0.75*(w[0] * edgeP[0][0] + w[1] * edgeP[1][0]); + EP[1] = 0.75*(w[0] * edgeP[0][1] + w[1] * edgeP[1][1]); + EP[2] = 0.75*(w[0] * edgeP[0][2] + w[1] * edgeP[1][2]); } else { @@ -7397,9 +7925,9 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ if (4 == face_edge_count[0] && 4 == face_edge_count[1]) { // common case when both neighboring faces are quads - subdivision_point[0] = edgePsum[0] + 0.0625*(facePsum[0][0] + facePsum[1][0]); - subdivision_point[1] = edgePsum[1] + 0.0625*(facePsum[0][1] + facePsum[1][1]); - subdivision_point[2] = edgePsum[2] + 0.0625*(facePsum[0][2] + facePsum[1][2]); + subdivision_point[0] = EP[0] + 0.0625*(facePsum[0][0] + facePsum[1][0]); + subdivision_point[1] = EP[1] + 0.0625*(facePsum[0][1] + facePsum[1][1]); + subdivision_point[2] = EP[2] + 0.0625*(facePsum[0][2] + facePsum[1][2]); if (bApplyDisplacement) { @@ -7414,9 +7942,14 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ if (3 == face_edge_count[0] && 3 == face_edge_count[1]) { // common case when both neighboring faces are triangles - subdivision_point[0] = edgePsum[0] + 0.125*(facePsum[0][0] + facePsum[1][0]); - subdivision_point[1] = edgePsum[1] + 0.125*(facePsum[0][1] + facePsum[1][1]); - subdivision_point[2] = edgePsum[2] + 0.125*(facePsum[0][2] + facePsum[1][2]); + + //// bug in evaluation prior to Nov 11, 2019 + ////subdivision_point[0] = EP[0] + 0.125*(facePsum[0][0] + facePsum[1][0]); + ////subdivision_point[1] = EP[1] + 0.125*(facePsum[0][1] + facePsum[1][1]); + ////subdivision_point[2] = EP[2] + 0.125*(facePsum[0][2] + facePsum[1][2]); + subdivision_point[0] = EP[0] + (0.5*edgePsum[0] + facePsum[0][0] + facePsum[1][0]) / 12.0; + subdivision_point[1] = EP[1] + (0.5*edgePsum[1] + facePsum[0][1] + facePsum[1][1]) / 12.0; + subdivision_point[2] = EP[2] + (0.5*edgePsum[2] + facePsum[0][2] + facePsum[1][2]) / 12.0; if (bApplyDisplacement) { @@ -7429,11 +7962,19 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ } // general formula works for all cases including face_edge_count[0] != face_count[2] - const double f0 = 0.125 / ((double)(face_edge_count[0]-2)); - const double f1 = 0.125 / ((double)(face_edge_count[1]-2)); - subdivision_point[0] = edgePsum[0] + f0 * facePsum[0][0] + f1 * facePsum[1][0]; - subdivision_point[1] = edgePsum[1] + f0 * facePsum[0][1] + f1 * facePsum[1][1]; - subdivision_point[2] = edgePsum[2] + f0 * facePsum[0][2] + f1 * facePsum[1][2]; + //// bug in evaluation prior to Nov 11, 2019 + ////const double f0 = 0.125 / ((double)(face_edge_count[0] - 2)); + ////const double f1 = 0.125 / ((double)(face_edge_count[1] - 2)); + ////subdivision_point[0] = EP[0] + f0 * facePsum[0][0] + f1 * facePsum[1][0]; + ////subdivision_point[1] = EP[1] + f0 * facePsum[0][1] + f1 * facePsum[1][1]; + ////subdivision_point[2] = EP[2] + f0 * facePsum[0][2] + f1 * facePsum[1][2]; + + const double f0 = (double)(face_edge_count[0] * 4U); + const double f1 = (double)(face_edge_count[1] * 4U); + const double x = 1.0 / f0 + 1.0 / f1 - 0.125; + subdivision_point[0] = EP[0] + x * edgePsum[0] + facePsum[0][0] / f0 + facePsum[1][0] / f1; + subdivision_point[1] = EP[1] + x * edgePsum[1] + facePsum[0][1] / f0 + facePsum[1][1] / f1; + subdivision_point[2] = EP[2] + x * edgePsum[2] + facePsum[0][2] / f0 + facePsum[1][2] / f1; if (bApplyDisplacement) { @@ -7447,9 +7988,9 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ if ( IsCrease() ) { - subdivision_point[0] = 0.5*(edgeP[0][0] + edgeP[1][0]); - subdivision_point[1] = 0.5*(edgeP[0][1] + edgeP[1][1]); - subdivision_point[2] = 0.5*(edgeP[0][2] + edgeP[1][2]); + subdivision_point[0] = 0.5*edgePsum[0]; + subdivision_point[1] = 0.5*edgePsum[1]; + subdivision_point[2] = 0.5*edgePsum[2]; if (bApplyDisplacement) { @@ -7676,22 +8217,22 @@ bool ON_SubDSectorSurfacePoint::IsSet( return false; } - if (false == bUndefinedNormalIsPossible) + p = m_limitT1; + p1 = p + 6; + while (p < p1) { - 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 (false == bUndefinedNormalIsPossible) { - 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; } @@ -7706,8 +8247,11 @@ bool ON_SubDSectorSurfacePoint::IsSet( return false; y += x * x; } - if (!(fabs(y - 1.0) <= 1e-4)) - return false; + if (false == bUndefinedNormalIsPossible) + { + if (!(fabs(y - 1.0) <= 1e-4)) + return false; + } } return true; @@ -8455,7 +8999,7 @@ bool ON_SubDimple::LocalSubdivide( 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); + ON_SubDEdge* r = AddEdge(ON_SubD::EdgeTag::Smooth, center, ON_SubDSectorType::UnsetSectorCoefficient, const_cast(eptrs[fei].RelativeVertex(1)), ON_SubDSectorType::UnsetSectorCoefficient); radial_edges.Append(r); } @@ -8645,7 +9189,7 @@ unsigned int ON_SubDimple::Internal_GlobalQuadSubdivideFace( unsigned int f1_count = 0; - const double w_2facesector = ON_SubDSectorType::CreaseSectorWeight(2); + const double w_2facesector = ON_SubDSectorType::CreaseSectorCoefficient(2); for (unsigned int i = 0; i < f0_edge_count; i++) { @@ -8808,12 +9352,12 @@ bool ON_SubDimple::GlobalSubdivide( return true; } -ON_SubDEdgePtr ON_SubDimple::MergeEdges( +ON_SubDEdgePtr ON_SubDimple::MergeConsecutiveEdges( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ) { - if ( false == ON_SubD::EdgesCanBeMerged(eptr0,eptr1) ) + if ( false == ON_SubD::EdgesAreConsecutive(eptr0,eptr1) ) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); ON_SubDEdge* e[2] = { ON_SUBD_EDGE_POINTER(eptr0.m_ptr), ON_SUBD_EDGE_POINTER(eptr1.m_ptr) }; @@ -8887,19 +9431,19 @@ ON_SubDEdgePtr ON_SubDimple::MergeEdges( ? ON_SubD::EdgeTag::SmoothX : ON_SubD::EdgeTag::Smooth; if ( false == bTagged[0]) - e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; else if (!(e[0]->m_sector_coefficient[0] > 0.0 && e[0]->m_sector_coefficient[0] < 1.0)) - e[0]->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; + e[0]->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; if ( false == bTagged[1]) - e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; else if (!(e[0]->m_sector_coefficient[1] > 0.0 && e[0]->m_sector_coefficient[1] < 1.0)) - e[0]->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; + e[0]->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; } else { e[0]->m_edge_tag = ON_SubD::EdgeTag::Crease; - e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; - e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; + e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; } ReturnEdge(e[1]); @@ -8907,16 +9451,16 @@ ON_SubDEdgePtr ON_SubDimple::MergeEdges( return eptr0; } -ON_SubDEdgePtr ON_SubD::MergeEdges( +ON_SubDEdgePtr ON_SubD::MergeConsecutiveEdges( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ) { ON_SubDimple* subdimple = SubDimple(false); - return (nullptr != subdimple) ? subdimple->MergeEdges(eptr0, eptr1) : ON_SubDEdgePtr::Null; + return (nullptr != subdimple) ? subdimple->MergeConsecutiveEdges(eptr0, eptr1) : ON_SubDEdgePtr::Null; } -static bool EdgesAreMergableTest( +static bool Internal_EdgesAreConsecutive( const ON_SubDEdgePtr eptr[2], bool bTestColinearity, double distance_tolerance, @@ -9051,13 +9595,13 @@ static bool EdgesAreMergableTest( return true; } -bool ON_SubD::EdgesCanBeMerged( +bool ON_SubD::EdgesAreConsecutive( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ) { ON_SubDEdgePtr eptr[2] = { eptr0,eptr1 }; - return EdgesAreMergableTest(eptr,false,ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN); + return Internal_EdgesAreConsecutive(eptr,false,ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN); } static bool Internal_EdgesPassTypeFilter( @@ -9115,7 +9659,7 @@ unsigned int ON_SubDimple::MergeColinearEdges( { eptr[0] = eptr[1]; eptr[1] = f->EdgePtr(fei0); - if (false == EdgesAreMergableTest(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance)) + if (false == Internal_EdgesAreConsecutive(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance)) break; if (false == Internal_EdgesPassTypeFilter(eptr, bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges)) break; @@ -9143,14 +9687,14 @@ unsigned int ON_SubDimple::MergeColinearEdges( eptr[0] = eptr[1]; eptr[1] = f->EdgePtr(fei); if ( - EdgesAreMergableTest(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance) + Internal_EdgesAreConsecutive(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance) && Internal_EdgesPassTypeFilter(eptr, bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges) ) { // 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 (eptr[0].m_ptr != MergeConsecutiveEdges(eptr[0], eptr[1]).m_ptr) { - ON_SUBD_ERROR("Bug in edge merging."); + ON_SUBD_ERROR("Bug in consecutive edge merging."); break; } ++removed_edge_count; @@ -9319,6 +9863,13 @@ const class ON_SubDVertex* ON_SubD::VertexFromId( return nullptr; } +const class ON_SubDVertex* ON_SubD::VertexFromComponentIndex( + ON_COMPONENT_INDEX component_index +) const +{ + return (ON_COMPONENT_INDEX::TYPE::subd_vertex == component_index.m_type) ? VertexFromId(component_index.m_index) : nullptr; +} + const ON_COMPONENT_INDEX ON_SubDVertex::ComponentIndex() const { return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_vertex,m_id); @@ -9380,6 +9931,13 @@ const class ON_SubDEdge* ON_SubD::EdgeFromId( return nullptr; } +const class ON_SubDEdge* ON_SubD::EdgeFromComponentIndex( + ON_COMPONENT_INDEX component_index +) const +{ + return (ON_COMPONENT_INDEX::TYPE::subd_edge == component_index.m_type) ? EdgeFromId(component_index.m_index) : nullptr; +} + const ON_COMPONENT_INDEX ON_SubDEdge::ComponentIndex() const { return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_edge,m_id); @@ -9442,6 +10000,12 @@ const class ON_SubDFace* ON_SubD::FaceFromId( return nullptr; } +const class ON_SubDFace* ON_SubD::FaceFromComponentIndex( + ON_COMPONENT_INDEX component_index +) const +{ + return (ON_COMPONENT_INDEX::TYPE::subd_face == component_index.m_type) ? FaceFromId(component_index.m_index) : nullptr; +} const ON_COMPONENT_INDEX ON_SubDFace::ComponentIndex() const { @@ -9904,8 +10468,8 @@ const ON_SubDEdge* ON_SubDimple::SplitEdge( // Either edge was a crease, new_edge is a crease, and sector weights do not applie // or edge was X or Smooth, edge is smooth, new_edge is smooth, new_vertex is smooth, // and the sector weights at this vertex do not apply. - edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; - new_edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; + edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; + new_edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; AddVertexToLevel(new_vertex); AddEdgeToLevel(new_edge); @@ -10024,8 +10588,8 @@ const ON_SubDEdge* ON_SubDimple::SplitFace( 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); + v[0], ON_SubDSectorType::UnsetSectorCoefficient, + v[1], ON_SubDSectorType::UnsetSectorCoefficient); if (nullptr == new_e) break; @@ -10519,7 +11083,7 @@ void ON_SubDLevel::ClearEvaluationCache() const 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(); + const_cast(edge)->m_sector_coefficient[evi] = ON_SubDSectorType::Create(edge, evi).SectorCoefficient(); } } } @@ -10530,6 +11094,85 @@ void ON_SubDLevel::ClearEvaluationCache() const } } +void ON_SubDLevel::ClearNeighborhoodEvaluationCache(const ON_SubDVertex * vertex0, bool bTagChanged ) const +{ + ClearEdgeFlags(); + ClearBoundingBox(); + m_surface_mesh = ON_SubDMesh::Empty; + m_control_net_mesh = ON_SubDMesh::Empty; + + if (nullptr == vertex0) + return; + + vertex0->ClearSavedSubdivisionPoints(); + + for (unsigned short v0ei = 0; v0ei < vertex0->m_edge_count; ++v0ei) + { + const ON_SubDEdge* edge0 = ON_SUBD_EDGE_POINTER(vertex0->m_edges[v0ei].m_ptr); + if (nullptr == edge0) + continue; + edge0->ClearSavedSubdivisionPoints(); + if (bTagChanged) + edge0->UnsetSectorCoefficientsForExperts(); + else if (edge0->IsSmooth()) + { + for (unsigned evi = 0; evi < 2; evi++) + { + if (false == (edge0->m_sector_coefficient[evi] > 0.0 && edge0->m_sector_coefficient[evi] < 1.0)) + continue; + const ON_SubDVertex* v = edge0->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. + edge0->UnsetSectorCoefficientsForExperts(); + } + } + } + + for (unsigned short v0fi = 0; v0fi < vertex0->m_face_count; ++v0fi) + { + const ON_SubDFace* face0 = vertex0->m_faces[v0fi]; + if (nullptr == face0) + continue; + face0->ClearSavedSubdivisionPoints(); + const ON_SubDEdgePtr* face0_eptr = face0->m_edge4; + for (unsigned short f0ei = 0; f0ei < face0->m_edge_count; ++f0ei, ++face0_eptr) + { + if (4 == f0ei) + { + face0_eptr = face0->m_edgex; + if (nullptr == face0_eptr) + break; + } + const ON_SubDEdge* edge0 = ON_SUBD_EDGE_POINTER(face0_eptr->m_ptr); + if (nullptr == edge0) + continue; + edge0->ClearSavedSubdivisionPoints(); + const ON_SubDVertex* vertex1 = edge0->m_vertex[ON_SUBD_EDGE_DIRECTION(face0_eptr->m_ptr)]; + if (vertex0 == vertex1 || nullptr == vertex1) + continue; + vertex1->ClearSavedSubdivisionPoints(); + for (unsigned short v1fi = 0; v1fi < vertex1->m_face_count; ++v1fi) + { + const ON_SubDFace* face1 = vertex1->m_faces[v1fi]; + if (nullptr == face1 || face0 == face1) + continue; + face1->ClearSavedSubdivisionPoints(); + } + for (unsigned short v1ei = 0; v1ei < vertex1->m_edge_count; ++v1ei) + { + const ON_SubDEdge* edge1 = ON_SUBD_EDGE_POINTER(vertex1->m_edges[v1ei].m_ptr); + if (nullptr == edge1) + continue; + edge1->ClearSavedSubdivisionPoints(); + } + } + } +} + unsigned int ON_SubD::ComponentPtrFromComponentIndex( const ON_COMPONENT_INDEX* ci_list, size_t ci_count, @@ -10822,20 +11465,20 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( if (2 != edge->m_face_count) { edge->m_edge_tag = ON_SubD::EdgeTag::Crease; - edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; - edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; + edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; } else { - edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight; - edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight; + edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; + edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; const bool bBothVertexTagsAreSet = ON_SubD::VertexTag::Unset != edge->m_vertex[0]->m_vertex_tag && ON_SubD::VertexTag::Unset != edge->m_vertex[1]->m_vertex_tag ; const unsigned int tagged_end_index = edge->TaggedEndIndex(); if (0 == tagged_end_index || 1 == tagged_end_index) - edge->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::IgnoredSectorWeight; + edge->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::IgnoredSectorCoefficient; switch (edge_tag0) { @@ -10849,8 +11492,8 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( edge->m_edge_tag = ON_SubD::EdgeTag::Smooth; if (3 == tagged_end_index) { - edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; - edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; + edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; } } break; @@ -10862,14 +11505,14 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( } else if (3 == tagged_end_index && bBothVertexTagsAreSet) { - edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; - edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; + edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; } break; case ON_SubD::EdgeTag::Crease: - edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight; - edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight; + edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient; + edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; break; //case ON_SubD::EdgeTag::Sharp: @@ -11203,11 +11846,13 @@ unsigned int ON_SubDimple::DeleteComponents( deleted_component_count++; } + ON_ComponentStatus allsetcheck; ON_SubDEdge* next_edge = level->m_edge[0]; 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 || (bDeleteIsolatedEdges && 0 == edge->m_face_count) ); + allsetcheck = ON_ComponentStatus::LogicalAnd(ON_ComponentStatus::AllSet, edge->m_status); + bool bDelete = (ON_ComponentStatus::AllSet == allsetcheck || (bDeleteIsolatedEdges && 0 == edge->m_face_count) ); if (false == bDelete) { for (unsigned short evi = 0; evi < 2 && false == bDelete; evi++) @@ -11232,7 +11877,8 @@ 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 || (bDeleteIsolatedEdges && 0 == vertex->m_face_count) || 0 == vertex->m_edge_count ); + allsetcheck = ON_ComponentStatus::LogicalAnd(ON_ComponentStatus::AllSet, vertex->m_status); + bool bDelete = (ON_ComponentStatus::AllSet == allsetcheck || (bDeleteIsolatedEdges && 0 == vertex->m_face_count) || 0 == vertex->m_edge_count ); if ( false == bDelete ) continue; @@ -11282,8 +11928,8 @@ unsigned int ON_SubDimple::DeleteComponents( 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::UnsetSectorCoefficient; + edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; } } @@ -11313,13 +11959,6 @@ unsigned int ON_SubDimple::DeleteComponents( vertex->m_edges[vertex->m_edge_count++] = vertex->m_edges[vei]; } - if ( crease_count > 2 ) - vertex->m_vertex_tag = ON_SubD::VertexTag::Corner; - else if (false == bInteriorVertex || crease_count > 1) - { - if (false == vertex->IsCreaseOrCorner()) - vertex->m_vertex_tag = ON_SubD::VertexTag::Crease; - } count = vertex->m_face_count; vertex->m_face_count = 0; @@ -11337,6 +11976,18 @@ unsigned int ON_SubDimple::DeleteComponents( m_heap.ReturnVertex(vertex); deleted_component_count++; } + else + { + if (1 == crease_count && 1 == vertex->m_edge_count && 0 == vertex->m_face_count) + vertex->m_vertex_tag = ON_SubD::VertexTag::Corner; + else if (crease_count > 2) + vertex->m_vertex_tag = ON_SubD::VertexTag::Corner; + else if (false == bInteriorVertex || crease_count > 1) + { + if (false == vertex->IsCreaseOrCorner()) + vertex->m_vertex_tag = ON_SubD::VertexTag::Crease; + } + } } if (0 == level->m_vertex_count || 0 == level->m_edge_count || (bDeleteIsolatedEdges && 0 == level->m_face_count)) @@ -11671,10 +12322,10 @@ unsigned int ON_SubD::SetComponentStatus( } ON_SubDComponentMarksClearAndRestore::ON_SubDComponentMarksClearAndRestore( - ON_SubD& subd + const ON_SubD& subd ) { - m_subd.ShareContentsFrom(subd); + m_subd.ShareDimple(subd); m_subd.ClearComponentMarks(true, true, true, &m_component_list); } @@ -13811,15 +14462,26 @@ const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor( if (bIsSmooth != (bIsCrease?false:true)) break; - if (ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type) + const unsigned short vertex_edge_count + = ((ON_SubD::ChainType::EqualEdgeTagAndOrdinary == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTagAndOrdinary == chain_type) + && (1 == edge->m_face_count || 2 == edge->m_face_count)) + ? (edge->m_face_count + 2) + : ((unsigned short)0); + + if (vertex_edge_count > 0 && vertex_edge_count != v->m_edge_count) + break; + + if (ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTagAndOrdinary == chain_type) { if (bIsSmooth) { + // edge is smooth so vertex must be smooth if (ON_SubD::VertexTag::Smooth != v->m_vertex_tag) break; } else { + // edge is crease so vertex must be crease if (ON_SubD::VertexTag::Crease != v->m_vertex_tag) break; } @@ -13892,7 +14554,13 @@ const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor( { if (bIsSmooth != nxt->IsSmooth()) { - if ( ON_SubD::ChainType::EqualEdgeTag == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type) + // edge tag changed + if ( + ON_SubD::ChainType::EqualEdgeTag == chain_type + || ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type + || ON_SubD::ChainType::EqualEdgeTagAndOrdinary == chain_type + || ON_SubD::ChainType::EqualEdgeAndVertexTagAndOrdinary == chain_type + ) break; } if (false == bEnableStatusCheck || ON_ComponentStatus::StatusCheck(nxt->m_status, status_pass, status_fail)) @@ -14897,6 +15565,37 @@ void ON_SubDEdgePtrLink::Resolve3OrMoreEdges( return; } +unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( + const ON_SubD& subd, + const ON_SimpleArray< ON_COMPONENT_INDEX >& unsorted_edges, + unsigned int minimum_chain_length, + ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges +) +{ + const unsigned count = unsorted_edges.UnsignedCount(); + ON_SimpleArray< const ON_SubDEdge* > a(count); + for (unsigned i = 0; i < count; ++i) + { + const ON_SubDEdge* e = subd.EdgeFromComponentIndex(unsorted_edges[i]); + if (nullptr != e) + a.Append(e); + } + return SortEdgesIntoEdgeChains(a, minimum_chain_length, sorted_edges); +} + +unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( + const ON_SimpleArray< ON_SubDComponentPtr >& unsorted_edges, + unsigned int minimum_chain_length, + ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges +) +{ + const unsigned count = unsorted_edges.UnsignedCount(); + ON_SimpleArray< const ON_SubDEdge* > a(count); + for (unsigned i = 0; i < count; ++i) + a.Append(unsorted_edges[i].Edge()); + return ON_SubDEdgeChain::SortEdgesIntoEdgeChains(a, minimum_chain_length, sorted_edges); +} + unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( const ON_SimpleArray< const ON_SubDEdge* >& unsorted_edges, unsigned int minimum_chain_length, @@ -15190,6 +15889,866 @@ unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains( return chain_count; } +unsigned int ON_SubDEdgeChain::OrientEdgesIntoEdgeChains( + const ON_SubD& subd, + const ON_SimpleArray< ON_COMPONENT_INDEX >& edges, + ON_SimpleArray< ON_SubDEdgePtr >& edge_chain +) +{ + const unsigned count = edges.UnsignedCount(); + ON_SimpleArray< const ON_SubDEdge* > a(count); + for (unsigned i = 0; i < count; ++i) + a.Append(subd.EdgeFromComponentIndex(edges[i])); + return ON_SubDEdgeChain::OrientEdgesIntoEdgeChains(a, edge_chain); +} + +unsigned int ON_SubDEdgeChain::OrientEdgesIntoEdgeChains( + const ON_SimpleArray< ON_SubDComponentPtr >& edges, + ON_SimpleArray< ON_SubDEdgePtr >& edge_chain +) +{ + const unsigned count = edges.UnsignedCount(); + ON_SimpleArray< const ON_SubDEdge* > a(count); + for (unsigned i = 0; i < count; ++i) + a.Append(edges[i].Edge()); + return ON_SubDEdgeChain::OrientEdgesIntoEdgeChains(a, edge_chain); +} + +unsigned int ON_SubDEdgeChain::OrientEdgesIntoEdgeChains( + const ON_SimpleArray< const ON_SubDEdge* >& edges, + ON_SimpleArray< ON_SubDEdgePtr >& edge_chains +) +{ + const unsigned count = edges.UnsignedCount(); + edge_chains.SetCount(0); + edge_chains.Reserve(count); + unsigned int chain_count = 0; + unsigned chain_length = 0; + ON_SubDEdgePtr* prev_eptr = nullptr; + for (unsigned i = 0; i < count; ++i) + { + const ON_SubDEdge* e = 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 = edge_chains.AppendNew(); + eptr = ON_SubDEdgePtr::Create(e); + if (nullptr != prev_eptr && prev_eptr->RelativeVertex(1) != eptr.RelativeVertex(0) ) + { + const ON_SubDVertex* prev_v[2] = { prev_eptr->RelativeVertex(0), prev_eptr->RelativeVertex(1) }; + const ON_SubDVertex* v[2] = { eptr.RelativeVertex(0), eptr.RelativeVertex(1) }; + if (prev_v[1] == v[1]) + eptr = eptr.Reversed(); + else if (1 == chain_length) + { + if (prev_v[0] == v[0]) + *prev_eptr = prev_eptr->Reversed(); + else if (prev_v[0] == v[1]) + { + *prev_eptr = prev_eptr->Reversed(); + eptr = eptr.Reversed(); + } + else + prev_eptr = nullptr; + } + else + prev_eptr = nullptr; + } + + if (nullptr == prev_eptr) + { + chain_count = 1; + chain_length = 0; + } + prev_eptr = &eptr; + ++chain_length; + } + return chain_count; +} + + + +ON_SubDComponentList::ON_SubDComponentList(const ON_SubDComponentList& src) + : m_subd_runtime_serial_number(src.m_subd_runtime_serial_number) + , m_subd_content_serial_number(src.m_subd_content_serial_number) + , m_subd_vertex_count(src.m_subd_vertex_count) + , m_subd_edge_count(src.m_subd_edge_count) + , m_subd_face_count(src.m_subd_face_count) + , m_component_list(src.m_component_list) +{ + m_subd.ShareDimple(src.m_subd); +} + +ON_SubDComponentList& ON_SubDComponentList::operator=(const ON_SubDComponentList& src) +{ + if (this != &src) + { + m_subd_runtime_serial_number = src.m_subd_runtime_serial_number; + m_subd_content_serial_number = src.m_subd_content_serial_number; + m_subd_vertex_count = src.m_subd_vertex_count; + m_subd_edge_count = src.m_subd_edge_count; + m_subd_face_count = src.m_subd_face_count; + m_component_list = src.m_component_list; + m_subd.ShareDimple(src.m_subd); + } + return *this; +} + +ON__UINT64 ON_SubDComponentList::SubDRuntimeSerialNumber() const +{ + return m_subd_runtime_serial_number; +} + +ON__UINT64 ON_SubDComponentList::SubDContentSerialNumber() const +{ + return m_subd_content_serial_number; +} + +unsigned int ON_SubDComponentList::Count() const +{ + return m_component_list.UnsignedCount(); +} + +const ON_SubDComponentPtr ON_SubDComponentList::operator[](int i) const +{ + return i >= 0 && i < m_component_list.Count() ? m_component_list[i] : ON_SubDComponentPtr::Null; +} + +const ON_SubDComponentPtr ON_SubDComponentList::operator[](unsigned int i) const +{ + return i < m_component_list.UnsignedCount() ? m_component_list[i] : ON_SubDComponentPtr::Null; +} + +const ON_SubDComponentPtr ON_SubDComponentList::operator[](ON__INT64 i) const +{ + return i >= 0 && i < ((ON__INT64)m_component_list.Count()) ? m_component_list[i] : ON_SubDComponentPtr::Null; +} + +const ON_SubDComponentPtr ON_SubDComponentList::operator[](ON__UINT64 i) const +{ + return i < ((ON__UINT64)m_component_list.UnsignedCount()) ? m_component_list[i] : ON_SubDComponentPtr::Null; +} + +#if defined(ON_RUNTIME_APPLE) +const ON_SubDComponentPtr ON_SubDComponentList::operator[](size_t i) const +{ + return i >= 0 && i < m_component_list.Count() ? m_component_list[i] : ON_SubDComponentPtr::Null; +} +#endif + +const ON_SimpleArray< ON_SubDComponentPtr >& ON_SubDComponentList::ComponentList() const +{ + return this->m_component_list; +} + +const ON_SubD& ON_SubDComponentList::SubD() const +{ + return m_subd; +} + +ON__UINT64 ON_SubDComponentList::UpdateContentSerialNumber() +{ + m_subd_content_serial_number = m_subd.ContentSerialNumber(); + return m_subd_content_serial_number; +} + +unsigned int ON_SubDComponentList::UpdateSubDForExperts(const ON_SubD & subd, bool bUpdateDeletedComponents) +{ + const unsigned count0 = Count(); + if (subd.RuntimeSerialNumber() == m_subd.RuntimeSerialNumber()) + return count0; // the components in this list are in subd. + + // Use the component ids to update the list to reference components in subd. + unsigned count1 = 0; + for (unsigned i = 0; i < count0; ++i) + { + ON_SubDComponentPtr cptr0 = m_component_list[i]; + const ON_SubDComponentBase* c0 = cptr0.ComponentBase(); + if (nullptr == c0) + continue; + if (false == bUpdateDeletedComponents && false == c0->IsActive()) + continue; + ON_COMPONENT_INDEX ci = cptr0.ComponentIndex(); + if (0 == ci.m_index) + continue; + ON_SubDComponentPtr cptr1 = subd.ComponentPtrFromComponentIndex(ci); + if (cptr1.IsNull()) + continue; + if (0 != cptr0.ComponentDirection()) + cptr1.SetComponentDirection(); + m_component_list[count1++] = cptr1; + } + m_component_list.SetCount(count1); + m_subd.ShareDimple(subd); + m_subd_runtime_serial_number = m_subd.RuntimeSerialNumber(); + m_subd_content_serial_number = m_subd.ContentSerialNumber(); + return Count(); +} + +unsigned ON_SubDComponentList::CreateFromComponentList(const ON_SubD& subd, const ON_SimpleArray& component_list) +{ + ON_SubDComponentMarksClearAndRestore saved_marks(subd); + const unsigned count = component_list.UnsignedCount(); + unsigned marked_count = 0; + for (unsigned i = 0; i < count; ++i) + { + const ON_COMPONENT_INDEX ci = component_list[i]; + if (ON_COMPONENT_INDEX::TYPE::subd_vertex != ci.m_type) + continue; + const unsigned vertex_id = (unsigned)ci.m_index; + const ON_SubDVertex* v = subd.VertexFromId(vertex_id); + if (nullptr == v) + continue; + if (v->m_status.RuntimeMark()) + continue; + v->m_status.SetRuntimeMark(); + ++marked_count; + } + return Internal_Create(subd, true, true, true, true, marked_count); +} + +unsigned ON_SubDComponentList::CreateFromComponentList(const ON_SubD& subd, const ON_SimpleArray& component_list) +{ + ON_SubDComponentMarksClearAndRestore saved_marks(subd); + const unsigned count = component_list.UnsignedCount(); + unsigned marked_count = 0; + for (unsigned i = 0; i < count; ++i) + { + const ON_SubDComponentBase* c = component_list[i].ComponentBase(); + if (nullptr == c) + continue; + if (c->m_status.RuntimeMark()) + continue; + c->m_status.SetRuntimeMark(); + ++marked_count; + } + return Internal_Create(subd, true, true, true, true, marked_count); +} + +unsigned ON_SubDComponentList::CreateFromVertexIdList(const ON_SubD& subd, const ON_SimpleArray& vertex_id_list) +{ + ON_SubDComponentMarksClearAndRestore saved_marks(subd); + const unsigned count = vertex_id_list.UnsignedCount(); + unsigned marked_count = 0; + for (unsigned i = 0; i < count; ++i) + { + const unsigned vertex_id = vertex_id_list[i]; + if (vertex_id <= 0 || vertex_id >= ON_UNSET_UINT_INDEX) + continue; + const ON_SubDVertex* v = subd.VertexFromId(vertex_id); + if (nullptr == v) + continue; + if (v->m_status.RuntimeMark()) + continue; + v->m_status.SetRuntimeMark(); + ++marked_count; + } + return Internal_Create(subd, true, false, false, true, marked_count); +} + + +unsigned ON_SubDComponentList::CreateFromVertexList(const ON_SubD& subd, const ON_SimpleArray& vertex_list) +{ + ON_SubDComponentMarksClearAndRestore saved_marks(subd); + const unsigned count = vertex_list.UnsignedCount(); + unsigned marked_count = 0; + for (unsigned i = 0; i < count; ++i) + { + const ON_SubDVertex* v = vertex_list[i].Vertex(); + if (nullptr == v) + continue; + if (v->m_status.RuntimeMark()) + continue; + v->m_status.SetRuntimeMark(); + ++marked_count; + } + return Internal_Create(subd, true, false, false, true, marked_count); +} + +unsigned ON_SubDComponentList::CreateFromVertexList(const ON_SubD& subd, const ON_SimpleArray& vertex_list) +{ + ON_SubDComponentMarksClearAndRestore saved_marks(subd); + const unsigned count = vertex_list.UnsignedCount(); + unsigned marked_count = 0; + for (unsigned i = 0; i < count; ++i) + { + const ON_SubDVertex* v = vertex_list[i]; + if (nullptr == v) + continue; + if (v->m_status.RuntimeMark()) + continue; + v->m_status.SetRuntimeMark(); + ++marked_count; + } + return Internal_Create(subd, true, false, false, true, marked_count); +} + +unsigned ON_SubDComponentList::CreateFromMarkedComponents(const ON_SubD& subd, bool bComponentInListMark) +{ + unsigned marked_count = 0; + ON_SubDComponentIterator cit(subd); + if (bComponentInListMark) + bComponentInListMark = true; // avoid other byte values. + for (ON_SubDComponentPtr c = cit.FirstComponent(); c.IsNotNull(); c = cit.NextComponent()) + { + if (bComponentInListMark != c.Mark()) + continue; + ++marked_count; + } + return Internal_Create(subd, true, true, true, bComponentInListMark, marked_count); +} + +unsigned ON_SubDComponentList::CreateFromMarkedVertices(const ON_SubD& subd, bool bVertexInListMark) +{ + unsigned marked_count = 0; + ON_SubDVertexIterator vit(subd); + if (bVertexInListMark) + bVertexInListMark = true; // avoid other byte values. + for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + { + if (bVertexInListMark != v->m_status.RuntimeMark()) + continue; + ++marked_count; + } + return Internal_Create(subd, true, false, false, bVertexInListMark, marked_count); +} + +unsigned ON_SubDComponentList::CreateFromMarkedEdges(const ON_SubD& subd, bool bEdgeInListMark) +{ + unsigned marked_count = 0; + ON_SubDEdgeIterator eit(subd); + if (bEdgeInListMark) + bEdgeInListMark = true; // avoid other byte values. + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + if (bEdgeInListMark != e->m_status.RuntimeMark()) + continue; + ++marked_count; + } + return Internal_Create(subd, false, true, false, bEdgeInListMark, marked_count); +} + +unsigned ON_SubDComponentList::CreateFromMarkedFaces(const ON_SubD& subd, bool bFaceInListMark) +{ + unsigned marked_count = 0; + ON_SubDFaceIterator fit(subd); + if (bFaceInListMark) + bFaceInListMark = true; // avoid other byte values. + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + if (bFaceInListMark != f->m_status.RuntimeMark()) + continue; + ++marked_count; + } + return Internal_Create(subd, false, false, true, bFaceInListMark, marked_count); +} + +unsigned ON_SubDComponentList::Internal_Create(const ON_SubD & subd, bool bAddVertices, bool bAddEdges, bool bAddFaces, bool bComponentInListMark, unsigned marked_component_count) +{ + Destroy(); + + if (0 == marked_component_count) + return 0; + + const unsigned face_count = bAddFaces ? subd.FaceCount() : 0U; + const unsigned edge_count = bAddEdges ? subd.EdgeCount() : 0U; + const unsigned vertex_count = bAddVertices ? subd.VertexCount() : 0U; + if (0 == vertex_count && 0 == edge_count && 0 == face_count) + return 0; + + if (marked_component_count > vertex_count + edge_count + face_count) + return 0; + + bComponentInListMark = bComponentInListMark ? true : false; + m_component_list.Reserve(marked_component_count); + m_component_list.SetCount(0); + if (vertex_count > 0) + { + ON_SubDVertexIterator vit(subd); + for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex()) + { + if (bComponentInListMark != v->m_status.RuntimeMark()) + continue; + m_component_list.Append(v->ComponentPtr()); + } + } + if (edge_count > 0) + { + ON_SubDEdgeIterator eit(subd); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + if (bComponentInListMark != e->m_status.RuntimeMark()) + continue; + m_component_list.Append(e->ComponentPtr()); + } + } + if (face_count > 0) + { + ON_SubDFaceIterator fit(subd); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + { + if (bComponentInListMark != f->m_status.RuntimeMark()) + continue; + m_component_list.Append(f->ComponentPtr()); + } + } + + if (m_component_list.UnsignedCount() > 0) + { + m_subd.ShareDimple(subd); + m_subd_runtime_serial_number = subd.RuntimeSerialNumber(); + m_subd_content_serial_number = subd.ContentSerialNumber(); + } + return m_component_list.UnsignedCount(); +} + +unsigned int ON_SubDComponentList::RemoveAllComponents() +{ + const unsigned count0 = Count(); + m_component_list.SetCount(0); + return count0; +} + +unsigned int ON_SubDComponentList::RemoveAllVertices() +{ + return Internal_RemoveComponents(true, false, false); +} + +unsigned int ON_SubDComponentList::RemoveAllEdges() +{ + return Internal_RemoveComponents(false, true, false); +} + +unsigned int ON_SubDComponentList::RemoveAllFaces() +{ + return Internal_RemoveComponents(false, false, true); +} + +unsigned ON_SubDComponentList::Internal_RemoveComponents( + bool bRemoveVertices, + bool bRemoveEdges, + bool bRemoveFaces +) +{ + unsigned int count0 = Count(); + if (bRemoveVertices || bRemoveEdges || bRemoveFaces) + { + unsigned count1 = 0; + for (unsigned i = 0; i < count0; ++i) + { + const ON_SubDComponentPtr cptr = m_component_list[i]; + bool bRemove = false; + switch (cptr.ComponentType()) + { + case ON_SubDComponentPtr::Type::Vertex: + bRemove = bRemoveVertices; + break; + case ON_SubDComponentPtr::Type::Edge: + bRemove = bRemoveEdges; + break; + case ON_SubDComponentPtr::Type::Face: + bRemove = bRemoveFaces; + break; + default: + bRemove = true; + } + if (bRemove) + continue; + m_component_list[count1++] = cptr; + } + m_component_list.SetCount(count1); + } + return count0 - Count(); +} + + +void ON_SubDComponentList::Destroy() +{ + m_subd_runtime_serial_number = 0; + m_subd_content_serial_number = 0; + m_component_list.SetCount(0); + m_subd.ShareDimple(ON_SubD::Empty); +} + +const ON_SubDComponentFilter ON_SubDComponentFilter::Create( + bool bAcceptVertices, + bool bAcceptEdges, + bool bAcceptFaces +) +{ + ON_SubDComponentFilter f; + if (false == bAcceptVertices) + f.m_bRejectVertices = true; + if (false == bAcceptEdges) + f.m_bRejectEdges = true; + if (false == bAcceptFaces) + f.m_bRejectFaces = true; + return f; +} + +bool ON_SubDComponentFilter::AcceptComponent(ON_COMPONENT_INDEX component_index, const class ON_Geometry* geometry) const +{ + if (false == component_index.IsSubDComponentIndex()) + return false; + const ON_SubDComponentRef* cref = ON_SubDComponentRef::Cast(geometry); + if (nullptr == cref) + return false; + const ON_SubDComponentPtr cptr = cref->ComponentPtr(); + if (component_index.m_index != (int)cptr.ComponentId()) + return false; + switch (component_index.m_type) + { + case ON_COMPONENT_INDEX::TYPE::subd_vertex: + if (ON_SubDComponentPtr::Type::Vertex != cptr.ComponentType()) + return false; + break; + case ON_COMPONENT_INDEX::TYPE::subd_edge: + if (ON_SubDComponentPtr::Type::Edge != cptr.ComponentType()) + return false; + break; + case ON_COMPONENT_INDEX::TYPE::subd_face: + if (ON_SubDComponentPtr::Type::Face != cptr.ComponentType()) + return false; + break; + } + return AcceptComponent(cptr); +} + +bool ON_SubDComponentFilter::AcceptComponent(const class ON_Geometry* geometry) const +{ + return AcceptComponent(ON_SubDComponentRef::Cast(geometry)); +} + + +bool ON_SubDComponentFilter::AcceptComponent(const class ON_SubDComponentRef* cref) const +{ + return (nullptr != cref) ? AcceptComponent(cref->ComponentPtr()) : false; +} + +bool ON_SubDComponentFilter::AcceptComponent(ON_SubDComponentPtr cptr) const +{ + switch (cptr.ComponentType()) + { + case ON_SubDComponentPtr::Type::Vertex: + return AcceptVertex(cptr.Vertex()); + break; + case ON_SubDComponentPtr::Type::Edge: + return AcceptEdge(cptr.Edge()); + break; + case ON_SubDComponentPtr::Type::Face: + return AcceptFace(cptr.Face()); + break; + } + return false; +} + +bool ON_SubDComponentFilter::AcceptVertex(ON_SubDVertexPtr vptr) const +{ + return AcceptVertex(vptr.Vertex()); +} + +bool ON_SubDComponentFilter::AcceptEdge(ON_SubDEdgePtr eptr) const +{ + return AcceptEdge(eptr.Edge()); +} + + +bool ON_SubDComponentFilter::AcceptFace(ON_SubDFacePtr fptr) const +{ + return AcceptFace(fptr.Face()); +} + +bool ON_SubDComponentFilter::AcceptVertex(const ON_SubDVertex * v) const +{ + if (m_bRejectVertices) + return false; + + if (nullptr == v) + return false; + + if (false == AcceptVertexTag(v->m_vertex_tag)) + return false; + + if (ON_SubDComponentFilter::Topology::Unset != m_vertex_topology_filter) + { + // check boundary/interior/nonmanifold + if (v->HasBoundaryVertexTopology()) + { + if (0 == (static_cast(ON_SubDComponentFilter::Topology::Boundary)& static_cast(m_vertex_topology_filter))) + return false; + } + else if (v->HasInteriorVertexTopology()) + { + if (0 == (static_cast(ON_SubDComponentFilter::Topology::Interior)& static_cast(m_vertex_topology_filter))) + return false; + } + else + { + if (0 == (static_cast(ON_SubDComponentFilter::Topology::Nonmanifold)& static_cast(m_vertex_topology_filter))) + return false; + } + } + + return true; +} + +bool ON_SubDComponentFilter::AcceptEdge(const ON_SubDEdge * e) const +{ + if (m_bRejectEdges) + return false; + + if (nullptr == e) + return false; + + if (false == AcceptEdgeTag(e->m_edge_tag)) + return false; + + if (ON_SubDComponentFilter::Topology::Unset != m_edge_topology_filter) + { + // check boundary/interior/nonmanifold + if (1 == e->m_face_count) + { + if (0 == (static_cast(ON_SubDComponentFilter::Topology::Boundary)& static_cast(m_edge_topology_filter))) + return false; + } + else if (2 == e->m_face_count) + { + if (0 == (static_cast(ON_SubDComponentFilter::Topology::Interior)& static_cast(m_edge_topology_filter))) + return false; + } + else + { + if (0 == (static_cast(ON_SubDComponentFilter::Topology::Nonmanifold)& static_cast(m_edge_topology_filter))) + return false; + } + } + + return true; +} + +bool ON_SubDComponentFilter::AcceptFace(const ON_SubDFace * f) const +{ + if (m_bRejectFaces) + return false; + + if (nullptr == f) + return false; + + if (m_maximum_face_edge_count > 0U) + { + const unsigned face_edge_count = f->m_edge_count; + if (face_edge_count < m_minimum_face_edge_count || face_edge_count > m_maximum_face_edge_count) + return false; + } + + if (ON_SubDComponentFilter::Topology::Unset != m_edge_topology_filter) + { + const ON_SubDEdgePtr* eptr = f->m_edge4; + for (unsigned short fei = 0; fei < f->m_edge_count; ++fei, ++eptr) + { + if (4 == fei) + { + eptr = f->m_edgex; + if (nullptr == eptr) + break; + } + const ON_SubDEdge* e = eptr->Edge(); + if (nullptr == e) + continue; + if (1 == e->m_face_count) + { + if (0 == (static_cast(ON_SubDComponentFilter::Topology::Boundary)& static_cast(m_edge_topology_filter))) + return false; + } + else if (2 == e->m_face_count) + { + if (0 == (static_cast(ON_SubDComponentFilter::Topology::Interior)& static_cast(m_edge_topology_filter))) + return false; + } + else + { + if (0 == (static_cast(ON_SubDComponentFilter::Topology::Nonmanifold)& static_cast(m_edge_topology_filter))) + return false; + } + } + } + + return true; +} + +void ON_SubDComponentFilter::SetAcceptVertices(bool bAcceptVertices) +{ + m_bRejectVertices = bAcceptVertices ? false : true; +} + +bool ON_SubDComponentFilter::AcceptVertices() const +{ + return false == m_bRejectVertices; +} + +void ON_SubDComponentFilter::SetAcceptEdges(bool bAcceptEdges) +{ + m_bRejectEdges = bAcceptEdges ? false : true; +} + +bool ON_SubDComponentFilter::AcceptEdges() const +{ + return false == m_bRejectEdges; +} + +void ON_SubDComponentFilter::SetAcceptFaces(bool bAcceptFaces) +{ + m_bRejectFaces = bAcceptFaces ? false : true; +} + +bool ON_SubDComponentFilter::AcceptFaces() const +{ + return false == m_bRejectFaces; +} + +void ON_SubDComponentFilter::SetVertexTopologyFilter(ON_SubDComponentFilter::Topology vertex_topology_filter) +{ + m_vertex_topology_filter = vertex_topology_filter; +} + +void ON_SubDComponentFilter::ClearVertexTopologyFilter() +{ + m_vertex_topology_filter = ON_SubDComponentFilter::Topology::Unset; +} + +ON_SubDComponentFilter::Topology ON_SubDComponentFilter::VertexTopologyFilter() const +{ + return m_vertex_topology_filter; +} + +void ON_SubDComponentFilter::SetEdgeTopologyFilter(ON_SubDComponentFilter::Topology edge_topology_filter) +{ + m_edge_topology_filter = edge_topology_filter; +} + +ON_SubDComponentFilter::Topology ON_SubDComponentFilter::EdgeTopologyFilter() const +{ + return m_edge_topology_filter; +} + +void ON_SubDComponentFilter::ClearEdgeTopologyFilter() +{ + m_edge_topology_filter = ON_SubDComponentFilter::Topology::Unset; +} + +void ON_SubDComponentFilter::SetFaceTopologyFilter(ON_SubDComponentFilter::Topology face_topology_filter) +{ + m_face_topology_filter = face_topology_filter; +} + +ON_SubDComponentFilter::Topology ON_SubDComponentFilter::FaceTopologyFilter() const +{ + return m_face_topology_filter; +} + +void ON_SubDComponentFilter::ClearFaceTopologyFilter() +{ + m_face_topology_filter = ON_SubDComponentFilter::Topology::Unset; +} + +bool ON_SubDComponentFilter::AcceptVertexTag(ON_SubD::VertexTag vertex_tag) const +{ + if (ON_SubD::VertexTag::Unset == m_vertex_tag_filter[0]) + return true; // no tag filter + + for (size_t i = 0; i < sizeof(m_vertex_tag_filter) / sizeof(m_vertex_tag_filter[0]); ++i) + { + if (ON_SubD::VertexTag::Unset == m_vertex_tag_filter[i]) + break; + if (m_vertex_tag_filter[i] != vertex_tag) + continue; + return true; + } + return false; +} + +void ON_SubDComponentFilter::AddAcceptedVertexTag(ON_SubD::VertexTag vertex_tag) +{ + for (size_t i = 0; i < sizeof(m_vertex_tag_filter) / sizeof(m_vertex_tag_filter[0]); ++i) + { + if (vertex_tag == m_vertex_tag_filter[i]) + break; + if (ON_SubD::VertexTag::Unset == m_vertex_tag_filter[i]) + { + m_vertex_tag_filter[i] = vertex_tag; + break; + } + } +} + +void ON_SubDComponentFilter::ClearVertexTagFilter() +{ + for (size_t i = 0; i < sizeof(m_vertex_tag_filter) / sizeof(m_vertex_tag_filter[0]); ++i) + m_vertex_tag_filter[i] = ON_SubD::VertexTag::Unset; +} + + +bool ON_SubDComponentFilter::AcceptEdgeTag(ON_SubD::EdgeTag edge_tag) const +{ + if (ON_SubD::EdgeTag::Unset == m_edge_tag_filter[0]) + return true; // no tag filter + + for (size_t i = 0; i < sizeof(m_edge_tag_filter) / sizeof(m_edge_tag_filter[0]); ++i) + { + if (ON_SubD::EdgeTag::Unset == m_edge_tag_filter[i]) + break; + if (m_edge_tag_filter[i] != edge_tag) + continue; + return true; + } + return false; +} + +void ON_SubDComponentFilter::AddAcceptedEdgeTag(ON_SubD::EdgeTag edge_tag) +{ + for (size_t i = 0; i < sizeof(m_edge_tag_filter) / sizeof(m_edge_tag_filter[0]); ++i) + { + if (edge_tag == m_edge_tag_filter[i]) + break; + if (ON_SubD::EdgeTag::Unset == m_edge_tag_filter[i]) + { + m_edge_tag_filter[i] = edge_tag; + break; + } + } +} + +void ON_SubDComponentFilter::ClearEdgeTagFilter() +{ + for (size_t i = 0; i < sizeof(m_edge_tag_filter) / sizeof(m_edge_tag_filter[0]); ++i) + m_edge_tag_filter[i] = ON_SubD::EdgeTag::Unset; +} + +bool ON_SubDComponentFilter::AcceptFaceEdgeCount( + unsigned face_edge_count +) const +{ + return (m_maximum_face_edge_count >= 3U) ? (face_edge_count >= m_minimum_face_edge_count && face_edge_count <= m_maximum_face_edge_count) : false; +} + +void ON_SubDComponentFilter::SetFaceEdgeCountFilter( + unsigned minimum_face_edge_count, + unsigned maximum_face_edge_count +) +{ + if (minimum_face_edge_count <= maximum_face_edge_count && maximum_face_edge_count >= 3U) + { + m_minimum_face_edge_count = minimum_face_edge_count; + m_maximum_face_edge_count = maximum_face_edge_count; + } +} + +void ON_SubDComponentFilter::ClearFaceEdgeCountFilter() +{ + m_minimum_face_edge_count = 0U; + m_maximum_face_edge_count = 0U; +} + + #if defined(ON_SUBD_CENSUS) ////////////////////////////////////////////////////////////////////////// diff --git a/opennurbs_subd.h b/opennurbs_subd.h index a0470ff4..942eb420 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -374,6 +374,40 @@ public: int relative_vertex_index ) const; + /* + Parameters: + relative_vertex_index - [in] + 0: return Edge()->Vertex(EdgeDirection()) + 1: return Edge()->Vertex(1-EdgeDirection()) + Returns: + The requested vertex control net point EdgeDirection() taken into account. + ON_3dPoint::NanPoint if relative_vertex_index, Edge() is nullptr, or Edge()->Vertex() is nullptr. + */ + const ON_3dPoint RelativeControlNetPoint( + int relative_vertex_index + ) const; + + const ON_Line RelativeControlNetLine() const; + + const ON_3dVector RelativeControlNetDirection() const; + + /* + Parameters: + relative_vertex_index - [in] + Returns: + If Edge() not nullptr, then + If (relative_vertex_index = 0), returns Edge()->m_sector_coefficient(EdgeDirection()) + If (relative_vertex_index = 0), returns Edge()->m_sector_coefficient(1-EdgeDirection()) + Otherwise ON_SubDSectorType::ErrorSectorCoefficient is returned. + Remarks: + The name "sector coefficient" is used because is is a property of the + vertex's sector (every edge in vertex sector has the same value at the tagged vertex). + The sector coefficient does not change which a subdivision is applied. + */ + double RelativeSectorCoefficient( + int relative_vertex_index + ) const; + /* Returns: The vector from RelativeVertex(0)->ControlNetPoint() to RelativeVertex(1)->ControlNetPoint(), @@ -422,6 +456,33 @@ public: const class ON_SubDComponentPtr& edge_component ); + /* + Parameters: + edge - [in] + start_vertex - [in] + One of the edge's vertices. + Returns: + An ON_SubDEdgePtr pointing at edge with RelativeVertex(0) = start_vertex. + */ + static const ON_SubDEdgePtr CreateFromStartVertex( + const class ON_SubDEdge* edge, + const ON_SubDVertex* start_vertex + ); + + /* + Parameters: + edge - [in] + end_vertex - [in] + One of the edge's vertices. + Returns: + An ON_SubDEdgePtr pointing at edge with RelativeVertex(1) = end_vertex. + */ + static const ON_SubDEdgePtr CreateFromEndVertex( + const class ON_SubDEdge* edge, + const ON_SubDVertex* end_vertex + ); + + /* Returns: The current value of the component mark ( m_status->RuntimeMark() ). @@ -526,6 +587,12 @@ public: const ON_ComponentStatus Status() const; + /* + Returns: + A ON_SubDFacePtr pointing at the same face with the direction reversed from this. + */ + const ON_SubDFacePtr Reversed() const; + static const ON_SubDFacePtr Create( const class ON_SubDFace* face, ON__UINT_PTR direction @@ -964,6 +1031,21 @@ public: */ ON_SubDComponentPtr::Type ComponentType() const; + /* + Returns true if First() is ON_SubDComponentPtr::Null. + */ + bool FirstIsNull() const; + + /* + Returns true if Second() is ON_SubDComponentPtr::Null. + */ + bool SecondIsNull() const; + + /* + Returns true if both First() and Second() are ON_SubDComponentPtr::Null. + */ + bool BothAreNull() const; + public: const static ON_SubDComponentPtrPair Null; @@ -1541,9 +1623,6 @@ public: class ON_NurbsSurface* m_nurbs_surface = nullptr; }; - - - ////////////////////////////////////////////////////////////////////////// // // ON_SubD @@ -1853,7 +1932,24 @@ public: /// Every edge in an edge chain has the same smooth/crease edge tag /// and interior vertices have the corresponding smooth/crease vertex tag. /// - EqualEdgeAndVertexTag = 3 + EqualEdgeAndVertexTag = 3, + + /// + /// Every edge in an edge chain has the same smooth/crease property + /// and every edge has the same number of faces. + /// If the edges have 1 face, then interior vertices have valence = 3. + /// If the edges have 2 faces, then interior vertices have valence = 4. + /// + EqualEdgeTagAndOrdinary = 4, + + /// + /// Every edge in an edge chain has the same smooth/crease edge tag, + /// every edge has the same number of faces, + /// and interior vertices have the corresponding smooth/crease vertex tag. + /// If the edges have 1 face, then interior vertices have valence = 3. + /// If the edges have 2 faces, then interior vertices have valence = 4. + /// + EqualEdgeAndVertexTagAndOrdinary = 5 }; #pragma endregion @@ -2610,7 +2706,11 @@ public: */ const class ON_SubDVertex* VertexFromId( unsigned int vertex_id - ) const; + ) const; + + const class ON_SubDVertex* VertexFromComponentIndex( + ON_COMPONENT_INDEX component_index + ) const; ///////////////////////////////////////////////////////// // @@ -2651,6 +2751,10 @@ public: unsigned int edge_id ) const; + const class ON_SubDEdge* EdgeFromComponentIndex( + ON_COMPONENT_INDEX component_index + ) const; + ///////////////////////////////////////////////////////// // // Face access @@ -2690,6 +2794,12 @@ public: unsigned int face_id ) const; + + const class ON_SubDFace* FaceFromComponentIndex( + ON_COMPONENT_INDEX component_index + ) const; + + ///////////////////////////////////////////////////////// // // Component (vertex, edge, face) state ( selected, highlighted, ... ) tools @@ -2910,12 +3020,17 @@ public: Returns: Merged edge (eptr0) or ON_SubDEdgePtr::Null if edges could not be merged */ - ON_SubDEdgePtr MergeEdges( + ON_SubDEdgePtr MergeConsecutiveEdges( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ); - static bool EdgesCanBeMerged( + /* + Returns: + True if eptr0.RelativeVetex(1) == eptr1.RelativeVetex(0) and both edges + have the same set of faces. + */ + static bool EdgesAreConsecutive( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ); @@ -3099,7 +3214,7 @@ public: sector weights will be checked and updated as needed. Returns: Number of edges that had a tag value changed or sector - coefficient set to ON_SubDSectorType::UnsetSectorWeight. + coefficient set to ON_SubDSectorType::UnsetSectorCoefficient. Remarks: It is easiest to call UpdateAllTagsAndSectorCoefficients(). */ @@ -3123,7 +3238,7 @@ public: sector weights will be checked and updated as needed. Returns: Number of edges that had a tag value changed or sector - coefficient set to ON_SubDSectorType::UnsetSectorWeight. + coefficient set to ON_SubDSectorType::UnsetSectorCoefficient. Remarks: It is easiest to call UpdateAllTagsAndSectorCoefficients(). */ @@ -3379,6 +3494,7 @@ public: ON_SubD::EdgeTag edge_tag ); + /* Description: Remove all interior creases. @@ -4054,6 +4170,19 @@ public: void ClearEvaluationCache() const; + /* + Description: + Clear all cached evaluation information (meshes, surface points, boundiang boxes, ...) + that depends on the vertex's control point location or tag. + Parameter: + vertex - [in] + */ + void ClearNeighborhoodEvaluationCache( + const ON_SubDVertex* vertex, + bool bTagChanged + ) const; + + /* Description: Get a mesh of the subdivision control net. @@ -4072,6 +4201,9 @@ 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.GetSurfaceNurbsFragments() @@ -4488,6 +4620,143 @@ private: #pragma ON_PRAGMA_WARNING_POP }; +////////////////////////////////////////////////////////////////////////// +// +// ON_SubDComponentList +// +class ON_CLASS ON_SubDComponentList +{ +public: + ON_SubDComponentList() = default; + ~ON_SubDComponentList() = default; + ON_SubDComponentList(const ON_SubDComponentList&); + ON_SubDComponentList& operator=(const ON_SubDComponentList&); + +public: + static const ON_SubDComponentList Empty; + +public: + unsigned CreateFromMarkedComponents(const ON_SubD& subd, bool bComponentInListMark); + unsigned CreateFromMarkedVertices(const ON_SubD& subd, bool bVertexInListMark); + unsigned CreateFromMarkedEdges(const ON_SubD& subd, bool bEdgeInListMark); + unsigned CreateFromMarkedFaces(const ON_SubD& subd, bool bFaceInListMark); + + unsigned CreateFromComponentList(const ON_SubD& subd, const ON_SimpleArray& component_list); + unsigned CreateFromComponentList(const ON_SubD& subd, const ON_SimpleArray& component_list); + + unsigned CreateFromVertexIdList(const ON_SubD& subd, const ON_SimpleArray& free_vertex_ids); + unsigned CreateFromVertexList(const ON_SubD& subd, const ON_SimpleArray& free_vertices); + unsigned CreateFromVertexList(const ON_SubD& subd, const ON_SimpleArray& free_vertices); + + void Destroy(); + + /* + Returns: + Number of removed components. + */ + unsigned int RemoveAllComponents(); + + /* + Returns: + Number of removed components. + */ + unsigned int RemoveAllVertices(); + + /* + Returns: + Number of removed components. + */ + unsigned int RemoveAllEdges(); + + /* + Returns: + Number of removed components. + */ + unsigned int RemoveAllFaces(); + + /* + Returns: + SubD runtime serial number. + */ + ON__UINT64 SubDRuntimeSerialNumber() const; + + /* + Returns: + SubD content serial number when this list was created or the last + time UpdateContentSerialNumber() was run. + */ + ON__UINT64 SubDContentSerialNumber() const; + + unsigned int Count() const; + + /* + operator[] returns ON_SubDComponentPtr::Null when index is out of bounds. + */ + const ON_SubDComponentPtr operator[](int) const; + const ON_SubDComponentPtr operator[](unsigned int) const; + const ON_SubDComponentPtr operator[](ON__INT64) const; + const ON_SubDComponentPtr operator[](ON__UINT64) const; +#if defined(ON_RUNTIME_APPLE) + const ON_SubDComponentPtr operator[](size_t) const; +#endif + + const ON_SimpleArray< ON_SubDComponentPtr >& ComponentList() const; + + const ON_SubD& SubD() const; + + /* + Description: + Update the saved subd content serial number to the current value of SubD().ContentSerialNumber(). + Returns: + Updated value of subd content serial number. + */ + ON__UINT64 UpdateContentSerialNumber(); + + /* + Description: + Change the component list to reference components in a different subd. + Parameters: + new_subd - [in] + subd to replace current referenced subd + bUpdateDeletedComponents - [in] + false: current components that are deleted will be ignored. + true: if the corresponding component in new_sub is not deleted, it + will be added to the list. + Returns: + Number of components in list after updating. + */ + unsigned int UpdateSubDForExperts(const ON_SubD& subd, bool bUpdateDeletedComponents); + + +private: + unsigned Internal_Create( + const ON_SubD& subd, + bool bAddVertices, + bool bAddEdges, + bool bAddFaces, + bool bComponentInListMark, + unsigned marked_component_count + ); + + unsigned Internal_RemoveComponents( + bool bRemoveVertices, + bool bRemoveEdges, + bool bRemoveFaces + ); + +private: + ON__UINT64 m_subd_runtime_serial_number = 0; + ON__UINT64 m_subd_content_serial_number = 0; + + unsigned m_subd_vertex_count = 0; + unsigned m_subd_edge_count = 0; + unsigned m_subd_face_count = 0; + unsigned m_reserved = 0; + +private: + ON_SubD m_subd; // keeps subd dimple in scope while m_component_list[] is active + ON_SimpleArray< ON_SubDComponentPtr > m_component_list; +}; ////////////////////////////////////////////////////////////////////////// // @@ -4496,9 +4765,9 @@ private: class ON_SubDComponentMarksClearAndRestore { public: - // Constructor saves current component RuntimeMark() settings. + // Constructor saves current component RuntimeMark() settings and then clears them. ON_SubDComponentMarksClearAndRestore( - ON_SubD& subd + const ON_SubD& subd ); // Destructor restores saved marks. @@ -4559,7 +4828,7 @@ public: ///////////////////////////////////////////////////////////////////////////////////// // - // Sector Weights + // Sector Coefficients // ///////////////////////////////////////////////////////////////////////////////////// // @@ -4644,10 +4913,10 @@ public: Returns: w: 0.0 <= w < 1.0 w = sector theta value. - ON_SubDSectorType::ErrorSectorWeight + ON_SubDSectorType::ErrorSectorCoefficient This ON_SubDSectorType is not valid and the calculation failed. */ - double SectorWeight() const; + double SectorCoefficient() const; unsigned int FacetEdgeCount() const; @@ -4839,9 +5108,9 @@ public: public: /* Returns: - ON_SubDSectorType::IgnoredSectorWeight + ON_SubDSectorType::IgnoredSectorCoefficient */ - static double SmoothSectorWeight(); + static double SmoothSectorCoefficient(); /* Parameters: @@ -4851,7 +5120,7 @@ public: Returns: 0: failed to caclulate weight - ON_SubDSectorType::UnsetSectorWeight: + ON_SubDSectorType::UnsetSectorCoefficient: 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. @@ -4862,15 +5131,15 @@ public: This is a useful tool when calling AddEdge while a subdivision level is being constructed. */ - static double CreaseSectorWeight( + static double CreaseSectorCoefficient( unsigned int sector_face_count ); - static double DartSectorWeight( + static double DartSectorCoefficient( unsigned int sector_face_count ); - static double CornerSectorWeight( + static double CornerSectorCoefficient( unsigned int sector_face_count, double corner_sector_angle_radians ); @@ -4901,33 +5170,33 @@ public: static const double ErrorSectorTheta; // = -9992.0; - // This value is is used to set sector weights when the + // This value is is used to set edge sector coefficients when the // actual value is not needed. This occurs at both ends // of a creased edge and when the end of a smooth edge // is a smooth vertex. - static const double IgnoredSectorWeight; // = 0.0; + static const double IgnoredSectorCoefficient; // = 0.0; - // This value is used to mark sector weights that need to be + // This value is used to mark edge sector coefficients that need to be // set in the future when more information is available. // It is typically used when creating a subD control net // and the facet type is not known. Any value < 0.0 and not // equal to ON_UNSET_VALUE would work. The fact that the actual // value is -999.0 has no other significance. - static const double UnsetSectorWeight; // = -8883.0; + static const double UnsetSectorCoefficient; // = -8883.0; - // This value is indicate a sector weight calculation failed. - static const double ErrorSectorWeight; // = -9993.0; + // This value indicates an edge sector coefficient calculation failed. + static const double ErrorSectorCoefficient; // = -9993.0; - static bool IsValidSectorWeightValue( + static bool IsValidSectorCoefficientValue( double weight_value, - bool bAllowUnsetTaggedEndWeight + bool bAllowUnsetTaggedEndCoefficient ); /* Returns: - ON_SubDSectorType::ErrorSectorWeight and calls debug breakpoint + ON_SubDSectorType::ErrorSectorCoefficient and calls debug breakpoint */ - static double SectorWeightCalculationError(); + static double SectorCoefficientCalculationError(); /* @@ -5349,7 +5618,7 @@ private: 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::MaximumCornerAngleIndex unsigned int m_sector_face_count = 0; - double m_sector_weight = 0.0; + double m_sector_coefficient = 0.0; double m_sector_theta = 0.0; double m_corner_sector_angle_radians = 0.0; @@ -5426,7 +5695,7 @@ private: Returns: 0: failed to caclulate weight - ON_SubDSectorType::ErrorSectorWeight: + ON_SubDSectorType::ErrorSectorCoefficient: sector_theta is not valid. 0 < w < 1: The returned value is @@ -5435,7 +5704,7 @@ private: This is a useful tool when calling AddEdge while a subdivision level is being constructed. */ - static double SectorWeightFromTheta( + static double SectorCoefficientFromTheta( double sector_theta ); }; @@ -7136,15 +7405,11 @@ protected: friend class ON_SubDHeap; enum SavedPointsFlags : unsigned char { - // 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. - SubdivisionPointBit = 0x20, - // if ( 0 != (m_saved_points_flags & SubDDisplacementVIsSet), then m_displacementV is set. - SubdivisionDisplacementBit = 0x40, + SubdivisionDisplacementBit = 0x20, + + // if ( 0 != (m_saved_points_flags & SubdivisionPointBit), then m_cache_subd_P is set. + SubdivisionPointBit = 0x40, // 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. @@ -7157,10 +7422,41 @@ protected: CachedPointMask = 0xF0 }; + enum ModifiedFlags : unsigned char + { + // if ( 0 != (m_saved_points_flags & Modified1Bit), then the component has been modified and + // cached subdivision information needs to be recalculated. + Modified1Bit = 0x01, + + // if ( 0 != (m_saved_points_flags & Modified2Bit), then the component is adjacent to + // a modified component and cached subdivision information needs to be recalculated. + Modified2Bit = 0x02, + + // ModifiedFlagsMask = Modified1Bit | Modified2Bit + // if ( 0 != (m_saved_points_flags & ModifiedFlagsMask), then any cached subdivision information + // on that component needs to be recalculated. + ModifiedFlagsMask = 0x03 + }; + // m_saved_points_flags is a bit field based on ON_SubDComponentBase::SavePointsFlags values. // GetSurfacePoint( bUseSavedSurfacePoint=true ) can change the value of m_saved_points_flags void Internal_SetSavedSurfacePointFlag(bool bSavedSurfacePointFlag) const; - void Internal_SetSavedControlNetFragmentFlag(bool bSavedControlNetFragmentFlag) const; + void Internal_SetModified1Flag() const; + void Internal_SetModified2Flag() const; + + /* + Returns: + True if Modified1Bit or Modified2Bit is set. + */ + bool Internal_Modified1IsSet() const; + + /* + Returns: + True if Modified1Bit or Modified2Bit is set. + */ + bool Internal_Modified1or2IsSet() const; + + void Internal_ClearModifiedFlags() const; mutable unsigned char m_saved_points_flags = 0U; unsigned char m_level = 0U; @@ -7195,18 +7491,10 @@ protected: */ 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 @@ -7264,10 +7552,63 @@ public: public: static const ON_SubDVertexEdgeProperties Zero; // all member values are zero. + /* + Returns: + True if there are no null edges and there are two edges with a single face and all remaining edges have two faces. + Remarks: + Tags are ignored. + */ + bool HasInteriorVertexTopology() const; + + /* + Returns: + True if there are no null edges and there are at least two edges and they all have two faces. + Remarks: + Tags are ignored. + */ + bool HasBoundaryVertexTopology() const; + + /* + Returns: + HasInteriorVertexTopology() || HasBoundaryVertexTopology(). + */ + bool HasManifoldVertexTopology() const; + + /* + Returns: + True if there are no null edges and there is an edge with zero faces or an edge with three or more faces. + Remarks: + Tags are ignored. + */ + bool HasNonmanifoldVertexTopology() const; + + /* + Returns: + Number of edges. + */ + unsigned EdgeCount() const; + public: // Number of null edges unsigned short m_null_edge_count = 0; + + ///////////////////////////////////////////////////// + // + // Vertex attached component counts + // + + // vertex->m_edge_count + unsigned short m_edge_count = 0; + + // vertex->m_face_count + unsigned short m_face_count = 0; + + ///////////////////////////////////////////////////// + // + // Edge tag counts + // + // Number of edges tags ON_SubD::EdgeTag::Unset unsigned short m_unset_edge_count = 0; @@ -7277,6 +7618,11 @@ public: // Number of edges tags ON_SubD::EdgeTag::Crease unsigned short m_crease_edge_count = 0; + ///////////////////////////////////////////////////// + // + // Edge topology counts + // + // Number of wire edges (0 attached faces) unsigned short m_wire_edge_count = 0; @@ -7289,6 +7635,12 @@ public: // Number of nonmanifold edges (3 or more attached faces) unsigned short m_nonmanifold_edge_count = 0; + + ///////////////////////////////////////////////////// + // + // Edge face counts + // + // Minimum value of attached edges's m_face_count. unsigned short m_min_edge_face_count = 0; @@ -7589,6 +7941,38 @@ public: const ON_SubDVertexEdgeProperties EdgeProperties() const; + /* + Parameters: + eptr0 - [out] + eptr1 - [out] + If a vertex has exactly two attached edges, each of which has a single attached face, + then these edges are returned in the order the appear in the vertex's edge list. + (RelativeVertex(0) = this vertex). Othwerise the parameters are set to null. + Returns: + True if the vertex has exactly two attached edges, each of which has a single attached face. + False otherwise. + */ + bool GetBoundaryVertexEdges( + ON_SubDEdgePtr* eptr0, + ON_SubDEdgePtr* eptr1 + ) const; + + /* + Parameters: + vei0 - [out] + vei1 - [out] + If a vertex has exactly two attached edges, each of which has a single attached face, + then the indices of those edges in the vertex's edge list are returned. + Othewise ON_UNSET_UINT_INDEX is returned. + Returns: + True if the vertex has exactly two attached edges, each of which has a single attached face. + False otherwise. + */ + bool GetBoundaryVertexEdgeIndices( + unsigned* vei0, + unsigned* vei1 + ) const; + /* Description: A "standard" vertex is one where the standard subdivsion matrix for that vertex @@ -7771,6 +8155,12 @@ public: */ bool SurfacePointIsSet() const; + + const ON_Plane VertexFrame( + ON_SubDComponentLocation subd_appearance + ) const; + + /* Description: Call this function if the vertex is modified and it will clear any @@ -7790,6 +8180,31 @@ public: */ unsigned int MarkedFaceCount() const; + /* + Returns: + Minimum number of edges for any face attached to this vertex. + */ + unsigned int MinimumFaceEdgeCount() const; + + /* + Returns: + Maximum number of edges for any face attached to this vertex. + */ + unsigned int MaximumFaceEdgeCount() const; + + + /* + Returns: + Minimum number of faces for any edge attached to this vertex. + */ + unsigned int MinimumEdgeFaceCount() const; + + /* + Returns: + Maximum number of faces for any edge attached to this vertex. + */ + unsigned int MaximumEdgeFaceCount() const; + /* Description: Expert user tool to unset ON_SubEdge.m_sector_coefficent[] values for @@ -7838,6 +8253,8 @@ private: private: friend class ON_SubDArchiveIdMap; + friend class ON_SubDEdge; + friend class ON_SubDFace; void CopyFrom( const ON_SubDVertex* src, bool bCopyEdgeArray, @@ -7921,6 +8338,11 @@ public: const ON_BoundingBox ControlNetBoundingBox() const; + const ON_Plane CenterFrame( + ON_SubDComponentLocation subd_appearance + ) const; + + /* Description: @@ -7986,12 +8408,12 @@ public: }; unsigned short m_face_count = 0; unsigned short m_facex_capacity = 0; - ON_SubDFacePtr m_face2[2]; + ON_SubDFacePtr m_face2[2] = {}; ON_SubDFacePtr* m_facex = nullptr; // m_vertex[0] = vertex at the start of the edge. // m_vertex[1] = vertex at the end of the edge. - const class ON_SubDVertex* m_vertex[2]; + const class ON_SubDVertex* m_vertex[2] = {}; // If the value of vertex->m_vertex_tag is not ON_SubD::VertexTag::Smooth, // then that vertex is "tagged". @@ -8012,7 +8434,7 @@ public: // // If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth and // exactly one end vertex is tagged, then the m_sector_coefficient[] - // value for the tagged end is calculated by ON_SubDSectorType::SectorWeight(). + // value for the tagged end is calculated by ON_SubDSectorType::SectorCoefficient(). // tagged_weight*tagged_vertex + (1.0 - tagged_weight)*untagged_vertex // is used when combining the edge ends. // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth @@ -8021,7 +8443,7 @@ public: // 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(). + // values are calculated by ON_SubDSectorType::SectorCoefficient(). // When the edge is subdivided, the midpoint of the edge is the // location of the edge.s subdivision point. // The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth @@ -8037,8 +8459,12 @@ public: // // 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]; + // the number of faces in the sector, and the control net crease angle when the tagged end is a corner. + // + // The name "sector coefficient" is used because the value is a property of the + // vertex's sector (every smooth edge inside a vertex sector has the same value at the tagged vertex). + // The sector coefficient does not change which a subdivision is applied. + double m_sector_coefficient[2] = {}; // If m_edge_tag is not ON_SubD::EdgeTag::Sharp, then m_sharpness is ignored. // If m_edge_tag is ON_SubD::EdgeTag::Sharp, then m_sharpness controls how hard/soft @@ -8207,6 +8633,8 @@ public: */ const ON_3dPoint ControlNetPoint( unsigned int i ) const; + const ON_Line ControlNetLine() const; + /* Returns: If vertices are set, then the vector from m_vertex[0]->ControlNetPoint() @@ -8451,6 +8879,8 @@ private: private: friend class ON_SubDArchiveIdMap; + friend class ON_SubDVertex; + friend class ON_SubDFace; void CopyFrom( const ON_SubDEdge* src, bool bReverseEdge, @@ -8981,6 +9411,9 @@ public: private: friend class ON_SubDArchiveIdMap; + friend class ON_SubDVertex; + friend class ON_SubDEdge; + void CopyFrom( const ON_SubDFace* src, bool bCopyEdgeArray @@ -11411,8 +11844,7 @@ public: // // If the facet type is quad, and C is a standard interior vertex, // then the "standard vertex ring" is the list of 2*N+1 points - // (C, P[0], Q[0], ...., P[N-1], Q[N-1]), where Q[I] is the average of the - // four corners of the quad F[i]. + // (C, P[0], Q[0], ...., P[N-1], Q[N-1]), where Q[I] is the vertex of quad F[i] diagonally across from C. // // If the facet type is quad, and C is a standard boundary vertex, // then the "standard vertex ring" is the list of 2*N points @@ -11668,6 +12100,18 @@ public: const ON_SubDEdge* edge0 ); + /* + 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* AllocateEdgeSubdivisionVertex( + bool bUseFindOrAllocate, + const ON_SubDEdge * edge0 + ); + /* Description: Find or allocate a vertex and the face subdivision point. The vertex will have an @@ -11706,13 +12150,13 @@ public: If v0 null or ON_SubD::VertexTag::Smooth == v0->m_vertex_tag, and v1 is null or tagged, then m_sector_weight[0] is set to v0_sector_weight. In all other cases the value of v0_sector_weight is ignored and m_sector_weight[0] - is set to ON_SubDSectorType::IgnoredSectorWeight. + is set to ON_SubDSectorType::IgnoredSectorCoefficient. v1 - [in] v1_sector_weight - [in] If v1 null or ON_SubD::VertexTag::Smooth == v1->m_vertex_tag, and v0 is null or tagged, then m_sector_weight[1] is set to v1_sector_weight. In all other cases the value of v1_sector_weight is ignored and m_sector_weight[1] - is set to ON_SubDSectorType::IgnoredSectorWeight. + is set to ON_SubDSectorType::IgnoredSectorCoefficient. Returns: An edge. The vertex parameter information is used to set the ON_SubDEdge.m_vertex[] @@ -11743,6 +12187,16 @@ public: double v1_sector_weight ); + const ON_SubDEdgePtr AllocateEdge( + bool bUseFindOrAllocatEdge, + ON_SubDVertex* v0, + double v0_sector_weight, + ON_SubDVertex* v1, + double v1_sector_weight + ); + + + private: /* Returns: @@ -11870,33 +12324,80 @@ public: 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. + edge_chains - [out] + The edge_chains[] 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). + edge_chains[i].RelativeVertex(1) == edge_chains[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]. + When edge_chains[i].RelativeVertex(1) != edge_chains[i+1].RelativeVertex(0), + a chain ends at edge_chains[i] and another begins at edge_chains[i+1]. The first edge in every chain has the same orientation as the input edge - from unsorted_edges[]. + from edge_chains[]. Returns: - Number of chains in sorted_edges[]. + Number of chains in edge_chains[]. */ static unsigned int SortEdgesIntoEdgeChains( const ON_SimpleArray< ON_SubDEdgePtr >& unsorted_edges, unsigned int minimum_chain_length, - ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges + ON_SimpleArray< ON_SubDEdgePtr >& edge_chains ); static unsigned int SortEdgesIntoEdgeChains( const ON_SimpleArray< const ON_SubDEdge* >& unsorted_edges, unsigned int minimum_chain_length, + ON_SimpleArray< ON_SubDEdgePtr >& edge_chains + ); + + static unsigned int SortEdgesIntoEdgeChains( + const ON_SimpleArray< ON_SubDComponentPtr >& unsorted_edges, + unsigned int minimum_chain_length, ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges ); + static unsigned int SortEdgesIntoEdgeChains( + const ON_SubD& subd, + const ON_SimpleArray< ON_COMPONENT_INDEX >& unsorted_edges, + unsigned int minimum_chain_length, + ON_SimpleArray< ON_SubDEdgePtr >& edge_chains + ); + + /* + Description: + Orient edges[] into edge chains preserving the order of edges[]. + Returns: + Number of chains in edge_chains[]. + */ + static unsigned int OrientEdgesIntoEdgeChains( + const ON_SubD& subd, + const ON_SimpleArray< ON_COMPONENT_INDEX >& edges, + ON_SimpleArray< ON_SubDEdgePtr >& edge_chains + ); + + /* + Description: + Orient edges[] into edge chains preserving the order of edges[]. + Returns: + Number of chains in edge_chains[]. + */ + static unsigned int OrientEdgesIntoEdgeChains( + const ON_SimpleArray< const ON_SubDEdge* >& edges, + ON_SimpleArray< ON_SubDEdgePtr >& edge_chains + ); + + /* + Description: + Orient edges[] into edge chains preserving the order of edges[]. + Returns: + Number of chains in edge_chains[]. + */ + static unsigned int OrientEdgesIntoEdgeChains( + const ON_SimpleArray< ON_SubDComponentPtr >& edges, + ON_SimpleArray< ON_SubDEdgePtr >& edge_chains + ); + ///////////////////////////////////////////////////////// // @@ -12159,6 +12660,222 @@ private: ON_ComponentStatus m_status_check_fail = ON_ComponentStatus::Selected; }; +class ON_CLASS ON_SubDComponentFilter +{ +public: + ON_SubDComponentFilter() = default; + ~ON_SubDComponentFilter() = default; + ON_SubDComponentFilter(const ON_SubDComponentFilter&) = default; + ON_SubDComponentFilter& operator=(const ON_SubDComponentFilter&) = default; + +public: + + /// + /// No filters are set and all components are accepted. + /// + static const ON_SubDComponentFilter Unset; + + /// + /// Only vertices are accepted. + /// + static const ON_SubDComponentFilter OnlyVertices; + + /// + /// Only edges are accepted. + /// + static const ON_SubDComponentFilter OnlyEdges; + + /// + /// Only faces are accepted. + /// + static const ON_SubDComponentFilter OnlyFaces; + + /* + Parameters: + bAcceptVertices - [in] + If true, all vertices are accepted. Otherwise, all vertices are rejected. + bAcceptEdges - [in] + If true, all edges are accepted. Otherwise all edges are rejected. + bAcceptFaces - [in] + If true, all faces are accepted. Otherwise all faces are rejected. + */ + static const ON_SubDComponentFilter Create( + bool bAcceptVertices, + bool bAcceptEdges, + bool bAcceptFaces + ); + +public: + /// + /// Topology filters. + /// + enum class Topology : unsigned char + { + /// + /// No topology filter. + /// + Unset = 0, + + /// + /// A boundary vertex has a single sector bounded by two boundary edges. + /// A boundary edge has a single face. + /// A boundary face has at least one boundary edge. + /// + Boundary = 1, + + /// + /// An interior vertex has the same number of edges and faces and all edges are interior. + /// An interior edge has two faces. + /// An interior face has all interior edges. + /// + Interior = 2, + + /// + /// A nonmanifold vertex is a vertex that is neither boundary nor interior. + /// A nonmanifold edge is an edge that is neither boundary nor interior. + /// A nonmanifold face is a face that is neither boundary nor interior. + /// + Nonmanifold = 4, + + /// + /// A component that is either boundary or interior. + /// + BoundaryOrInterior = 3, + + /// + /// A component that is either boundary or nonmanifold. + /// + BoundaryOrNonmanifold = 5, + + /// + /// A component that is either interior or nonmanifold + /// + InteriorOrNonmanifold = 6 + }; + + bool AcceptComponent( + const class ON_Geometry* geometry + ) const; + + bool AcceptComponent( + ON_COMPONENT_INDEX component_index, + const class ON_Geometry* geometry + ) const; + + bool AcceptComponent( + const class ON_SubDComponentRef* cref + ) const; + + /* + Returns: + True if the filter accepts the component. False otherwise. + */ + bool AcceptComponent(ON_SubDComponentPtr cptr) const; + + /* + Returns: + True if the filter accepts the vertex. False otherwise. + */ + bool AcceptVertex(ON_SubDVertexPtr vptr) const; + + /* + Returns: + True if the filter accepts the edge. False otherwise. + */ + bool AcceptEdge(ON_SubDEdgePtr eptr) const; + + /* + Returns: + True if the filter accepts the face. False otherwise. + */ + bool AcceptFace(ON_SubDFacePtr fptr) const; + + /* + Returns: + True if the filter accepts the vertex. False otherwise. + */ + bool AcceptVertex(const ON_SubDVertex* v) const; + + /* + Returns: + True if the filter accepts the edge. False otherwise. + */ + bool AcceptEdge(const ON_SubDEdge* e) const; + + /* + Returns: + True if the filter accepts the face. False otherwise. + */ + bool AcceptFace(const ON_SubDFace* f) const; + + void SetAcceptVertices(bool bAcceptVertices); + + bool AcceptVertices() const; + + void SetAcceptEdges(bool bAcceptEdges); + + bool AcceptEdges() const; + + void SetAcceptFaces(bool bAcceptFaces); + + bool AcceptFaces() const; + + void SetVertexTopologyFilter(ON_SubDComponentFilter::Topology vertex_topology_filter); + + void ClearVertexTopologyFilter(); + + ON_SubDComponentFilter::Topology VertexTopologyFilter() const; + + void SetEdgeTopologyFilter(ON_SubDComponentFilter::Topology edge_topology_filter); + + ON_SubDComponentFilter::Topology EdgeTopologyFilter() const; + + void ClearEdgeTopologyFilter(); + + void SetFaceTopologyFilter(ON_SubDComponentFilter::Topology face_topology_filter); + + ON_SubDComponentFilter::Topology FaceTopologyFilter() const; + + void ClearFaceTopologyFilter(); + + bool AcceptVertexTag(ON_SubD::VertexTag vertex_tag) const; + + void AddAcceptedVertexTag(ON_SubD::VertexTag vertex_tag); + + void ClearVertexTagFilter(); + + bool AcceptEdgeTag(ON_SubD::EdgeTag edge_tag) const; + + void AddAcceptedEdgeTag(ON_SubD::EdgeTag edge_tag); + + void ClearEdgeTagFilter(); + + bool AcceptFaceEdgeCount( + unsigned face_edge_count + ) const; + + void SetFaceEdgeCountFilter( + unsigned minimum_face_edge_count, + unsigned maximum_face_edge_count + ); + + void ClearFaceEdgeCountFilter(); + +private: + bool m_bRejectVertices = false; + ON_SubDComponentFilter::Topology m_vertex_topology_filter = ON_SubDComponentFilter::Topology::Unset; + ON_SubD::VertexTag m_vertex_tag_filter[4] = {}; + + bool m_bRejectEdges = false; + ON_SubDComponentFilter::Topology m_edge_topology_filter = ON_SubDComponentFilter::Topology::Unset; + ON_SubD::EdgeTag m_edge_tag_filter[2] = {}; + + bool m_bRejectFaces = false; + ON_SubDComponentFilter::Topology m_face_topology_filter = ON_SubDComponentFilter::Topology::Unset; + unsigned m_minimum_face_edge_count = 0U; + unsigned m_maximum_face_edge_count = 0U; +}; + #if defined(ON_COMPILING_OPENNURBS) /* diff --git a/opennurbs_subd_archive.cpp b/opennurbs_subd_archive.cpp index b0e615ca..6d69b795 100644 --- a/opennurbs_subd_archive.cpp +++ b/opennurbs_subd_archive.cpp @@ -1417,7 +1417,7 @@ bool ON_SubDimple::Write( { const_cast< ON_SubDHeap* >(&m_heap)->ClearArchiveId(); - const int minor_version = (archive.Archive3dmVersion() < 70) ? 0 : 1; + const int minor_version = (archive.Archive3dmVersion() < 70) ? 0 : 2; if ( !archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, minor_version) ) return ON_SUBD_RETURN_ERROR(false); bool rc = false; @@ -1471,6 +1471,10 @@ bool ON_SubDimple::Write( if (false == m_texture_mapping_tag.Write(archive)) break; + // minor version = 2 addtions + if (false == m_symmetry.Write(archive)) + break; + rc = true; break; } @@ -1555,6 +1559,12 @@ bool ON_SubDimple::Read( if (false == m_texture_mapping_tag.Read(archive)) break; + + if (minor_version >= 2) + { + if (false == m_symmetry.Read(archive)) + break; + } } rc = true; diff --git a/opennurbs_subd_copy.cpp b/opennurbs_subd_copy.cpp index 6e6936d1..88e9abf5 100644 --- a/opennurbs_subd_copy.cpp +++ b/opennurbs_subd_copy.cpp @@ -619,6 +619,8 @@ ON_SubD& ON_SubD::operator=(const ON_SubD& src) { this->Destroy(); this->CopyHelper(src); + // The next line copies user data + ON_Geometry::operator=(src); } return *this; } @@ -706,8 +708,9 @@ ON_SubDimple::ON_SubDimple(const ON_SubDimple& src) continue; if (nullptr == src_level->m_edge[0]) continue; - if (nullptr == src_level->m_face[0]) - continue; + // it's ok to have subd with just vertices and edges. + //NO//if (nullptr == src_level->m_face[0]) + //NO// continue; break; } @@ -732,6 +735,10 @@ ON_SubDimple::ON_SubDimple(const ON_SubDimple& src) m_max_face_id = src.m_max_face_id; m_subd_appearance = src.m_subd_appearance; + m_texture_domain_type = src.m_texture_domain_type; + m_texture_mapping_tag = src.m_texture_mapping_tag; + m_symmetry = src.m_symmetry; + ChangeContentSerialNumber(); } diff --git a/opennurbs_subd_data.cpp b/opennurbs_subd_data.cpp index 333483e6..96cd3dc3 100644 --- a/opennurbs_subd_data.cpp +++ b/opennurbs_subd_data.cpp @@ -832,6 +832,15 @@ bool ON_SubDimple::Transform( } + if (m_symmetry.IsSet()) + { + m_symmetry = m_symmetry.TransformConditionally(xform); + } + else + { + m_symmetry = ON_Symmetry::Unset; + } + return rc; } diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h index aa066e55..d3ae0565 100644 --- a/opennurbs_subd_data.h +++ b/opennurbs_subd_data.h @@ -469,22 +469,19 @@ void ON_SubDIncrementErrorCount(); // defined in opennurbs_subd.cpp // // m_saved_points_flags // -#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_FLAGS(cache_subd_flags) (ON_SUBD_CACHE_FLAGS_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_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)) +#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_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask)) +#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_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask)) +#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_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask)) ////////////////////////////////////////////////////////////////////////// @@ -1164,6 +1161,8 @@ public: void ClearEvaluationCache() const; + void ClearNeighborhoodEvaluationCache(const ON_SubDVertex * vertex, bool bTagChanged) const; + void ClearTopologicalAttributes() const { m_aggregates.ClearTopologicalAttributes(); @@ -1947,7 +1946,7 @@ public: double sin_angle_tolerance ); - ON_SubDEdgePtr MergeEdges( + ON_SubDEdgePtr MergeConsecutiveEdges( ON_SubDEdgePtr eptr0, ON_SubDEdgePtr eptr1 ); @@ -2097,6 +2096,12 @@ private: ); +private: + ON_Symmetry m_symmetry; + +public: + + private: ON_SubDimple& operator=(const ON_SubDimple&) = delete; diff --git a/opennurbs_subd_eval.cpp b/opennurbs_subd_eval.cpp index f188897f..c676af67 100644 --- a/opennurbs_subd_eval.cpp +++ b/opennurbs_subd_eval.cpp @@ -100,7 +100,7 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( sector_angle_radians = ON_UNSET_UINT_INDEX; } - const double smooth_edge_w0 = this->SectorWeight(); + const double smooth_edge_w0 = this->SectorCoefficient(); ON_SimpleArray< ON_SubDVertex* > V(R); ON_SimpleArray< ON_SubDEdge* > E(N); @@ -151,13 +151,13 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( else edge_tag_vei = ON_SubD::EdgeTag::Smooth; // interior edge - double w0 = (ON_SubD::EdgeTag::Smooth == edge_tag_vei) ? smooth_edge_w0 : ON_SubDSectorType::IgnoredSectorWeight; + double w0 = (ON_SubD::EdgeTag::Smooth == edge_tag_vei) ? smooth_edge_w0 : ON_SubDSectorType::IgnoredSectorCoefficient; unsigned int ev1i = 1 + vei*ring_ei_delta; E.Append( subd->AddEdgeWithSectorCoefficients( edge_tag_vei, V[0], w0, - V[ev1i], ON_SubDSectorType::IgnoredSectorWeight) + V[ev1i], ON_SubDSectorType::IgnoredSectorCoefficient) ); } @@ -178,8 +178,8 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD( f_edgeptr[0] = ON_SubDEdgePtr::Create(f_edge[0], 0); 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_edge[1] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[1], ON_SubDSectorType::IgnoredSectorCoefficient, f_vertex[2], ON_SubDSectorType::IgnoredSectorCoefficient); + f_edge[2] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[2], ON_SubDSectorType::IgnoredSectorCoefficient, f_vertex[3], ON_SubDSectorType::IgnoredSectorCoefficient); 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); @@ -431,10 +431,14 @@ double ON_SubDMatrix::TestEvaluation() const static bool GetSectorLimitPointHelper( const ON_SubDSectorIterator& sit, - bool bUndefinedNormalIsPossible, + bool& bUndefinedNormalIsPossible, ON_SubDSectorSurfacePoint& limit_point ) { + limit_point.m_limitP[0] = ON_DBL_QNAN; + limit_point.m_limitP[1] = ON_DBL_QNAN; + limit_point.m_limitP[2] = ON_DBL_QNAN; + const ON_SubDSectorType sector_type = ON_SubDSectorType::Create(sit); if (false == sector_type.IsValid()) return ON_SUBD_RETURN_ERROR(false); @@ -467,9 +471,32 @@ static bool GetSectorLimitPointHelper( if (R != SM.m_R || nullptr == SM.m_LP) break; + if ( + false == bUndefinedNormalIsPossible + && ON_SubD::VertexTag::Crease == SM.m_sector_type.VertexTag() + && R >= 5 + && *((const ON_3dPoint*)(point_ring+ point_ring_stride)) == *((const ON_3dPoint*)(point_ring + (R-1)* point_ring_stride)) + ) + { + // Crease where ends of the creased edges are equal. + // common overlapping creases (happens when a smooth interior edge is separated (unwelded) into two creases) + bUndefinedNormalIsPossible = true; + } + if (false == SM.EvaluateSurfacePoint(point_ring, R, point_ring_stride, bUndefinedNormalIsPossible, limit_point)) break; + if ( + false == bUndefinedNormalIsPossible + && 0.0 == limit_point.m_limitN[0] && 0.0 == limit_point.m_limitN[1] && 0.0 == limit_point.m_limitN[2] + && limit_point.IsSet(true) + ) + { + // SM.EvaluateSurfacePoint() logged the error - setting bUndefinedNormalIsPossible = true here + // allows the limit point to be cached so the same error doesn't continue to get logged. + bUndefinedNormalIsPossible = true; + } + rc = true; break; } @@ -591,19 +618,12 @@ bool ON_SubDVertex::GetSurfacePoint( // cache does not contain this limit point. } - if (nullptr == (nullptr == sector_face ? sit.Initialize(this) : sit.Initialize(sector_face, 0, this))) { limit_point = ON_SubDSectorSurfacePoint::Unset; return ON_SUBD_RETURN_ERROR(false); } - if (nullptr == sit.Initialize(sector_face,0,this)) - { - limit_point = ON_SubDSectorSurfacePoint::Unset; - return ON_SUBD_RETURN_ERROR(false); - } - limit_point_sector_face = sit.IncrementToCrease(-1); rc = GetSectorLimitPointHelper( sit, bUndefinedNormalIsPossible, limit_point); @@ -644,6 +664,117 @@ const ON_3dPoint ON_SubDVertex::SurfacePoint() const } + +const ON_Plane ON_SubDVertex::VertexFrame( + ON_SubDComponentLocation subd_appearance +) const +{ + if (0 == FaceCount()) + return ON_Plane::NanPlane; + + const ON_SubDFace* sector_face = Face(0); + if (nullptr == sector_face) + return ON_Plane::NanPlane; + + ON_Plane vertex_frame(ON_Plane::NanPlane); + if (ON_SubDComponentLocation::ControlNet == subd_appearance) + { + ON_3dVector V = ON_3dVector::ZeroVector; + for (int vei = 0; vei < m_edge_count; ++vei) + { + const ON_SubDEdge* e = Edge(vei); + if (nullptr == e) + continue; + const ON_SubDVertex* v1 = e->OtherEndVertex(this); + if (nullptr == v1) + continue; + const ON_SubDFace* f = (1 == e->m_face_count) ? e->Face(0) : nullptr; + if (nullptr == f) + continue; + sector_face = f; + V = (v1->ControlNetPoint() - ControlNetPoint()).UnitVector(); + break; + } + vertex_frame.CreateFromNormal(ControlNetPoint(), sector_face->ControlNetCenterNormal()); + const ON_3dVector X = (V - (V * vertex_frame.zaxis) * vertex_frame.zaxis).UnitVector(); + if (X.IsUnitVector()) + { + vertex_frame.xaxis = X; + vertex_frame.yaxis = ON_CrossProduct(vertex_frame.zaxis, vertex_frame.xaxis).UnitVector(); + } + } + else + { + // If this is a smooth vertex or a crease vertex on the boundary, + // then the sector_face does not matter. Otherwise it picks the + // "side of the crease" for the normal. + ON_SubDSectorSurfacePoint limit_point; + if (FaceCount()) + if (false == GetSurfacePoint(sector_face, limit_point)) + return ON_Plane::NanPlane; + + ON_3dVector Y(ON_CrossProduct(limit_point.m_limitN, limit_point.m_limitT1)); + Y.Unitize(); + + // The normal is more important than the tangent direction. + vertex_frame.CreateFromNormal(ON_3dPoint(limit_point.m_limitP), ON_3dVector(limit_point.m_limitN)); + vertex_frame.yaxis = Y; + vertex_frame.xaxis = ON_CrossProduct(vertex_frame.yaxis, vertex_frame.zaxis); + vertex_frame.xaxis.Unitize(); + } + + return vertex_frame.IsValid() ? vertex_frame : ON_Plane::NanPlane; +} + + +const ON_Plane ON_SubDEdge::CenterFrame( + ON_SubDComponentLocation subd_appearance +) const +{ + // to fix RH-41763, get the limit mesh fragment for an attached face + // and use subd.LimitSurfaceMesh().GetEdgeCenterPointAndNormal() + // to get P and N. + ON_Plane edge_frame(ON_Plane::NanPlane); + ON_3dPoint center_point(ON_3dPoint::NanPoint); + ON_3dVector center_normal(ON_3dVector::NanVector); + bool rc = false; + // surface center and normal are not available in public opennurbs + center_point = ControlNetCenterPoint(); + center_normal = ControlNetCenterNormal(0); + rc = center_point.IsValid() && center_normal.IsUnitVector(); + if (rc) + { + if (false == edge_frame.CreateFromNormal(center_point, center_normal)) + return ON_Plane::NanPlane; + const ON_3dVector U = ControlNetDirection(); + ON_2dVector v(U * edge_frame.xaxis, U * edge_frame.yaxis); + if ( v.Unitize() ) + { + if (fabs(v.y) > ON_SQRT_EPSILON&& fabs(v.x) < (1.0 - ON_SQRT_EPSILON)) + { + const ON_3dVector X = (v.x * edge_frame.xaxis + v.y * edge_frame.yaxis).UnitVector(); + if (X.IsUnitVector()) + { + const ON_3dVector Y = ON_CrossProduct(edge_frame.zaxis, X).UnitVector(); + if (Y.UnitVector()) + { + edge_frame.xaxis = X; + edge_frame.yaxis = Y; + } + } + } + else if (v.x < 0.0) + { + edge_frame.xaxis = -edge_frame.xaxis; + edge_frame.yaxis = -edge_frame.yaxis; + } + } + } + return edge_frame.IsValid() ? edge_frame : ON_Plane::NanPlane; +} + + + const ON_3dPoint ON_SubDVertex::Point(ON_SubDComponentLocation point_location) const { switch (point_location) @@ -823,10 +954,11 @@ void ON_SubDVertex::ClearSavedSurfacePoints() const { // clear vertex specific cache Internal_ClearSurfacePointFlag(); - if (ON_UNSET_VALUE != m_limit_point.m_limitP[0] && nullptr != m_limit_point.m_sector_face) + if (nullptr != m_limit_point.m_next_sector_limit_point) { // return multiple sector limit points to pool const ON_SubDSectorSurfacePoint* next_p = m_limit_point.m_next_sector_limit_point; + m_limit_point.m_next_sector_limit_point = nullptr; for (const ON_SubDSectorSurfacePoint* p = next_p; nullptr != p; p = next_p) { next_p = p->m_next_sector_limit_point; diff --git a/opennurbs_subd_fragment.cpp b/opennurbs_subd_fragment.cpp index d5424362..3d0c07e7 100644 --- a/opennurbs_subd_fragment.cpp +++ b/opennurbs_subd_fragment.cpp @@ -1201,13 +1201,17 @@ bool ON_SubDMeshFragment::Internal_GetFrameHelper( if (P.IsValid() && Z.IsNotZero()) { 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()) + const ON_3dVector V = (Q - P).UnitVector(); + const ON_3dVector X = (V - (Z*V)*Z).UnitVector(); + const ON_3dVector Y = ON_CrossProduct(Z, X).UnitVector(); + if ( X.IsUnitVector() && Y.IsUnitVector() + && fabs(X*Z) <= ON_SQRT_EPSILON + && fabs(Y*Z) <= ON_SQRT_EPSILON + ) { frame.origin = P; frame.xaxis = X; - frame.yaxis = ON_CrossProduct(Z, X); + frame.yaxis = Y; frame.zaxis = Z; frame.UpdateEquation(); } diff --git a/opennurbs_subd_frommesh.cpp b/opennurbs_subd_frommesh.cpp index b2983642..64f0486a 100644 --- a/opennurbs_subd_frommesh.cpp +++ b/opennurbs_subd_frommesh.cpp @@ -418,6 +418,13 @@ ON_SubD* ON_SubD::CreateFromMesh( i0++; i1--; } + // Flip middle edge if odd number of edges + if (i0 == i1) + { + int k = mesh_edges[i0].i; + mesh_edges[i0].i = mesh_edges[i0].j; + mesh_edges[i0].j = k; + } } // the ngon created a single subd face @@ -673,8 +680,8 @@ ON_SubD* ON_SubD::CreateFromMesh( // 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) - : new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorWeight, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorWeight); + ? new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorCoefficient, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorCoefficient) + : new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorCoefficient, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorCoefficient); mesh_edges[mesh_edge_map[i]].e = mesh_edge.e; for (i++; i < mesh_edges.UnsignedCount(); i++) { @@ -807,7 +814,7 @@ ON_SubD* ON_SubD::CreateFromMesh( if (tagged_end_index < 2) { // sector weight will be calculated when facet type is set - const_cast(edge)->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::UnsetSectorWeight; + const_cast(edge)->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::UnsetSectorCoefficient; } else if (2 == tagged_end_index) { @@ -817,8 +824,8 @@ ON_SubD* ON_SubD::CreateFromMesh( // first subdivision will convert edge to smooth 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; + const_cast(edge)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient; + const_cast(edge)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient; } else { diff --git a/opennurbs_subd_heap.cpp b/opennurbs_subd_heap.cpp index c9149389..ed3fd6ff 100644 --- a/opennurbs_subd_heap.cpp +++ b/opennurbs_subd_heap.cpp @@ -483,6 +483,11 @@ ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex( return v1; } +ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateEdgeSubdivisionVertex(bool bUseFindOrAllocate, const ON_SubDEdge* edge0) +{ + return bUseFindOrAllocate ? FindOrAllocateVertex(edge0) : AllocateVertex(edge0); +} + ON_SubDVertex * ON_SubD_FixedSizeHeap::FindOrAllocateVertex(const ON_SubDEdge * edge0) { if ( nullptr == edge0) @@ -590,6 +595,10 @@ ON_SubDVertex * ON_SubD_FixedSizeHeap::AllocateSectorFaceVertex(const ON_SubDFac return v1; } +const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge(bool bUseFindOrAllocatEdge, ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, double v1_sector_weight) +{ + return bUseFindOrAllocatEdge ? FindOrAllocateEdge( v0, v0_sector_weight, v1, v1_sector_weight) : AllocateEdge(v0, v0_sector_weight, v1, v1_sector_weight); +} const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::FindOrAllocateEdge(ON_SubDVertex * v0, double v0_sector_weight, ON_SubDVertex * v1, double v1_sector_weight) { @@ -641,7 +650,7 @@ const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge( if (ON_SubD::VertexTag::Smooth == v0->m_vertex_tag) { bTaggedVertex[0] = false; - v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + v0_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient; } else { @@ -658,7 +667,7 @@ const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge( if (ON_SubD::VertexTag::Smooth == v1->m_vertex_tag) { bTaggedVertex[1] = false; - v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + v1_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient; } else { @@ -666,18 +675,18 @@ const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge( if (bTaggedVertex[0] && bTaggedVertex[1]) { // crease edge - no weights - v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; - v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight; + v0_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient; + v1_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient; } } } else bTaggedVertex[1] = false; - if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v0_sector_weight, true)) + if ( false == ON_SubDSectorType::IsValidSectorCoefficientValue(v0_sector_weight, true)) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); - if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v1_sector_weight, true)) + if ( false == ON_SubDSectorType::IsValidSectorCoefficientValue(v1_sector_weight, true)) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); ON_SubDEdge* e = m_e + m_e_index; @@ -1779,7 +1788,6 @@ 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) diff --git a/opennurbs_subd_limit.cpp b/opennurbs_subd_limit.cpp index c4d86a50..0cc85b23 100644 --- a/opennurbs_subd_limit.cpp +++ b/opennurbs_subd_limit.cpp @@ -1705,7 +1705,7 @@ static double ON_SubDQuadFaceTopology_CopySectorWeight( return ON_SUBD_RETURN_ERROR(false); if (ON_SubD::EdgeTag::Smooth != e0->m_edge_tag && ON_SubD::EdgeTag::SmoothX != e0->m_edge_tag ) - return ON_SubDSectorType::IgnoredSectorWeight; + return ON_SubDSectorType::IgnoredSectorCoefficient; if (e0v == e0->m_vertex[0]) return e0->m_sector_coefficient[0]; @@ -1718,6 +1718,7 @@ static double ON_SubDQuadFaceTopology_CopySectorWeight( static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge( ON_SubD_FixedSizeHeap& fsh, + bool bUseFindOrAllocate, ON_SubDVertex* qv1, const ON_SubDVertex* qv0, const ON_SubDEdge* e0 @@ -1726,7 +1727,7 @@ static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge( if (nullptr == qv1 || nullptr == e0) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); - ON_SubDVertex* v1 = fsh.AllocateVertex(e0); + ON_SubDVertex* v1 = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate,e0); if (nullptr == v1) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); @@ -1737,20 +1738,20 @@ static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge( if ( qv1->m_vertex_tag != qv0->m_vertex_tag ) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); if ( ON_SubD::VertexTag::Smooth == qv0->m_vertex_tag) - v0_weight = ON_SubDSectorType::IgnoredSectorWeight; + v0_weight = ON_SubDSectorType::IgnoredSectorCoefficient; else { v0_weight = ON_SubDQuadFaceTopology_CopySectorWeight(e0, qv0); - if (false == ON_SubDSectorType::IsValidSectorWeightValue(v0_weight,false)) + if (false == ON_SubDSectorType::IsValidSectorCoefficientValue(v0_weight,false)) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); } } else - v0_weight = ON_SubDSectorType::IgnoredSectorWeight; + v0_weight = ON_SubDSectorType::IgnoredSectorCoefficient; - const double v1_weight = ON_SubDSectorType::IgnoredSectorWeight; + const double v1_weight = ON_SubDSectorType::IgnoredSectorCoefficient; - ON_SubDEdgePtr ep1 = fsh.AllocateEdge(qv1,v0_weight,v1,v1_weight); + ON_SubDEdgePtr ep1 = fsh.AllocateEdge(bUseFindOrAllocate,qv1,v0_weight,v1,v1_weight); ON_SubDEdge* e1 = ep1.Edge(); if (nullptr == e1) return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null); @@ -1796,9 +1797,9 @@ static ON_SubDFace* ON_SubDQuadFaceTopology_SubdivideFace( if ( nullptr == v[2]) return ON_SUBD_RETURN_ERROR(nullptr); - const double v1_weight = (ON_SubD::VertexTag::Crease == v[1]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorWeight; - 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; + const double v1_weight = (ON_SubD::VertexTag::Crease == v[1]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorCoefficient; + const double v2_weight = ON_SubDSectorType::IgnoredSectorCoefficient; + const double v3_weight = (ON_SubD::VertexTag::Crease == v[3]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorCoefficient; ON_SubDEdgePtr e12 = fsh.AllocateEdge(v[1],v1_weight,v[2],v2_weight); if ( nullptr == e12.Edge()) @@ -1833,7 +1834,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( // 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(2); + const double at_crease_weight = ON_SubDSectorType::CreaseSectorCoefficient(2); if ( m_fsh == q1ft->m_fsh) q1ft->m_fsh = nullptr; @@ -1892,6 +1893,9 @@ bool ON_SubDQuadNeighborhood::Subdivide( if (N < ON_SubDSectorType::MinimumSectorEdgeCount(qv0->m_vertex_tag)) return ON_SUBD_RETURN_ERROR(false); + // When qv0 is a valence 2 vertex with trianglar faces, we need to use find or allocate. + const bool bUseFindOrAllocate = (2 == N && 3 == qv0->MinimumFaceEdgeCount()); + ON_SubDSectorIterator sit; if ( nullptr == sit.Initialize(qf0,0,qv0) ) return ON_SUBD_RETURN_ERROR(false); @@ -1931,7 +1935,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( ON_SubDEdgePtr edge_grid1[4][2] = {}; // edge1 = new edge from qv1 to edge0 subdivision point - ON_SubDEdgePtr edgep1 = ON_SubDQuadFaceTopology_SubdivideEdge(fsh,qv1,qv0,edge0); + ON_SubDEdgePtr edgep1 = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, bUseFindOrAllocate,qv1,qv0,edge0); if (edgep1.IsNull()) return ON_SUBD_RETURN_ERROR(false); @@ -1955,7 +1959,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( if (edge0 == e0[1]) e1[1] = edgep1; // back to where we started else - e1[1] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, qv1, qv0, e0[1]); + e1[1] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, bUseFindOrAllocate, qv1, qv0, e0[1]); if (e1[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); @@ -2067,7 +2071,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( if (nullptr == e0[0]) return ON_SUBD_RETURN_ERROR(false); e1[1] = e1[0]; - e1[0] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, qv1, qv0, e0[0]); + e1[0] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, bUseFindOrAllocate, qv1, qv0, e0[0]); if (e1[0].IsNull()) return ON_SUBD_RETURN_ERROR(false); @@ -2107,7 +2111,9 @@ bool ON_SubDQuadNeighborhood::Subdivide( // Add the 7 remaining elements to vertex_grid1[][] if (false == bBoundaryCrease1[0]) { - vertex_grid1[3][0] = fsh.AllocateVertex(m_edge_grid[q0fvi][1]); + // When the level 0 vertex is valence 2 and the neignboring faces are triangles, + // this vertex needs to be added to the hash table + vertex_grid1[3][0] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, m_edge_grid[q0fvi][1]); if ( nullptr == vertex_grid1[3][0]) return ON_SUBD_RETURN_ERROR(false); } @@ -2116,7 +2122,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( if ( nullptr == vertex_grid1[3][1]) return ON_SUBD_RETURN_ERROR(false); - vertex_grid1[3][2] = fsh.AllocateVertex(qf0_edges[1]); + vertex_grid1[3][2] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, qf0_edges[1]); if ( nullptr == vertex_grid1[3][2]) return ON_SUBD_RETURN_ERROR(false); @@ -2124,7 +2130,7 @@ bool ON_SubDQuadNeighborhood::Subdivide( if ( nullptr == vertex_grid1[3][3]) return ON_SUBD_RETURN_ERROR(false); - vertex_grid1[2][3] = fsh.AllocateVertex(qf0_edges[2]); + vertex_grid1[2][3] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, qf0_edges[2]); if ( nullptr == vertex_grid1[2][3]) return ON_SUBD_RETURN_ERROR(false); @@ -2134,14 +2140,15 @@ bool ON_SubDQuadNeighborhood::Subdivide( if (false == bBoundaryCrease1[3]) { - vertex_grid1[0][3] = fsh.AllocateVertex(m_edge_grid[(q0fvi+3)%4][0]); + vertex_grid1[0][3] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, m_edge_grid[(q0fvi + 3) % 4][0]); if ( nullptr == vertex_grid1[0][3]) return ON_SUBD_RETURN_ERROR(false); } edge_grid1[1][0] = fsh.AllocateEdge( + bUseFindOrAllocate, vertex_grid1[2][1], - ON_SubDSectorType::IgnoredSectorWeight, + ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[3][1], ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[0], qf0_vertices[1]) ); @@ -2149,8 +2156,9 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); edge_grid1[1][1] = fsh.AllocateEdge( - vertex_grid1[2][2], - ON_SubDSectorType::IgnoredSectorWeight, + bUseFindOrAllocate, + vertex_grid1[2][2], + ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[3][2], at_crease_weight // ignored unless vertex_grid1[3][2] is tagged as a crease ); @@ -2158,8 +2166,9 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); edge_grid1[2][0] = fsh.AllocateEdge( - vertex_grid1[2][2], - ON_SubDSectorType::IgnoredSectorWeight, + bUseFindOrAllocate, + vertex_grid1[2][2], + ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[2][3], at_crease_weight // ignored unless vertex_grid1[2][3] is tagged as a crease ); @@ -2167,8 +2176,9 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); edge_grid1[2][1] = fsh.AllocateEdge( + bUseFindOrAllocate, vertex_grid1[1][2], - ON_SubDSectorType::IgnoredSectorWeight, + ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[1][3], ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[3], qf0_vertices[3]) ); @@ -2188,8 +2198,9 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); fedges[0] = fsh.AllocateEdge( + bUseFindOrAllocate, vertex_grid1[2][0], - ON_SubDSectorType::IgnoredSectorWeight, + ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[3][0], at_crease_weight // ignored unless vertex_grid1[3][0] is tagged as a crease ); @@ -2197,8 +2208,9 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); // m_edge_grid[q0fvi][1] fedges[1] = fsh.AllocateEdge( + bUseFindOrAllocate, vertex_grid1[3][0], - ON_SubDSectorType::IgnoredSectorWeight, + ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[3][1], ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[q0fvi][1], qf0_vertices[1]) ); @@ -2225,10 +2237,11 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); fedges[1] = fsh.AllocateEdge( + bUseFindOrAllocate, vertex_grid1[3][1], ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[1]), vertex_grid1[3][2], - ON_SubDSectorType::IgnoredSectorWeight + ON_SubDSectorType::IgnoredSectorCoefficient ); if (fedges[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); @@ -2255,8 +2268,9 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); fedges[1] = fsh.AllocateEdge( + bUseFindOrAllocate, vertex_grid1[3][2], - ON_SubDSectorType::IgnoredSectorWeight, + ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[3][3], ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[2]) ); @@ -2264,10 +2278,11 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); fedges[2] = fsh.AllocateEdge( + bUseFindOrAllocate, vertex_grid1[3][3], ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[2]), vertex_grid1[2][3], - ON_SubDSectorType::IgnoredSectorWeight + ON_SubDSectorType::IgnoredSectorCoefficient ); if (fedges[2].IsNull()) return ON_SUBD_RETURN_ERROR(false); @@ -2293,8 +2308,9 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); fedges[2] = fsh.AllocateEdge( + bUseFindOrAllocate, vertex_grid1[2][3], - ON_SubDSectorType::IgnoredSectorWeight, + ON_SubDSectorType::IgnoredSectorCoefficient, vertex_grid1[1][3], ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[3]) ); @@ -2322,19 +2338,21 @@ bool ON_SubDQuadNeighborhood::Subdivide( return ON_SUBD_RETURN_ERROR(false); fedges[1] = fsh.AllocateEdge( + bUseFindOrAllocate, vertex_grid1[1][3], ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[(q0fvi+3)%4][0], qf0_vertices[3]), vertex_grid1[0][3], - ON_SubDSectorType::IgnoredSectorWeight + ON_SubDSectorType::IgnoredSectorCoefficient ); if ( fedges[1].IsNull()) return ON_SUBD_RETURN_ERROR(false); fedges[2] = fsh.AllocateEdge( + bUseFindOrAllocate, vertex_grid1[0][3], at_crease_weight, // ingored unless vertex_grid1[0][3] is tagged as a crease vertex_grid1[0][2], - ON_SubDSectorType::IgnoredSectorWeight + ON_SubDSectorType::IgnoredSectorCoefficient ); if ( fedges[2].IsNull()) return ON_SUBD_RETURN_ERROR(false); @@ -2396,18 +2414,18 @@ bool ON_SubDQuadNeighborhood::Subdivide( if ( qv0->SurfacePointIsSet() && qv0->GetSurfacePoint(qv0_sector_face,limit_point) - && limit_point.IsSet(false) + && limit_point.IsSet(true) ) { limit_point.m_sector_face = qv1_sector_face; // qv1's sector face limit_point.m_next_sector_limit_point = nullptr; // sector checks are required to get "first" face - qv1->SetSavedSurfacePoint(false,limit_point); + qv1->SetSavedSurfacePoint(true,limit_point); } else if (qv1->GetSurfacePoint( qv1_sector_face, limit_point)) { limit_point.m_sector_face = qv0_sector_face; limit_point.m_next_sector_limit_point = nullptr; // sector checks are required to get "first" face - qv0->SetSavedSurfacePoint(false,limit_point); + qv0->SetSavedSurfacePoint(true,limit_point); } } @@ -2629,7 +2647,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( // 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(2); + const double at_crease2_weight = ON_SubDSectorType::CreaseSectorCoefficient(2); const unsigned int N = face->m_edge_count; @@ -2707,7 +2725,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( edge1 = m_fsh.AllocateEdge( center_vertex1, - ON_SubDSectorType::IgnoredSectorWeight, + ON_SubDSectorType::IgnoredSectorCoefficient, vertex1, at_crease2_weight // ingored unless vertex1 is tagged as a crease ); @@ -2766,7 +2784,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( edge1 = m_fsh.AllocateEdge( ring_vertex1[0], - ON_SubDSectorType::IgnoredSectorWeight, + ON_SubDSectorType::IgnoredSectorCoefficient, ring_vertex1[1], ON_SubDQuadFaceTopology_CopySectorWeight(prev_edge0, vertex0) ); @@ -2779,7 +2797,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( ring_vertex1[1], ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0), ring_vertex1[2], - ON_SubDSectorType::IgnoredSectorWeight + ON_SubDSectorType::IgnoredSectorCoefficient ); if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); @@ -2872,7 +2890,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( ring_vertex1[2], at_crease2_weight, // ingored unless ring_vertex1[0] is tagged as a crease vertex1, - ON_SubDSectorType::IgnoredSectorWeight + ON_SubDSectorType::IgnoredSectorCoefficient ); if ( edge1.IsNull()) return ON_SUBD_RETURN_ERROR(false); @@ -3061,7 +3079,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( return ON_SUBD_RETURN_ERROR(false); face1_eptrs[0] = edge1; - edge1 = m_fsh.FindOrAllocateEdge(face1_corners[1], at_crease2_weight, face1_corners[2], ON_SubDSectorType::IgnoredSectorWeight); + edge1 = m_fsh.FindOrAllocateEdge(face1_corners[1], at_crease2_weight, face1_corners[2], ON_SubDSectorType::IgnoredSectorCoefficient); if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); face1_eptrs[1] = edge1; @@ -3110,7 +3128,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1_quartet[1], 1); face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1_quartet[0], 0); - edge1 = m_fsh.FindOrAllocateEdge(face1_corners[2], ON_SubDSectorType::IgnoredSectorWeight, face1_corners[3], at_crease2_weight); + edge1 = m_fsh.FindOrAllocateEdge(face1_corners[2], ON_SubDSectorType::IgnoredSectorCoefficient, face1_corners[3], at_crease2_weight); if ( edge1.IsNull() ) return ON_SUBD_RETURN_ERROR(false); face1_eptrs[2] = edge1; @@ -3322,13 +3340,13 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper( face1_corners[1], at_crease2_weight, face1_corners[2], - ON_SubDSectorType::IgnoredSectorWeight + ON_SubDSectorType::IgnoredSectorCoefficient ); break; case 2: edge1 = m_fsh.FindOrAllocateEdge( face1_corners[2], - ON_SubDSectorType::IgnoredSectorWeight, + ON_SubDSectorType::IgnoredSectorCoefficient, face1_corners[3], at_crease2_weight ); diff --git a/opennurbs_subd_matrix.cpp b/opennurbs_subd_matrix.cpp index 56bad4df..9677bc3e 100644 --- a/opennurbs_subd_matrix.cpp +++ b/opennurbs_subd_matrix.cpp @@ -971,7 +971,7 @@ unsigned int ON_SubDSectorType::GetSubdivisionMatrix( if (N < 2 || R < 4) return ON_SUBD_RETURN_ERROR(0); - const double w = this->SectorWeight(); + const double w = this->SectorCoefficient(); double* x; double* y; diff --git a/opennurbs_subd_mesh.cpp b/opennurbs_subd_mesh.cpp index 506f0622..464836b1 100644 --- a/opennurbs_subd_mesh.cpp +++ b/opennurbs_subd_mesh.cpp @@ -1736,5 +1736,16 @@ void ON_SubD::ClearEvaluationCache() const } } +void ON_SubD::ClearNeighborhoodEvaluationCache(const ON_SubDVertex * vertex, bool bTagChanged) const +{ + const ON_SubDLevel* level = ActiveLevelConstPointer(); + + if (nullptr != level) + { + const_cast(this)->ChangeContentSerialNumberForExperts(); + level->ClearNeighborhoodEvaluationCache(vertex, bTagChanged); + } +} + //////////////////////////////////////////////////////////////////////////// diff --git a/opennurbs_subd_ring.cpp b/opennurbs_subd_ring.cpp index 789e2330..6b1af75a 100644 --- a/opennurbs_subd_ring.cpp +++ b/opennurbs_subd_ring.cpp @@ -467,7 +467,7 @@ static double Subdivide_CenterVertexSectorWeight( ) { if ( ON_SubD::EdgeTag::Crease == edge0->m_edge_tag) - return ON_SubDSectorType::IgnoredSectorWeight; + return ON_SubDSectorType::IgnoredSectorCoefficient; if (ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag || ON_SubD::EdgeTag::SmoothX == edge0->m_edge_tag) { if (vertex0 == edge0->m_vertex[0]) @@ -570,9 +570,9 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( 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(5-K) - : ON_SubDSectorType::IgnoredSectorWeight; - ON_SubDEdgePtr edge1 = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0,vertex0), vertex1, ON_SubDSectorType::IgnoredSectorWeight ); + ? ON_SubDSectorType::CreaseSectorCoefficient(5-K) + : ON_SubDSectorType::IgnoredSectorCoefficient; + ON_SubDEdgePtr edge1 = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0,vertex0), vertex1, ON_SubDSectorType::IgnoredSectorCoefficient ); if (edge1.IsNull()) return ON_SUBD_RETURN_ERROR(nullptr); edge1.Edge()->m_edge_tag = edge1_tag; @@ -607,7 +607,7 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( 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); + e1[K] = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0, vertex0), v1[K], ON_SubDSectorType::IgnoredSectorCoefficient); if (e1[K].IsNull()) return ON_SUBD_RETURN_ERROR(nullptr); e1[K].Edge()->m_edge_tag = edge1_tag; @@ -617,8 +617,8 @@ const ON_SubDVertex* ON_SubD::SubdivideSector( // 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); + e1[1] = fsh.AllocateEdge(v1[1], at_crease_weight, v1[2], ON_SubDSectorType::IgnoredSectorCoefficient); + e1[2] = fsh.AllocateEdge(v1[2], ON_SubDSectorType::IgnoredSectorCoefficient, 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) ) diff --git a/opennurbs_subd_sector.cpp b/opennurbs_subd_sector.cpp index c5b61534..59dbdce8 100644 --- a/opennurbs_subd_sector.cpp +++ b/opennurbs_subd_sector.cpp @@ -91,9 +91,9 @@ public: -double ON_SubDSectorType::SectorWeightCalculationError() +double ON_SubDSectorType::SectorCoefficientCalculationError() { - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient); } bool ON_SubDSectorType::IsValid() const @@ -112,28 +112,28 @@ bool ON_SubDSectorType::IsValid() const case ON_SubD::VertexTag::Smooth: if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle)) return ON_SUBD_RETURN_ERROR(false); - if (!(m_sector_weight == ON_SubDSectorType::IgnoredSectorWeight)) + if (!(m_sector_coefficient == ON_SubDSectorType::IgnoredSectorCoefficient)) return ON_SUBD_RETURN_ERROR(false); break; 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_sector_face_count))) + if (!(m_sector_coefficient == ON_SubDSectorType::CreaseSectorCoefficient(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 < ON_2PI)) return ON_SUBD_RETURN_ERROR(false); - if (!(m_sector_weight == ON_SubDSectorType::CornerSectorWeight(m_sector_face_count,m_corner_sector_angle_radians))) + if (!(m_sector_coefficient == ON_SubDSectorType::CornerSectorCoefficient(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_sector_face_count))) + if (!(m_sector_coefficient == ON_SubDSectorType::DartSectorCoefficient(m_sector_face_count))) return ON_SUBD_RETURN_ERROR(false); break; @@ -492,12 +492,12 @@ double ON_SubDSectorType::CornerSectorAngleRadiansFromEdges( -double ON_SubDSectorType::SectorWeightFromTheta( +double ON_SubDSectorType::SectorCoefficientFromTheta( double sector_theta ) { if (!(sector_theta > 0.0 && sector_theta <= ON_PI)) - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient); double cos_theta = cos(sector_theta); @@ -533,20 +533,20 @@ double ON_SubDSectorType::SectorWeightFromTheta( if (cos_theta <= -1.0) return wrange[0]; - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient); } -double ON_SubDSectorType::SectorWeight() const +double ON_SubDSectorType::SectorCoefficient() const { - return m_sector_weight; + return m_sector_coefficient; } -bool ON_SubDSectorType::IsValidSectorWeightValue( +bool ON_SubDSectorType::IsValidSectorCoefficientValue( double weight_value, - bool bAllowUnsetTaggedEndWeight + bool bAllowUnsetTaggedEndCoefficient ) { - return ((weight_value >= 0.0 && weight_value < 1.0) || (bAllowUnsetTaggedEndWeight && ON_SubDSectorType::UnsetSectorWeight == weight_value)); + return ((weight_value >= 0.0 && weight_value < 1.0) || (bAllowUnsetTaggedEndCoefficient && ON_SubDSectorType::UnsetSectorCoefficient == weight_value)); } bool ON_SubDSectorType::IsValidCornerSectorAngleRadians( @@ -575,32 +575,32 @@ double ON_SubDSectorType::ClampCornerSectorAngleRadians( } -double ON_SubDSectorType::SmoothSectorWeight() +double ON_SubDSectorType::SmoothSectorCoefficient() { - return ON_SubDSectorType::IgnoredSectorWeight; + return ON_SubDSectorType::IgnoredSectorCoefficient; } -double ON_SubDSectorType::CreaseSectorWeight( +double ON_SubDSectorType::CreaseSectorCoefficient( unsigned int sector_face_count ) { if (sector_face_count < 1) - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient); double sector_theta = ON_SubDSectorType::CreaseSectorTheta(sector_face_count); - return ON_SubDSectorType::SectorWeightFromTheta(sector_theta); + return ON_SubDSectorType::SectorCoefficientFromTheta(sector_theta); } -double ON_SubDSectorType::DartSectorWeight( +double ON_SubDSectorType::DartSectorCoefficient( unsigned int sector_face_count ) { if (sector_face_count < 2) - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient); double sector_theta = ON_SubDSectorType::DartSectorTheta(sector_face_count); - return ON_SubDSectorType::SectorWeightFromTheta(sector_theta); + return ON_SubDSectorType::SectorCoefficientFromTheta(sector_theta); } -double ON_SubDSectorType::CornerSectorWeight( +double ON_SubDSectorType::CornerSectorCoefficient( unsigned int sector_face_count, double corner_sector_angle_radians ) @@ -613,9 +613,9 @@ double ON_SubDSectorType::CornerSectorWeight( { const double sector_theta = ON_SubDSectorType::CornerSectorThetaFromCornerAngle(sector_face_count,corner_sector_angle_radians); if (sector_theta >= 0.0) - return ON_SubDSectorType::SectorWeightFromTheta(sector_theta); + return ON_SubDSectorType::SectorCoefficientFromTheta(sector_theta); } - return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight); + return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient); } static int CompareUnsinged(unsigned int a, unsigned int b) @@ -719,10 +719,10 @@ ON_SubDSectorType ON_SubDSectorType::CreateSmoothSectorType( = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) ? sector_face_count : 0; - st.m_sector_weight + st.m_sector_coefficient = (st.m_sector_face_count>0) - ? ON_SubDSectorType::IgnoredSectorWeight - : ON_SubDSectorType::UnsetSectorWeight; + ? ON_SubDSectorType::IgnoredSectorCoefficient + : ON_SubDSectorType::UnsetSectorCoefficient; st.m_sector_theta = (st.m_sector_face_count>0) ? ON_SubDSectorType::SmoothSectorTheta @@ -746,10 +746,10 @@ ON_SubDSectorType ON_SubDSectorType::CreateCreaseSectorType( = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) ? sector_face_count : 0; - st.m_sector_weight + st.m_sector_coefficient = (st.m_sector_face_count>0) - ? ON_SubDSectorType::CreaseSectorWeight(sector_face_count) - : ON_SubDSectorType::UnsetSectorWeight; + ? ON_SubDSectorType::CreaseSectorCoefficient(sector_face_count) + : ON_SubDSectorType::UnsetSectorCoefficient; st.m_sector_theta = (st.m_sector_face_count>0) ? ON_SubDSectorType::CreaseSectorTheta(sector_face_count) @@ -774,10 +774,10 @@ ON_SubDSectorType ON_SubDSectorType::CreateDartSectorType( = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) ? sector_face_count : 0; - st.m_sector_weight + st.m_sector_coefficient = (st.m_sector_face_count>0) - ? ON_SubDSectorType::DartSectorWeight(sector_face_count) - : ON_SubDSectorType::UnsetSectorWeight; + ? ON_SubDSectorType::DartSectorCoefficient(sector_face_count) + : ON_SubDSectorType::UnsetSectorCoefficient; st.m_sector_theta = (st.m_sector_face_count>0) ? ON_SubDSectorType::DartSectorTheta(sector_face_count) @@ -826,10 +826,10 @@ ON_SubDSectorType ON_SubDSectorType::CreateCornerSectorType( = ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count) ? sector_face_count : 0; - st.m_sector_weight + st.m_sector_coefficient = (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; + ? ON_SubDSectorType::CornerSectorCoefficient( sector_face_count, corner_sector_angle_radians) + : ON_SubDSectorType::UnsetSectorCoefficient; 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) diff --git a/opennurbs_subd_texture.cpp b/opennurbs_subd_texture.cpp index bcf9234b..03d345fa 100644 --- a/opennurbs_subd_texture.cpp +++ b/opennurbs_subd_texture.cpp @@ -377,7 +377,8 @@ bool ON_SubD::SetTextureCoordinatesFromFaceDomains() const { if (ON_SubDTextureDomainType::Unset == this->TextureDomainType()) { - if (false == SetTextureDomains(ON_SubDTextureDomainType::PerFace, false, false)) + // uset default to packed + if (false == SetTextureDomains(ON_SubDTextureDomainType::Packed, false, false)) return false; } ON_SubDFaceIterator fit(*this); diff --git a/opennurbs_symmetry.cpp b/opennurbs_symmetry.cpp new file mode 100644 index 00000000..aad1e515 --- /dev/null +++ b/opennurbs_symmetry.cpp @@ -0,0 +1,1086 @@ +/* $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 . +// +//////////////////////////////////////////////////////////////// +*/ + +#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_Symmetry::Type ON_Symmetry::SymmetryTypeFromUnsigned(unsigned int symmetry_type_as_unsigned) +{ + switch (symmetry_type_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::Reflect); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::Rotate); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::ReflectAndRotate); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::Inversion); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::Cyclic); + } + + ON_ERROR("Invalid type_as_unsigned parameter"); + return ON_Symmetry::Type::Unset; +} + +const ON_wString ON_Symmetry::SymmetryTypeToString(ON_Symmetry::Type symmetry_type) +{ + const wchar_t* s; + switch (symmetry_type) + { + case ON_Symmetry::Type::Unset: + s = L"Unset"; + break; + case ON_Symmetry::Type::Reflect: + s = L"Reflect"; + break; + case ON_Symmetry::Type::Rotate: + s = L"Rotate"; + break; + case ON_Symmetry::Type::ReflectAndRotate: + s = L"ReflectAndRotate"; + break; + case ON_Symmetry::Type::Inversion: + s = L"Inversion"; + break; + case ON_Symmetry::Type::Cyclic: + s = L"Cyclic"; + break; + default: + s = nullptr; + break; + } + return ON_wString(s); +} + + +ON_Symmetry::Coordinates ON_Symmetry::SymmetryCoordinatesFromUnsigned(unsigned int symmetry_coordinates_as_unsigned) +{ + switch (symmetry_coordinates_as_unsigned) + { + ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Coordinates::Unset); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Coordinates::Object); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Coordinates::World); + } + + ON_ERROR("Invalid symmetry_coordinates_as_unsigned parameter"); + return ON_Symmetry::Coordinates::Unset; +} + +const ON_wString ON_Symmetry::SymmetryCoordinatesToString(ON_Symmetry::Coordinates symmetry_coordinates) +{ + const wchar_t* s; + switch (symmetry_coordinates) + { + case ON_Symmetry::Coordinates::Unset: + s = L"Unset"; + break; + case ON_Symmetry::Coordinates::Object: + s = L"Object"; + break; + case ON_Symmetry::Coordinates::World: + s = L"World"; + break; + default: + s = nullptr; + break; + } + return ON_wString(s); +} + +bool ON_Symmetry::Write(ON_BinaryArchive& archive) const +{ + if (false == archive.BeginWrite3dmAnonymousChunk(2)) + return false; + + bool rc = false; + for (;;) + { + const ON_Symmetry::Type symmetry_type = IsSet() ? SymmetryType() : ON_Symmetry::Type::Unset; + const unsigned char utype = static_cast(symmetry_type); + if (false == archive.WriteChar(utype)) + break; + if (ON_Symmetry::Type::Unset == symmetry_type) + { + rc = true; + break; + } + if (false == archive.WriteInt(m_inversion_order)) + break; + if (false == archive.WriteInt(m_cyclic_order)) + break; + if (false == archive.WriteUuid(m_id)) + break; + + if (archive.BeginWrite3dmAnonymousChunk(1)) + { + switch (m_type) + { + case ON_Symmetry::Type::Unset: + break; + case ON_Symmetry::Type::Reflect: + rc = archive.WritePlaneEquation(m_reflection_plane); + break; + case ON_Symmetry::Type::Rotate: + rc = archive.WriteLine(m_rotation_axis); + break; + case ON_Symmetry::Type::ReflectAndRotate: + rc = archive.WritePlaneEquation(m_reflection_plane) && archive.WriteLine(m_rotation_axis); + break; + case ON_Symmetry::Type::Inversion: + rc = archive.WriteXform(m_inversion_transform); + break; + case ON_Symmetry::Type::Cyclic: + rc = archive.WriteXform(m_cyclic_transform); + break; + default: + ON_ERROR("You added a new enum value but failed to update archive IO code."); + break; + } + if (false == archive.EndWrite3dmChunk()) + rc = false; + } + + // ON_Symmetry::Coordinates added Dec 16, 2019 chunk version 2 + const ON_Symmetry::Coordinates symmetry_coordinates = IsSet() ? SymmetryCoordinates() : ON_Symmetry::Coordinates::Unset; + const unsigned char ucoordinates = static_cast(symmetry_coordinates); + if (false == archive.WriteChar(ucoordinates)) + break; + + break; + } + if (false == archive.EndWrite3dmChunk()) + rc = false; + return rc; +} + + +bool ON_Symmetry::Read(ON_BinaryArchive& archive) +{ + *this = ON_Symmetry::Unset; + + int chunk_version = 0; + if (false == archive.BeginRead3dmAnonymousChunk(&chunk_version)) + return false; + + ON_Symmetry::Type symmetry_type = ON_Symmetry::Type::Unset; + unsigned int inversion_order = 0; + unsigned int cyclic_order = 0; + ON_UUID symmetry_id = ON_nil_uuid; + ON_Xform inversion_transform = ON_Xform::Nan; + ON_Xform cyclic_transform = ON_Xform::Nan; + ON_PlaneEquation reflection_plane = ON_PlaneEquation::NanPlaneEquation; + ON_Line rotation_axis = ON_Line::NanLine; + + bool rc = false; + for (;;) + { + if (chunk_version <= 0) + break; + unsigned char utype = 0; + if (false == archive.ReadChar(&utype)) + break; + symmetry_type = ON_Symmetry::SymmetryTypeFromUnsigned(utype); + if (ON_Symmetry::Type::Unset == symmetry_type) + { + rc = true; + break; + } + + + if (false == archive.ReadInt(&inversion_order)) + break; + if (false == archive.ReadInt(&cyclic_order)) + break; + if (false == archive.ReadUuid(symmetry_id)) + break; + + int inner_chunk_version = 0; + if (false == archive.BeginRead3dmAnonymousChunk(&inner_chunk_version)) + break; + + ON_Symmetry symmetry; + + ON_Symmetry::Coordinates symmetry_coordinates = ON_Symmetry::Coordinates::Object; + + switch (symmetry_type) + { + case ON_Symmetry::Type::Unset: + break; + + case ON_Symmetry::Type::Reflect: + rc = archive.ReadPlaneEquation(reflection_plane); + if (rc) + symmetry = ON_Symmetry::CreateReflectSymmetry(reflection_plane, symmetry_coordinates); + break; + + case ON_Symmetry::Type::Rotate: + rc = archive.ReadLine(rotation_axis); + if (rc) + symmetry = ON_Symmetry::CreateRotateSymmetry(rotation_axis, cyclic_order, symmetry_coordinates); + break; + + case ON_Symmetry::Type::ReflectAndRotate: + rc = archive.ReadPlaneEquation(reflection_plane) && archive.ReadLine(rotation_axis); + if (rc) + symmetry = ON_Symmetry::CreateReflectAndRotateSymmetry(reflection_plane, rotation_axis, cyclic_order, symmetry_coordinates); + break; + + case ON_Symmetry::Type::Inversion: + rc = archive.ReadXform(inversion_transform); + if (rc) + symmetry = ON_Symmetry::CreateInversionSymmetry(symmetry_id, inversion_transform, symmetry_coordinates); + break; + + case ON_Symmetry::Type::Cyclic: + rc = archive.ReadXform(cyclic_transform); + if (rc) + symmetry = ON_Symmetry::CreateCyclicSymmetry(symmetry_id, cyclic_transform, cyclic_order, symmetry_coordinates); + break; + + default: + // Old code reading a file containing a future type. + symmetry_type = ON_Symmetry::Type::Unset; + rc = true; // means no media reading error + break; + } + + if ( + rc + && ON_Symmetry::Type::Unset != symmetry_type + && symmetry.SymmetryType() == symmetry_type + && symmetry.InversionOrder() == inversion_order + && symmetry.CyclicOrder() == cyclic_order + && symmetry.SymmetryId() == symmetry_id + ) + { + *this = symmetry; + } + + if (false == archive.EndRead3dmChunk()) + rc = false; + + if (chunk_version < 2) + break; + + unsigned char ucoordinates = 0; + rc = archive.ReadChar(&ucoordinates); + if (false == rc) + break; + symmetry_coordinates = ON_Symmetry::SymmetryCoordinatesFromUnsigned(ucoordinates); + if (ON_Symmetry::Coordinates::Unset != symmetry_coordinates && m_coordinates != symmetry_coordinates) + m_coordinates = symmetry_coordinates; + + break; + } + + if (false == archive.EndRead3dmChunk()) + rc = false; + + return rc; +} + +void ON_PlaneEquation::Dump(class ON_TextLog& text_log) const +{ + // print -0 as 0. + double c[4] = { (0.0==x) ? 0.0 : x,(0.0 == y) ? 0.0 : y,(0.0 == z) ? 0.0 : z,(0.0 == d) ? 0.0 : d }; + for (int i = 0; i < 3; ++i) + { + if (false == (0.0 != c[i] && 0.0 == c[(i + 1) % 3] && 0.0 == c[(i + 2) % 3]) ) + continue; + const char* coord = (0 == i) ? "x" : ((1 == i) ? "y" : "z"); + if (0.0 == c[3]) + text_log.Print(L"%s = 0", coord); + else if (1.0 == c[i]) + text_log.Print(L"%s = %g", coord, -c[3]); + else + text_log.Print(L"%g*%s = %g", c[i] , coord, -c[3]); + return; + } + + // general case + text_log.Print(L"%g*x + %g*y + %g*z + %g = 0", c[0], c[1], c[2], c[3]); +} + +void ON_Symmetry::Dump(ON_TextLog& text_log) const +{ + const ON_wString type = ON_Symmetry::SymmetryTypeToString(m_type); + const ON_wString coordinates = ON_Symmetry::SymmetryCoordinatesToString(m_coordinates); + text_log.Print(L"%ls %ls symmetry\n",static_cast(type), static_cast(coordinates)); + if (IsUnset()) + return; + + text_log.Print(L"Motif count: %u\n", MotifCount()); + + switch (m_type) + { + case ON_Symmetry::Type::Unset: + break; + + case ON_Symmetry::Type::Reflect: + { + const ON_PlaneEquation e = ReflectionPlane(); + text_log.Print(L"plane: "); + ReflectionPlane().Dump(text_log); + text_log.PrintNewLine(); + } + break; + + case ON_Symmetry::Type::Rotate: + { + text_log.Print(L"rotation count: %u (%g degrees)\n", RotationCount(), RotationAngleDegrees()); + const ON_Line axis = RotationAxis(); + text_log.Print(L"axis: "); + text_log.Print(axis.from); + text_log.Print(L", "); + text_log.Print(axis.to); + text_log.PrintNewLine(); + } + break; + + case ON_Symmetry::Type::ReflectAndRotate: + { + const ON_PlaneEquation e = ReflectionPlane(); + text_log.Print(L"plane: "); + ReflectionPlane().Dump(text_log); + text_log.PrintNewLine(); + text_log.Print(L"rotation count: %u (%g degrees)\n", RotationCount(), RotationAngleDegrees()); + const ON_Line axis = RotationAxis(); + text_log.Print(L"axis: "); + text_log.Print(axis.from); + text_log.Print(L", "); + text_log.Print(axis.to); + text_log.PrintNewLine(); + } + break; + + case ON_Symmetry::Type::Inversion: + { + const ON_Line line = RotationAxis(); + text_log.Print(InversionTransform()); + text_log.PrintNewLine(); + } + break; + + case ON_Symmetry::Type::Cyclic: + { + const ON_Line line = RotationAxis(); + text_log.Print(CyclicTransform()); + text_log.PrintNewLine(); + } + break; + + default: + break; + } +} + +const ON_Symmetry ON_Symmetry::TransformConditionally(const ON_Xform& xform) const +{ + return + (ON_Symmetry::Coordinates::Object == SymmetryCoordinates()) + ? ON_Symmetry::TransformUnconditionally(xform) + : ON_Symmetry(*this); +} + +const ON_Symmetry ON_Symmetry::TransformUnconditionally(const ON_Xform& xform) const +{ + switch (m_type) + { + case ON_Symmetry::Type::Unset: + break; + + case ON_Symmetry::Type::Reflect: + { + if (false == m_reflection_plane.IsValid()) + break; + ON_PlaneEquation e = m_reflection_plane; + e.Transform(xform); + if (false == e.IsValid()) + break; + return ON_Symmetry::CreateReflectSymmetry(e, m_coordinates); + } + break; + + case ON_Symmetry::Type::Rotate: + { + if (false == m_rotation_axis.IsValid()) + break; + ON_Line a = m_rotation_axis; + a.Transform(xform); + if (false == a.IsValid()) + break; + return ON_Symmetry::CreateRotateSymmetry(a, RotationCount(), m_coordinates); + } + break; + + case ON_Symmetry::Type::ReflectAndRotate: + { + if (false == m_reflection_plane.IsValid()) + break; + if (false == m_rotation_axis.IsValid()) + break; + ON_PlaneEquation e = m_reflection_plane; + e.Transform(xform); + if (false == e.IsValid()) + break; + ON_Line a = m_rotation_axis; + a.Transform(xform); + if (false == a.IsValid()) + break; + return ON_Symmetry::CreateReflectAndRotateSymmetry(e, a, RotationCount(), m_coordinates); + } + break; + + case ON_Symmetry::Type::Inversion: + { + const ON_Xform xform_inverse = xform.Inverse(); + const ON_Xform inversion_xform = xform * InversionTransform()*xform_inverse; + return ON_Symmetry::CreateInversionSymmetry(SymmetryId(), inversion_xform, m_coordinates); + } + break; + + case ON_Symmetry::Type::Cyclic: + { + const ON_Xform xform_inverse = xform.Inverse(); + const ON_Xform cyclic_xform = xform * CyclicTransform()*xform_inverse; + return ON_Symmetry::CreateCyclicSymmetry(SymmetryId(), cyclic_xform, CyclicOrder(), m_coordinates); + } + break; + + default: + break; + } + return ON_Symmetry::Unset; +} + +const ON_Symmetry ON_Symmetry::CreateInversionSymmetry( + ON_UUID symmetry_id, + ON_Xform inversion_transform, + ON_Symmetry::Coordinates symmetry_coordinates +) +{ + for (;;) + { + if (false == inversion_transform.IsValid()) + break; + + const double det = inversion_transform.Determinant(); + if (false == (det < 0.0)) + break; + + ON_Xform x = inversion_transform* inversion_transform; + if (false == x.IsIdentity(ON_Symmetry::ZeroTolerance)) + break; + + if (false == (ON_nil_uuid == symmetry_id) ) + { + // prohibit using built-in ids + if (ON_Symmetry::ReflectId == symmetry_id) + break; + if (ON_Symmetry::RotateId == symmetry_id) + break; + if (ON_Symmetry::ReflectAndRotateId == symmetry_id) + break; + } + + ON_Symmetry symmetry; + symmetry.m_type = ON_Symmetry::Type::Cyclic; + symmetry.m_coordinates = symmetry_coordinates; + symmetry.m_inversion_order = 2; + symmetry.m_cyclic_order = 1; + symmetry.m_id = symmetry_id; + symmetry.m_inversion_transform = inversion_transform; + symmetry.m_cyclic_transform = ON_Xform::IdentityTransformation; + return symmetry; + } + + return ON_Symmetry::Unset; +} + + +const ON_Symmetry ON_Symmetry::CreateCyclicSymmetry( + ON_UUID symmetry_id, + ON_Xform cyclic_transform, + unsigned int cyclic_order, + ON_Symmetry::Coordinates symmetry_coordinates +) +{ + for (;;) + { + if (cyclic_order < 2) + break; + if (cyclic_order > ON_Symmetry::MaximumOrder) + break; + if (false == cyclic_transform.IsValid()) + break; + + const double det = cyclic_transform.Determinant(); + if (2 == cyclic_order || 1 == (cyclic_order % 2)) + { + if (false == (det > 0.0)) + break; + } + else + { + if (false == (det != 0.0)) + break; + } + + unsigned n = 1; + ON_Xform x = cyclic_transform; + while (n < cyclic_order && x.IsValid() && false == x.IsIdentity(ON_Symmetry::ZeroTolerance)) + { + x = cyclic_transform * x; + ++n; + } + if (n != cyclic_order) + break; + if (false == x.IsIdentity(ON_Symmetry::ZeroTolerance)) + break; + + if (false == (ON_nil_uuid == symmetry_id)) + { + // prohibit using built-in ids + if (ON_Symmetry::ReflectId == symmetry_id) + break; + if (ON_Symmetry::RotateId == symmetry_id) + break; + if (ON_Symmetry::ReflectAndRotateId == symmetry_id) + break; + } + + ON_Symmetry symmetry; + symmetry.m_type = ON_Symmetry::Type::Cyclic; + symmetry.m_coordinates = symmetry_coordinates; + symmetry.m_inversion_order = 1; + symmetry.m_cyclic_order = cyclic_order; + symmetry.m_id = symmetry_id; + symmetry.m_inversion_transform = ON_Xform::IdentityTransformation; + symmetry.m_cyclic_transform = cyclic_transform; + return symmetry; + } + + return ON_Symmetry::Unset; +} + +const ON_Symmetry ON_Symmetry::CreateReflectSymmetry( + ON_PlaneEquation reflection_plane, + ON_Symmetry::Coordinates symmetry_coordinates +) +{ + for (;;) + { + if (false == reflection_plane.IsValid()) + break; + const ON_Xform xform(ON_Xform::MirrorTransformation(reflection_plane)); + ON_Symmetry symmetry = ON_Symmetry::CreateInversionSymmetry(ON_nil_uuid, xform, symmetry_coordinates); + if (ON_Symmetry::Type::Cyclic != symmetry.m_type) + break; + symmetry.m_type = ON_Symmetry::Type::Reflect; + symmetry.m_coordinates = symmetry_coordinates; + symmetry.m_id = ON_Symmetry::ReflectId; + symmetry.m_reflection_plane = reflection_plane; + return symmetry; + } + return ON_Symmetry::Unset; +} + +const ON_Symmetry ON_Symmetry::CreateRotateSymmetry( + ON_Line rotation_axis, + unsigned int rotation_count, + ON_Symmetry::Coordinates symmetry_coordinates +) +{ + for (;;) + { + if (rotation_count < 2 || rotation_count > ON_Symmetry::MaximumOrder) + break; + if (false == rotation_axis.IsValid()) + break; + const ON_Xform R = Internal_RotationXform(rotation_axis, 1, rotation_count); + ON_Symmetry symmetry = ON_Symmetry::CreateCyclicSymmetry(ON_nil_uuid, R, rotation_count, symmetry_coordinates); + if (ON_Symmetry::Type::Cyclic != symmetry.m_type) + break; + symmetry.m_type = ON_Symmetry::Type::Rotate; + symmetry.m_coordinates = symmetry_coordinates; + symmetry.m_id = ON_Symmetry::RotateId; + symmetry.m_rotation_axis = rotation_axis; + return symmetry; + } + return ON_Symmetry::Unset; +} + + +const ON_Symmetry ON_Symmetry::CreateReflectAndRotateSymmetry( + ON_PlaneEquation reflection_plane, + ON_Line rotation_axis, + unsigned int rotation_count, + ON_Symmetry::Coordinates symmetry_coordinates +) +{ + for (;;) + { + if (false == reflection_plane.IsValid()) + break; + if (false == rotation_axis.IsValid()) + break; + + // rotation axis must be in the reflection plane + const double h0 = reflection_plane.ValueAt(rotation_axis.from); + const double h1 = reflection_plane.ValueAt(rotation_axis.to); + if (false == (fabs(h0) <= ON_ZERO_TOLERANCE)) + break; + if (false == (fabs(h1) <= ON_ZERO_TOLERANCE)) + break; + + const ON_Symmetry reflection = CreateReflectSymmetry(reflection_plane, symmetry_coordinates); + if (ON_Symmetry::Type::Reflect != reflection.SymmetryType()) + break; + const ON_Symmetry rotation = CreateRotateSymmetry(rotation_axis,rotation_count, symmetry_coordinates); + if (ON_Symmetry::Type::Rotate != rotation.SymmetryType()) + break; + + ON_Symmetry symmetry; + symmetry.m_type = ON_Symmetry::Type::ReflectAndRotate; + symmetry.m_coordinates = symmetry_coordinates; + symmetry.m_inversion_order = reflection.m_inversion_order; + symmetry.m_cyclic_order = rotation.m_cyclic_order; + symmetry.m_id = ON_Symmetry::ReflectAndRotateId; + symmetry.m_inversion_transform = reflection.m_inversion_transform; + symmetry.m_cyclic_transform = rotation.m_cyclic_transform; + symmetry.m_reflection_plane = reflection.m_reflection_plane; + symmetry.m_rotation_axis = rotation.m_rotation_axis; + return symmetry; + } + return ON_Symmetry::Unset; +} + +int ON_Symmetry::Internal_CompareDouble(const double* lhs, const double* rhs, size_t count) +{ + if (lhs == rhs) + return 0; + if (nullptr == lhs) + return 1; + if (nullptr == rhs) + return -1; + for (size_t i = 0; i < count; ++i) + { + const double x = lhs[i]; + const double y = rhs[i]; + if (x < y) + return -1; + if (x > y) + return 1; + const bool xok = (x == x) ? true : false; + const bool yok = (y == y) ? true : false; + if (xok == yok) + continue; + if (false == xok) + return 1; // lhs is a nan + if (false == yok) + return -1; // rhs is a nan + } + return 0; +} + +int ON_Symmetry::Compare(const ON_Symmetry* lhs, const ON_Symmetry* rhs) +{ + if (lhs == rhs) + return 0; + + // sort nulls to end + if (nullptr == lhs) + return 1; + if (nullptr == rhs) + return -1; + + if (static_cast(lhs->m_type) < static_cast(rhs->m_type)) + return -1; + if (static_cast(lhs->m_type) > static_cast(rhs->m_type)) + return 1; + if (ON_Symmetry::Type::Unset == lhs->m_type) + return 0; + + if (static_cast(lhs->m_coordinates) < static_cast(rhs->m_coordinates)) + return -1; + if (static_cast(lhs->m_coordinates) > static_cast(rhs->m_coordinates)) + return 1; + + + if (lhs->m_inversion_order < rhs->m_inversion_order) + return -1; + if (lhs->m_inversion_order > rhs->m_inversion_order) + return 1; + + if (lhs->m_cyclic_order < rhs->m_cyclic_order) + return -1; + if (lhs->m_cyclic_order > rhs->m_cyclic_order) + return 1; + + if (0U == lhs->m_inversion_order || 0U == lhs->m_cyclic_order) + return 0; + + if (ON_Symmetry::Type::Reflect == lhs->m_type || ON_Symmetry::Type::ReflectAndRotate == lhs->m_type ) + { + const int rc = ON_Symmetry::Internal_CompareDouble(&lhs->m_reflection_plane.x, &rhs->m_reflection_plane.x, 4); + if (0 != rc) + return rc; + } + + if (ON_Symmetry::Type::Rotate == lhs->m_type || ON_Symmetry::Type::ReflectAndRotate == lhs->m_type) + { + const int rc = ON_Symmetry::Internal_CompareDouble(&lhs->m_rotation_axis.from.x, &rhs->m_rotation_axis.from.x, 6); + if (0 != rc) + return rc; + } + + if ( + ON_Symmetry::Type::Reflect == lhs->m_type + || ON_Symmetry::Type::Rotate == lhs->m_type + || ON_Symmetry::Type::ReflectAndRotate == lhs->m_type + ) + return 0; + + if (lhs->m_inversion_order > 1) + { + const int rc = ON_Symmetry::Internal_CompareDouble(&lhs->m_inversion_transform.m_xform[0][0], &rhs->m_inversion_transform.m_xform[0][0], 16); + if (0 != rc) + return rc; + } + + if (lhs->m_cyclic_order > 1) + { + const int rc = ON_Symmetry::Internal_CompareDouble(&lhs->m_inversion_transform.m_xform[0][0], &rhs->m_inversion_transform.m_xform[0][0], 16); + if (0 != rc) + return rc; + } + + return 0; +} + +ON_Symmetry::Type ON_Symmetry::SymmetryType() const +{ + return m_type; +} + +ON_Symmetry::Coordinates ON_Symmetry::SymmetryCoordinates() const +{ + return m_coordinates; +} + +const ON_UUID ON_Symmetry::SymmetryId() const +{ + return m_id; +} + +void ON_Symmetry::Clear() +{ + *this = ON_Symmetry::Unset; +} + +bool ON_Symmetry::IsSet() const +{ + return + ON_Symmetry::Type::Unset != m_type + && (1 == m_inversion_order || 2 == m_inversion_order) + && m_cyclic_order >= 1 + && MotifCount() >= 2 + ; +} + +bool ON_Symmetry::IsUnset() const +{ + return (false == IsSet()); +} + +unsigned int ON_Symmetry::MotifCount() const +{ + return InversionOrder()*CyclicOrder(); +} + + +unsigned int ON_Symmetry::InversionOrder() const +{ + return m_inversion_order; +} + + +unsigned int ON_Symmetry::CyclicOrder() const +{ + return m_cyclic_order; +} + +const ON_Xform ON_Symmetry::InversionTransform() const +{ + return IsSet() ? m_inversion_transform : ON_Xform::Nan; +} + +const ON_Xform ON_Symmetry::CyclicTransform() const +{ + return IsSet() ? m_cyclic_transform : ON_Xform::Nan; +} + +const ON_SHA1_Hash ON_Symmetry::Hash() const +{ + for(;;) + { + if (false == IsSet()) + break; + + ON_SHA1 sha1; + + const unsigned char t = static_cast(m_type); + sha1.AccumulateBytes(&t, sizeof(t)); + + const unsigned char c = static_cast(m_coordinates); + sha1.AccumulateBytes(&c, sizeof(c)); + + sha1.AccumulateInteger32(InversionOrder()); + sha1.AccumulateInteger32(CyclicOrder()); + + if (ON_Symmetry::Type::Reflect == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type) + sha1.AccumulateDoubleArray(4, &m_reflection_plane.x); + + if (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type) + sha1.AccumulateDoubleArray(6, &m_rotation_axis.from.x); + + if (ON_Symmetry::Type::Reflect != m_type && ON_Symmetry::Type::Rotate != m_type && ON_Symmetry::Type::ReflectAndRotate != m_type) + { + if (InversionOrder() > 1) + sha1.AccumulateDoubleArray(16, &m_inversion_transform.m_xform[0][0]); + if (CyclicOrder() > 1) + sha1.AccumulateDoubleArray(16, &m_cyclic_transform.m_xform[0][0]); + } + return sha1.Hash(); + } + + return ON_SHA1_Hash::EmptyContentHash; +} + +const ON_PlaneEquation ON_Symmetry::ReflectionPlane() const +{ + return (ON_Symmetry::Type::Reflect == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type) + ? m_reflection_plane + : ON_PlaneEquation::NanPlaneEquation; +} + +const ON_Line ON_Symmetry::RotationAxis() const +{ + return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type) + ? m_rotation_axis + : ON_Line::NanLine; +} + +const ON_3dPoint ON_Symmetry::RotationAxisPoint() const +{ + return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type) + ? m_rotation_axis.from + : ON_3dPoint::NanPoint; +} + +const ON_3dVector ON_Symmetry::RotationAxisDirection() const +{ + return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type) + ? m_rotation_axis.Direction() + : ON_3dVector::NanVector; +} + +const ON_3dVector ON_Symmetry::RotationAxisTangent() const +{ + return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type) + ? m_rotation_axis.Tangent() + : ON_3dVector::NanVector; +} + +unsigned int ON_Symmetry::RotationCount() const +{ + return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type) + ? m_cyclic_order + : 0U; +} + +double ON_Symmetry::RotationAngleDegrees() const +{ + return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type) + ? (360.0 / ((double)RotationCount())) + : ON_DBL_QNAN; +} + +double ON_Symmetry::RotationAngleRadians() const +{ + return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type) + ? ((2.0*ON_PI) / ((double)RotationCount())) + : ON_DBL_QNAN; +} + +const ON_Xform ON_Symmetry::Internal_RotationXform( + int rotation_index, + int rotation_count +) const +{ + if (rotation_index < 0 || rotation_index >= rotation_count) + return ON_Xform::Nan; + if (0 == rotation_index) + return ON_Xform::IdentityTransformation; + if (1 == rotation_index) + return m_cyclic_transform; + + return ON_Symmetry::Internal_RotationXform(m_rotation_axis, rotation_index, rotation_count); +} + +const ON_Xform ON_Symmetry::Internal_RotationXform( + ON_Line rotation_axis, + int rotation_index, + int rotation_count +) +{ + if (rotation_index < 0 || rotation_index >= rotation_count) + return ON_Xform::Nan; + if (0 == rotation_index) + return ON_Xform::IdentityTransformation; + + // calculate from trig functions for maximum precision + double sin_sign = 1.0; + if (2 * rotation_index > rotation_count) + { + rotation_index = rotation_count - rotation_index; + sin_sign = -1.0; + } + + double cos_angle = ON_DBL_QNAN; + double sin_angle = ON_DBL_QNAN; + + if (2 * rotation_index == rotation_count) + { + // angle = pi + sin_angle = 0.0; + cos_angle = -1.0; + } + else if (4 * rotation_index == rotation_count) + { + // angle = pi/2 + sin_angle = 1.0; + cos_angle = 0.0; + } + else if (6 * rotation_index == rotation_count) + { + // angle = pi/3 + sin_angle = 0.5*sqrt(3.0); + cos_angle = 0.5; + } + else if (8 * rotation_index == rotation_count) + { + // angle = pi/4 + sin_angle = cos_angle = 1.0 / sqrt(2.0); + } + else if (12 * rotation_index == rotation_count) + { + // angle = pi/3 + sin_angle = 0.5; + cos_angle = 0.5*sqrt(3.0); + } + else + { + const double a = (rotation_index*(2.0*ON_PI)) / ((double)rotation_count); + sin_angle = sin(a); + cos_angle = cos(a); + } + + ON_Xform r; + r.Rotation(sin_sign*sin_angle, cos_angle, rotation_axis.Direction(), rotation_axis.from); + return r; +} + +const ON_Xform ON_Symmetry::MotifTransformation( + int index +) const +{ + const int count = MotifCount(); + if ( count <= 1) + return ON_Xform::Nan; + + // convert index to be >= 0 + index = ((index % count) + count) % count; + + ON_Xform x = ON_Xform::Nan; + switch (m_type) + { + case ON_Symmetry::Type::Unset: + break; + + case ON_Symmetry::Type::Reflect: + x = (0 == index) + ? ON_Xform::IdentityTransformation + : m_inversion_transform; + break; + + case ON_Symmetry::Type::Rotate: + x = Internal_RotationXform(index, count); + break; + + case ON_Symmetry::Type::ReflectAndRotate: + if (0 == index) + x = ON_Xform::IdentityTransformation; + else if (1 == index) + x = m_inversion_transform; + else if (2 == index) + x = m_cyclic_transform; + else if ( index > 2 ) + x = Internal_ReflectAndRotateTransformation((unsigned)index); + break; + + case ON_Symmetry::Type::Inversion: + x = (0 == index) + ? ON_Xform::IdentityTransformation + : m_inversion_transform; + break; + + case ON_Symmetry::Type::Cyclic: + if (0 == index) + { + x = ON_Xform::IdentityTransformation; + } + else if (1 == index) + { + x = m_cyclic_transform; + } + else if (index >= 2) + { + x = m_cyclic_transform * m_cyclic_transform; + for (int i = 2; i < index; i++) + x = m_cyclic_transform * x; + } + break; + + default: + break; + } + return x; +} + + +const ON_Xform ON_Symmetry::Internal_ReflectAndRotateTransformation(unsigned index) const +{ + ON_Xform r = Internal_RotationXform(index / 2, m_cyclic_order); + if (1 == index % 2) + r = r * m_inversion_transform; + return r; +} diff --git a/opennurbs_symmetry.h b/opennurbs_symmetry.h new file mode 100644 index 00000000..9cca2149 --- /dev/null +++ b/opennurbs_symmetry.h @@ -0,0 +1,479 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see . +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_SYMMETRY_INC_) +#define OPENNURBS_SYMMETRY_INC_ + +////////////////////////////////////////////////////////////////////////// +// +// ON_Symmetry +// +class ON_CLASS ON_Symmetry +{ +public: + ON_Symmetry() = default; + ~ON_Symmetry() = default; + ON_Symmetry(const ON_Symmetry&) = default; + ON_Symmetry& operator=(const ON_Symmetry&) = default; + +public: + static const ON_Symmetry Unset; + + enum : unsigned int + { + MaximumOrder = 4096 + }; + + enum class Type : unsigned char + { + Unset = 0, + + /// + /// Reflection about a plane. + /// The symmetric object has 2 copies of the motif. + /// Points on the reflection plane are fixed. + /// + Reflect = 1, + + /// + /// Rotation around an axis. + /// The symmetric object has reflection copies of the motif. + /// Points on the axis are fixed. + /// + Rotate = 2, + + /// + /// Reflection and rotation alternate. + /// Rotation angle is 360/N degrees + /// The symmetric object has 2N copies of the motif. + /// Points on the rotation axis are fixed. + /// + ReflectAndRotate = 3, + + /// + /// General case inversion symmetry. + /// (inversion transformation)^2 = identity. + /// Det(inversion transformation) = -1. + /// The symmetric object has 2 copies of the motif. + /// + Inversion = 4, + + /// + /// General case cyclic symmtry (order >= 2) + /// (cyclic transformation)^N = identity. + /// When N is 2 or odd, Det(cyclic transformation) = 1. + /// When N is even and greater than 2, Det(cyclic transformation) = 1 or -1. + /// The symmetric object has N copies of the motif. + /// + Cyclic = 5, + }; + + static ON_Symmetry::Type SymmetryTypeFromUnsigned(unsigned int symmetry_type_as_unsigned); + + static const ON_wString SymmetryTypeToString( + ON_Symmetry::Type symmetry_type + ); + + + enum class Coordinates : unsigned char + { + Unset = 0, + + /// + /// The symmetry is associated with an object is is applied to. + /// If that object is transformed, the symmetry's planes and rotation axes + /// are also transformed. + /// + Object = 1, + + /// + /// The symmetry is indepenent of any objects is it applied to. + /// The symmetry's planes and rotation axes are not changed when + /// any of those objects are transformed. + /// + World = 2, + }; + + static ON_Symmetry::Coordinates SymmetryCoordinatesFromUnsigned(unsigned int coordinates_as_unsigned); + + static const ON_wString SymmetryCoordinatesToString( + ON_Symmetry::Coordinates symmetry_coordinates + ); + + static const ON_UUID ReflectId; + static const ON_UUID RotateId; + static const ON_UUID ReflectAndRotateId; + + /* + Description: + Reflection about a plane. + The symmetric object has 2 copies of the motif. + Points on the reflection plane are fixed. + Parameters: + reflection_plane - [in] + symmetry_coordinates - [in] + object or world. + Example: + If the reflection_plane is the y-z plane, + then (x,y,z) -> (-x,y,z) -> (x,y,z). + Remarks: + InversionTransform() = reflection. + CyclicTransform() = identity. + */ + static const ON_Symmetry CreateReflectSymmetry( + ON_PlaneEquation reflection_plane, + ON_Symmetry::Coordinates symmetry_coordinates + ); + + /* + Description: + Rotation around an axis. + The symmetric object has reflection copies of the motif. + Points on the axis are fixed. + Parameters: + rotation_axis - [in] + rotation_count - [in] + rotation_count must be >= 2 and the rotation angle is (360/rotation_count) degrees. + symmetry_coordinates - [in] + object or world. + Example: + If the rotation axis is the z-axis and the order is N, then + then (x,y,z) -> (r*cos(a), r*sin(a), z) -> (r*cos(2*a), r*sin(2*a), z) -> ... -> (x,y,z), + where r = sqrt(x*x + y*y) and a = (360 degrees)/N. + Remarks: + CyclicTransform() = rotation. + InversionTransform() = identity. + */ + static const ON_Symmetry CreateRotateSymmetry( + ON_Line rotation_axis, + unsigned int rotation_count, + ON_Symmetry::Coordinates symmetry_coordinates + ); + + /* + Description: + Reflection and rotation alternate. + The symmetric object has 2*reflection copies of the motif. + Points on the rotation axis are fixed. + Parameters: + reflection_plane - [in] + rotation_axis - [in] + A line in the reflection plane. + rotation_count - [in] + rotation_count must be >= 2 and the rotation angle is (360/rotation_count) degrees. + symmetry_coordinates - [in] + object or world. + Example: + If the reflection_plane is the y-z plane, the rotation axis is the z-axis, + and rotation_count = 2, + then (x,y,z) -> (-x,y,z) -> (-x-y,z) -> (x,-y,z) -> (x,y,z). + Remarks: + InversionTransform() = reflection. + CyclicTransform() = rotation by (360/rotation_count) degrees. + */ + static const ON_Symmetry CreateReflectAndRotateSymmetry( + ON_PlaneEquation reflection_plane, + ON_Line rotation_axis, + unsigned int rotation_count, + ON_Symmetry::Coordinates symmetry_coordinates + ); + + /* + Description: + Create an inversion symmetry from a transformation. + The symmetric object has 2 copies of the modif. + Parameters: + symmetry_id - [in] + An id you can assign to the symmetry + inversion_transform - [in] + inversion_transform^2 = identity. + Det(inversion_transform) = -1. + symmetry_coordinates - [in] + object or world. + Remarks: + If inversion_transform is a reflection, consider using CreateReflectSymmetry() instead. + When Det(transformation) = 1 and transformtion^2 = identity, use CreateCyclicSymmetry() instead. + */ + static const ON_Symmetry CreateInversionSymmetry( + ON_UUID symmetry_id, + ON_Xform inversion_transform, + ON_Symmetry::Coordinates symmetry_coordinates + ); + + /* + Description: + Create a cyclic symmetry from a transformation. + The symmetric object has order copies of the modif. + Parameters: + symmetry_id - [in] + An id you can assign to the symmetry + cyclic_transform - [in] + cyclic_transform^order = identity + cyclic_transform^i != identity when 0 < i < order + If order is even and at least 4, then Det(cyclic_transform) = 1 or -1. + Otherwise Det(cyclic_transform) = 1. + cyclic_order - [in] + cyclic_order >= 2 + symmetry_coordinates - [in] + object or world. + Remarks: + If cyclic_transform is a rotation, use CreateRotationSymmetry(). + If cyclic_transform is a reflection, use CreateReflectionSymmetry(). + If 2 = cyclic_order and Det(cyclic_transform) = -1, use CreateInversionSymmetry(). + */ + static const ON_Symmetry CreateCyclicSymmetry( + ON_UUID symmetry_id, + ON_Xform cyclic_transform, + unsigned int cyclic_order, + ON_Symmetry::Coordinates symmetry_coordinates + ); + + static int Compare(const ON_Symmetry* lhs, const ON_Symmetry* rhs); + +public: + /* + Returns: + Symmetry type. + */ + ON_Symmetry::Type SymmetryType() const; + + /* + Returns: + Symmetry type. + */ + ON_Symmetry::Coordinates SymmetryCoordinates() const; + + /* + Returns: + Symmetry unique id + */ + const ON_UUID SymmetryId() const; + + /* + Descripton: + Set this instance to ON_Symmetry::Unset. + */ + void Clear(); + + /* + Rturns: + True if this instance is set to a symmetry. + */ + bool IsSet() const; + + /* + Rturns: + True if this instance is not set. + */ + bool IsUnset() const; + + /* + Returns: + Number of types the motif appears in the symmetric object. + Remarks: + MotifCount() = CyclicOrder()*InversionOrder(). + */ + unsigned int MotifCount() const; + + /* + Returns: + 0: unset symmetry + 1: InversionTransform() = identity + 2: InversionTransform()^2 = identity and InversionTransform() != identity + Remarks: + In common cases, InversionTransform() is either the identity or a reflection. + */ + unsigned int InversionOrder() const; + + /* + Returns: + 0: unset symmetry + 1: the cyclic transformation is the identity. + N >= 2: CyclicTransform()^N = idenity and CyclicTransform()^i != idenity when 0 < i < N. + Remarks: + In common cases, CyclicTransform() is either the identity or a rotation. + */ + unsigned int CyclicOrder() const; + + /* + Description: + Get the transformation that maps the starting motif to the specified copy. + Parameters: + index - [in] + 0 based index of the copy (negative values are supported) + Remarks: + "0 based" means when index is a multiple of MotifCount(), the identity is returned. + */ + const ON_Xform MotifTransformation( + int index + ) const; + + /* + Returns: + The inversion transformation is returned. + InversionTransform()^InversionOrder() = identity. + Remarks: + In common cases, InversionTransform() is either the identity or a reflection (mirror). + Remarks: + NOTE: A symmetry with SymmetryOrder() = 2 and transformation S can be represented + as either InversionTransform() = S and CyclicTransform() = identity or + or CyclicTransform() = S and InversionTransform() = idenity. + The convention is to use the cyclic transforms when S is a 180 rotations + and inversion transforms otherwise. + */ + const ON_Xform InversionTransform() const; + + /* + Returns: + The cyclic transformation is returned. + CyclicTransform()^CyclicOrder() = identity. + Remarks: + In common cases, CyclicTransform() is either the identity or a rotation. + Remarks: + NOTE: A symmetry with SymmetryOrder() = 2 and transformation S can be represented + as either InversionTransform() = S and CyclicTransform() = identity or + or CyclicTransform() = S and InversionTransform() = idenity. + The convention is to use the cyclic transforms when S is a 180 rotations + and inversion transforms otherwise. + */ + const ON_Xform CyclicTransform() const; + + /* + Returns: + A SHA1 hash uniquely identifying the symmetry + */ + const ON_SHA1_Hash Hash() const; + + /* + Returns: + If the symmetry is type is Reflect or ReflectAndRotate, then the reflection plane is returned. + Othewise ON_Plane::Nan is returned. + */ + const ON_PlaneEquation ReflectionPlane() const; + + /* + Returns: + If the symmetry is type is Rotate or ReflectAndRotate, then the rotation axis is returned. + Othewise ON_Line::Nan is returned. + */ + const ON_Line RotationAxis() const; + + /* + Returns: + If the symmetry is type is Rotate or ReflectAndRotate, then a point on the rotation axis is returned. + Othewise ON_3dPoint::Nan is returned. + */ + const ON_3dPoint RotationAxisPoint() const; + + /* + Returns: + If the symmetry is type is Rotate or ReflectAndRotate, then the direction of the rotation axis is returned. + Othewise ON_3dVector::Nan is returned. + Remarks: + This vector may have length != 1 + */ + const ON_3dVector RotationAxisDirection() const; + + /* + Returns: + If the symmetry is type is Rotate or ReflectAndRotate, then a unit vector in the direction of the rotation axis is returned. + Othewise ON_3dVector::Nan is returned. + */ + const ON_3dVector RotationAxisTangent() const; + + /* + Returns: + If the symmetry is type is Rotate or ReflectAndRotate, then the rotation count is returned. + Othewise 0 is returned. + */ + unsigned int RotationCount() const; + + /* + Returns: + If the symmetry is type is Rotate or ReflectAndRotate, then the rotation angle in degrees is returned. + Othewise ON_DBL_QNAN is returned. + Remarks: + RotationAngleDegrees() = 360.0/RotationCount() + */ + double RotationAngleDegrees() const; + + /* + Returns: + If the symmetry is type is Rotate or ReflectAndRotate, then the rotation angle in radians is returned. + Othewise ON_DBL_QNAN is returned. + Remarks: + RotationAngleRadians() = (2.0*ON_PI)/RotationCount() + */ + double RotationAngleRadians() const; + +public: + bool Write(class ON_BinaryArchive&) const; + bool Read(class ON_BinaryArchive&); + void Dump(class ON_TextLog&) const; + + /* + Description: + If SymmetryCoordinates() = ON_Symmetry::Coordinates::Object, then the symmetry defition + is transformed. + */ + const ON_Symmetry TransformConditionally(const ON_Xform& xform) const; + + /* + Description: + The the symmetry defition is transformed. + */ + const ON_Symmetry TransformUnconditionally(const ON_Xform& xform) const; + +private: + static const double ZeroTolerance; + + ON_Symmetry::Type m_type = ON_Symmetry::Type::Unset; + + ON_Symmetry::Coordinates m_coordinates = ON_Symmetry::Coordinates::Unset; + + // m_inversion_order (0 = unset, 1 = identity (no inversion), 2 = (non-identity inversion) + unsigned char m_inversion_order = 0; + + unsigned char m_reserved1 = 0; + + // m_cyclic_order (0 = unset, 1 = identity (no cyclic), >= 2 cyclic order (non-identity cyclic) + unsigned int m_cyclic_order = 0; + + ON__UINT_PTR m_reserved2 = 0; + + // id is a preset value for the 3 built in symmetries and user defined for others + ON_UUID m_id = ON_nil_uuid; + + // m_inversion_transform^2 = identity + ON_Xform m_inversion_transform = ON_Xform::Nan; + + // m_cyclic_transform^m_cyclic_order = identity + ON_Xform m_cyclic_transform = ON_Xform::Nan; + + // Set when type is Reflect or ReflectAndRotate + ON_PlaneEquation m_reflection_plane = ON_PlaneEquation::NanPlaneEquation; + + // Set when type is Rotate or ReflectAndRotate + ON_Line m_rotation_axis = ON_Line::NanLine; + +private: + static int Internal_CompareDouble(const double* lhs, const double* rhs, size_t count); + const ON_Xform Internal_ReflectAndRotateTransformation(unsigned index) const; + static const ON_Xform Internal_RotationXform(ON_Line axis, int rotation_index, int rotation_count); + const ON_Xform Internal_RotationXform(int rotation_index, int rotation_count) const; +}; + +#endif diff --git a/opennurbs_text.cpp b/opennurbs_text.cpp index 824c6476..e204a0e6 100644 --- a/opennurbs_text.cpp +++ b/opennurbs_text.cpp @@ -41,6 +41,7 @@ void ON_TextContent::Internal_Destroy() m_annotation_type = ON_TextContent::Empty.m_annotation_type; m_dimstyle_text_position_properties_hash = ON_TextContent::Empty.m_dimstyle_text_position_properties_hash; Internal_ClearTextContentHash(); + m_default_font = &ON_Font::Default; } ON_TextContent::~ON_TextContent() @@ -79,6 +80,9 @@ void ON_TextContent::Internal_CopyFrom( { m__wrapped_runs = new ON_TextRunArray(*src.m__wrapped_runs); } + + m_default_font = src.m_default_font; + m_runtime_halign = src.m_runtime_halign; } @@ -342,14 +346,17 @@ bool ON_TextContent::Internal_ParseRtf( bool rc = parser.Parse(); if (rc) rc = ON_TextContent::MeasureTextContent(this, true, false); + if (rc) + m_default_font = &dimstyle->Font(); if (rc && bComposeAndUpdateRtf) { - rc = RtfComposer::Compose(this, dimstyle, default_fontname, str); + rc = RtfComposer::Compose(this, str, false); if (rc) { m_text = str; } } + return rc; } @@ -358,6 +365,33 @@ const ON_wString ON_TextContent::RichText() const return m_text; } +const ON_wString ON_TextContent::RichTextFromRuns(ON::RichTextStyle rich_text_style) const +{ + ON_wString rich_text; + + switch (rich_text_style) + { + case ON::RichTextStyle::Windows10SDK: + if (!RtfComposer::Compose(this, rich_text, true)) + rich_text.Empty(); + break; + + case ON::RichTextStyle::AppleOSXSDK: + rich_text = RtfComposer::ComposeAppleRTF(this); + break; + + default: + break; + }; + + return rich_text; +} + +const ON_wString ON_TextContent::PlatformRichTextFromRuns() const +{ + return RichTextFromRuns(ON::RichTextStyleFromCurrentPlatform()); +} + const ON_wString ON_TextContent::PlainTextWithFields() const { return Internal_GetPlainText(false, false); @@ -1356,6 +1390,25 @@ ON_Mesh* ON_TextContent::Get3dPickMesh() const return mesh; } +ON_Mesh* ON_TextContent::Get3dMaskPickMesh(double maskoffset) const +{ + ON_3dPoint corners[4]; + if (Get3dMaskCorners(maskoffset, corners)) + { + ON_Mesh* mesh = new ON_Mesh(1, 4, false, false); + if (nullptr != mesh) + { + mesh->SetVertex(0, corners[0]); + mesh->SetVertex(1, corners[1]); + mesh->SetVertex(2, corners[2]); + mesh->SetVertex(3, corners[3]); + mesh->SetQuad(0, 0, 1, 2, 3); + return mesh; + } + } + return Get3dPickMesh(); +} + bool ON_TextContent::Get2dSize(bool raw, double& width, double& height) const { @@ -1386,8 +1439,7 @@ bool ON_TextContent::Get3dCorners(ON_3dPoint corners[4]) const { if (nullptr == corners) return false; - bool rc = false; - ON_2dPoint corners2d[4]; + bool rc = false; ON_2dPoint corners2d[4]; if (Get2dCorners(corners2d)) { corners[0] = ON_Plane::World_xy.PointAt(corners2d[0].x, corners2d[0].y); @@ -1744,11 +1796,19 @@ const wchar_t* ON_TextContent::RtfText() const return m_text; } +const ON_Font& ON_TextContent::DefaultFont() const +{ + if (nullptr != m_default_font) + return *m_default_font; + else + return ON_Font::Default; +} + bool ON_TextContent::ComposeText() { ON_wString rtf; ON_wString nothing; - if (RtfComposer::Compose(this, nullptr, nothing, rtf)) + if (RtfComposer::Compose(this, rtf, false)) { m_text = rtf; return true; @@ -2478,7 +2538,28 @@ bool ON_TextContent::FormatArea( bool alt, ON_wString& formatted_string) { - double area = area_in; + return ON_TextContent::FormatAreaOrVolume(area_in, true, units_in, dimstyle, alt, formatted_string); +} + +bool ON_TextContent::FormatVolume( + double volume_in, + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + bool alt, + ON_wString& formatted_string) +{ + return ON_TextContent::FormatAreaOrVolume(volume_in, false, units_in, dimstyle, alt, formatted_string); +} + +bool ON_TextContent::FormatAreaOrVolume( + double area_or_volume_in, + bool format_area, + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + bool alt, + ON_wString& formatted_string) +{ + double value = area_or_volume_in; if (nullptr == dimstyle) dimstyle = &ON_DimStyle::Default; @@ -2499,21 +2580,23 @@ bool ON_TextContent::FormatArea( : dimstyle->LengthFactor(); double unit_length_factor = ON::UnitScale(units_in, dim_us); - area = unit_length_factor * unit_length_factor * length_factor * area_in; + value = format_area ? + unit_length_factor * unit_length_factor * length_factor * area_or_volume_in : + unit_length_factor * unit_length_factor * unit_length_factor * length_factor * area_or_volume_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; + if (fabs(value) < pow(10.0, -(precision + 1))) + value = 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, + value, output_format, roundoff, precision, diff --git a/opennurbs_text.h b/opennurbs_text.h index 178c4d9e..99950f21 100644 --- a/opennurbs_text.h +++ b/opennurbs_text.h @@ -189,11 +189,33 @@ public: /* Returns: - Raw text that can contain rich text formatting instructions. + Rhino clean rich text that is currently saved on this instance. + The text can contain text formatting instructions. Fields are not evaluated. */ const ON_wString RichText() const; + /* + Returns: + Rich text suitable for initializing SDK controls on the current platform (Windows or Apple OS X). + The returned string is alwasy rich text (never plain text). + The returned rich text is always generated directly from the runs. + This text is typically different from the Rhino clean rich text returned by the RichText() command. + */ + const ON_wString PlatformRichTextFromRuns() const; + + + /* + Parameters: + rich_text_style - [in] + Type of rich text to return. + Returns: + The returned string is alwasy rich text (never plain text). + The returned rich text is always generated directly from the runs. + This text is typically different from the Rhino clean rich text returned by the RichText() command. + */ + const ON_wString RichTextFromRuns(ON::RichTextStyle rich_text_style) const; + /* Returns: Plain text information with any rich text formatting instructions removed. @@ -229,6 +251,7 @@ public: */ const ON_wString WrappedPlainTextWithFields() const; + private: void Internal_SetRunTextHeight(double height); @@ -324,6 +347,7 @@ public: ON_Mesh* Get2dPickMesh() const; ON_Mesh* Get3dPickMesh() const; + ON_Mesh* Get3dMaskPickMesh(double maskoffset) const; // Returns pointer to either m_runs, the basic parsed and evaluated text // or m_wrapped_runs which is the runs after text wrapping @@ -353,6 +377,10 @@ public: //void SetCurrentDimStyle(const ON_DimStyle* dimstyle) const; + // Returns the default font for this text content. + // Typically, that would be the font from the dimstyle + const ON_Font& DefaultFont() const; + ON::TextHorizontalAlignment RuntimeHorizontalAlignment() const; void SetRuntimeHorizontalAlignment(ON::TextHorizontalAlignment halign) const; @@ -399,7 +427,13 @@ private: mutable ON_SHA1_Hash m_text_content_bbox_hash = ON_SHA1_Hash::ZeroDigest; mutable ON_BoundingBox m_text_content_bbox = ON_BoundingBox::EmptyBoundingBox; - ON__INT_PTR m_reserved0 = (ON__INT_PTR)0; + // Stores a pointer to the top level installed font for the entire textContent + // May be overridden by font in individual runs + // Dec. 12, 2019 + mutable const ON_Font* m_default_font = &ON_Font::Default; + //ON__INT_PTR m_reserved0 = (ON__INT_PTR)0; + + public: friend class ON_Text; @@ -556,14 +590,22 @@ public: 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); + // Volume formatting + static bool FormatVolume( + double volume, + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + bool alternate, // Primary or alternate + ON_wString& formatted_string); // Output + + private: + static bool FormatAreaOrVolume( + double area_or_volume, + bool format_area, + ON::LengthUnitSystem units_in, + const ON_DimStyle* dimstyle, + bool alternate, // Primary or alternate + ON_wString& formatted_string); // Output }; diff --git a/opennurbs_textiterator.cpp b/opennurbs_textiterator.cpp index 5177b454..4fbd800a 100644 --- a/opennurbs_textiterator.cpp +++ b/opennurbs_textiterator.cpp @@ -2840,7 +2840,7 @@ bool ON_RtfParser::Parse() // This has to make a string that the text control can digest // and that's short and simple as possible so it can be put in the file -unsigned int RtfComposer::GetFacenameKey(const ON_Font* font, ON_SimpleArray< wchar_t[34] >& fonttable) +unsigned int RtfComposer::GetFacenameKey(const ON_Font* font, ON_ClassArray< ON_wString >& fonttable) { if (nullptr == font) return 0; @@ -2850,16 +2850,15 @@ unsigned int RtfComposer::GetFacenameKey(const ON_Font* font, ON_SimpleArray< wc if (rtf_facename.IsEmpty()) return 0; +#if defined(ON_RUNTIME_WIN) if (rtf_facename.Length() > 33) - ON_ERROR("rtf_facename is too long\n"); - wchar_t facename[34]; - wcsncpy(facename, rtf_facename.Array(), 33); - facename[33] = 0; + ON_ERROR("rtf_facename is longer than Windows LogfontName\n"); +#endif - return RtfComposer::GetFacenameKey(facename, fonttable); + return RtfComposer::GetFacenameKey(rtf_facename.Array(), fonttable); } -unsigned int RtfComposer::GetFacenameKey(const wchar_t* facename, ON_SimpleArray< wchar_t[34] >& fonttable) +unsigned int RtfComposer::GetFacenameKey(const wchar_t* facename, ON_ClassArray< ON_wString >& fonttable) { if(nullptr == facename || 0 == facename[0]) return 0; @@ -2871,12 +2870,9 @@ unsigned int RtfComposer::GetFacenameKey(const wchar_t* facename, ON_SimpleArray if(ON_wString::EqualOrdinal(facename, fonttable[i], true)) return i; } - if (nullptr != wcsncpy(fonttable.AppendNew(), facename, 33)) - { - fonttable[count][33] = 0; - return count; - } - return 0; + + fonttable.AppendNew() = facename; + return count; } unsigned int RtfComposer::GetColorKey(ON_Color color, ON_SimpleArray< unsigned int >& colortable) { @@ -2977,15 +2973,11 @@ static bool GetRunText(ON_TextRun* run, ON_wString& text_out, bool& foundunicode return true; } - bool RtfComposer::Compose( const ON_TextContent* text, - const ON_DimStyle* dimstyle, - const ON_wString default_fontname, - ON_wString& rtf) + ON_wString& rtf, + bool bForceRtf) { - dimstyle = &ON_DimStyle::DimStyleOrDefault(dimstyle); - if (0 == text) return false; @@ -2999,14 +2991,10 @@ bool RtfComposer::Compose( if (nullptr == runs) return false; - //if (dimstyle->IsChildDimstyle()) - //{ - // ON_UUID parent_id = dimstyle->ParentId(); - // - //} - - - const ON_Font& style_font = dimstyle->Font(); + const ON_Font& style_font = text->DefaultFont(); + const ON_wString style_fontname = style_font.RichTextFontName(); + if (style_fontname.IsEmpty()) + return false; bool style_bold = style_font.IsBoldInQuartet(); bool style_italic = (ON_Font::Style::Italic == style_font.FontStyle()); @@ -3018,21 +3006,17 @@ bool RtfComposer::Compose( bool chg_underline = false; bool chg_strikeout = false; bool chg_facename = false; - if (dimstyle->IsChildDimstyle() && dimstyle->IsFieldOverride(ON_DimStyle::field::Font)) - chg_facename = true; - + // First facename is from the ON_TextContent // Any after that are from runs - ON_SimpleArray< wchar_t[34] > fonttable(8); - + ON_ClassArray< ON_wString > fonttable(8); + // Creates a fonttable entry the first time - unsigned int deffont_key = default_fontname.IsNotEmpty() ? - GetFacenameKey(default_fontname, fonttable) : - GetFacenameKey(&style_font, fonttable); + unsigned int stylefont_key = GetFacenameKey(style_fontname, fonttable); int runcount = runs->Count(); int nlcount = 0; - + // See if this is multi-line text bool multiline = false; for (int ri = 0; ri < runcount; ri++) @@ -3041,14 +3025,17 @@ bool RtfComposer::Compose( if (nullptr != run) { if (ON_TextRun::RunType::kText == run->Type() && 0 < nlcount) + { multiline = true; + break; + } else if (ON_TextRun::RunType::kNewline == run->Type() || ON_TextRun::RunType::kParagraph == run->Type()) nlcount++; } } - - bool make_rtf = false; + + bool make_rtf = bForceRtf; ON_SimpleArray< ON_TextRun* > runholders; for (int ri = 0; ri < runcount; ri++) { @@ -3064,25 +3051,29 @@ bool RtfComposer::Compose( if (nullptr != run_font) { runholders.AppendNew() = run; - if(!chg_bold) + if (!chg_bold) chg_bold = run_font->IsBoldInQuartet() != style_bold; - if(!chg_italic) + if (!chg_italic) chg_italic = run_font->IsItalic() != style_italic; - if(!chg_underline) + if (!chg_underline) chg_underline = run_font->IsUnderlined() != style_underline; - if(!chg_strikeout) + if (!chg_strikeout) chg_strikeout = run_font->IsStrikethrough() != style_strikeout; - unsigned int facename_key = GetFacenameKey(run_font, fonttable); - if(!chg_facename) - chg_facename = facename_key != deffont_key; + const ON_wString& run_fontname = run_font->RichTextFontName(); + if (run_fontname.IsEmpty()) + return false; + unsigned int run_font_key = GetFacenameKey(run_fontname, fonttable); + + if (!chg_facename) + chg_facename = run_font_key != stylefont_key; } const ON__UINT32* run_codepoints = run->UnicodeString(); if (!make_rtf && nullptr != run_codepoints) { for (int i = 0; run_codepoints[i] != 0; i++) { - if (run_codepoints[i] > 127 + if (run_codepoints[i] > 127 || run_codepoints[i] == '\\' || run_codepoints[i] == '{' || run_codepoints[i] == '}' @@ -3102,7 +3093,6 @@ bool RtfComposer::Compose( runholders.AppendNew() = run; } } - } // end of getting runinfo if (chg_bold || chg_italic || chg_underline || chg_strikeout || chg_facename) @@ -3133,18 +3123,18 @@ bool RtfComposer::Compose( // add properties for this string run_strings += L"{"; bool addspace = false; + unsigned int run_font_key = GetFacenameKey(run_font, fonttable); - { - temp.Format(L"\\f%d", run_font_key); - run_strings += temp; - addspace = true; - } + temp.Format(L"\\f%d", run_font_key); + run_strings += temp; + addspace = true; + if (run_font->IsBoldInQuartet()) { run_strings += L"\\b"; addspace = true; } - else if(style_bold) + else if (style_bold) { run_strings += L"\\b0"; addspace = true; @@ -3200,7 +3190,7 @@ bool RtfComposer::Compose( GetRunText(run, run_strings, make_rtf); } } - else if (ri < runcount-1 && multiline && (ON_TextRun::RunType::kNewline == run->Type() || ON_TextRun::RunType::kParagraph == run->Type())) + else if (ri < runcount - 1 && multiline && (ON_TextRun::RunType::kNewline == run->Type() || ON_TextRun::RunType::kParagraph == run->Type())) { if (make_rtf) run_strings += L"{\\par}"; @@ -3227,13 +3217,13 @@ bool RtfComposer::Compose( if (0 < nfont) { ON_wString fonttable_string; - temp.Format(L"\\deff%d", deffont_key); + temp.Format(L"\\deff%d", stylefont_key); rtf += temp; fonttable_string = L"{\\fonttbl"; for (int fi = 0; fi < nfont; fi++) { // - temp.Format(L"{\\f%d %s;}", fi, fonttable[fi]); + temp.Format(L"{\\f%d %s;}", fi, fonttable[fi].Array()); fonttable_string += temp; } rtf += fonttable_string; @@ -3269,3 +3259,183 @@ void RtfComposer::SetRecomposeRTF(bool b) RtfComposer::m_bComposeRTF = b; } +const ON_wString RtfComposer::ComposeAppleRTF( + const ON_TextContent* text) +{ + ON_wString rtf_string; + + if (0 == text) + return rtf_string; + + ON_TextRunArray* runs = text->TextRuns(true); + if (nullptr == runs) + return rtf_string; + + + const ON_Font& style_font = text->DefaultFont(); + const ON_wString style_fontname = style_font.PostScriptName(); + if (style_fontname.IsEmpty()) + return rtf_string; + + // First facename is from the ON_TextContent + // Any after that are from runs + ON_ClassArray< ON_wString > fonttable(8); + + // Creates a fonttable entry the first time + unsigned int deffont_key = GetFacenameKey(style_fontname, fonttable); + + int runcount = runs->Count(); + int nlcount = 0; + + // See if this is multi-line text + bool multiline = false; + for (int ri = 0; ri < runcount; ri++) + { + ON_TextRun* run = (*runs)[ri]; + if (nullptr != run) + { + if (ON_TextRun::RunType::kText == run->Type() && 0 < nlcount) + multiline = true; + else if (ON_TextRun::RunType::kNewline == run->Type() || + ON_TextRun::RunType::kParagraph == run->Type()) + nlcount++; + } + } + + ON_SimpleArray< ON_TextRun* > runholders; + for (int ri = 0; ri < runcount; ri++) + { + ON_TextRun* run = (*runs)[ri]; + if (nullptr != run) + { + if ( + ON_TextRun::RunType::kText == run->Type() || + ON_TextRun::RunType::kField == run->Type() + ) + { + const ON_Font* run_font = run->Font(); + if (nullptr != run_font) + { + runholders.AppendNew() = run; + } + } + else if ( + ON_TextRun::RunType::kParagraph == run->Type() || + ON_TextRun::RunType::kNewline == run->Type() + ) + { + runholders.AppendNew() = run; + } + } + } // end of getting runinfo + + ON_wString run_strings; + ON_wString temp; + runcount = runholders.Count(); + for (int ri = 0; ri < runcount; ri++) + { + ON_TextRun* run = runholders[ri]; + if (nullptr == run) + continue; + + if ( + ON_TextRun::RunType::kText == run->Type() || + ON_TextRun::RunType::kField == run->Type() + ) + { + const ON_Font* run_font = run->Font(); + if (nullptr == run_font) + continue; + + const ON_wString& run_fontname = run_font->PostScriptName(); + if (run_fontname.IsEmpty()) + return rtf_string; + + // add properties for this string + run_strings += L"{"; + bool addspace = false; + + unsigned int run_font_key = GetFacenameKey(run_fontname, fonttable); + temp.Format(L"\\f%d", run_font_key); + run_strings += temp; + addspace = true; + + if (run_font->IsBoldInQuartet()) + { + run_strings += L"\\b"; + addspace = true; + } + if (run_font->IsItalic()) + { + run_strings += L"\\i"; + addspace = true; + } + if (run_font->IsUnderlined()) + { + run_strings += L"\\ul"; + addspace = true; + } + if (addspace) + run_strings += L" "; + + bool true_bool = true; + if (run->IsStacked() == ON_TextRun::Stacked::kStacked && run->m_stacked_text != 0) + { + run_strings += L"[["; + run_strings += run->m_stacked_text->m_separator; + GetRunText(run->m_stacked_text->m_top_run, run_strings, true_bool); + run_strings += run->m_stacked_text->m_separator; + GetRunText(run->m_stacked_text->m_bottom_run, run_strings, true_bool); + run_strings += L"]]"; + } + else if (ON_TextRun::RunType::kField == run->Type()) + { + run_strings += L"%<"; + GetRunText(run, run_strings, true_bool); + run_strings += L">%"; + } + else + { + GetRunText(run, run_strings, true_bool); + } + run_strings += L"}"; + } + else if (ri < runcount - 1 && multiline && (ON_TextRun::RunType::kNewline == run->Type() || ON_TextRun::RunType::kParagraph == run->Type())) + { + run_strings += L"{\\par}"; + } + } // end of collecting run text + + int nfont = fonttable.Count(); + if (run_strings.Length() > 0) + { + // deff0 means use font0 for the default font throughout the string. + // If we include font0 in the font table, when we send + // the string back to the RTF control, the font listed as + // font0 will be used instead of the default font for the + // style. + // So the default font is listed as 0 and there is no + // entry in the table for font0. + + rtf_string.Format(L"{\\rtf1"); + if (0 < nfont) + { + ON_wString fonttable_string; + temp.Format(L"\\deff%d", deffont_key); + rtf_string += temp; + fonttable_string = L"{\\fonttbl"; + for (int fi = 0; fi < nfont; fi++) + { + temp.Format(L"{\\f%d %s;}", fi, fonttable[fi].Array()); + fonttable_string += temp; + } + rtf_string += fonttable_string; + } + + rtf_string += L"}\\fs40"; + + rtf_string += run_strings; + rtf_string += L"\\par}"; + } + return rtf_string; +} diff --git a/opennurbs_textiterator.h b/opennurbs_textiterator.h index 9a960764..67bf1183 100644 --- a/opennurbs_textiterator.h +++ b/opennurbs_textiterator.h @@ -874,12 +874,13 @@ public: int m_facename_key = -1; }; - static bool Compose( const ON_TextContent* text, - const ON_DimStyle* dimstyle, - const ON_wString default_fontname, - ON_wString& rtf); + ON_wString& rtf, + bool bForceRtf); + + static const ON_wString ComposeAppleRTF( + const ON_TextContent* text); static bool RecomposeRTF(); static void SetRecomposeRTF(bool b); @@ -889,8 +890,8 @@ private: RtfComposer(); - static unsigned int GetFacenameKey(const ON_Font* font, ON_SimpleArray< wchar_t[34] >& fonttable); - static unsigned int GetFacenameKey(const wchar_t* facename, ON_SimpleArray< wchar_t[34] >& fonttable); + static unsigned int GetFacenameKey(const ON_Font* font, ON_ClassArray< ON_wString >& fonttable); + static unsigned int GetFacenameKey(const wchar_t* facename, ON_ClassArray< ON_wString >& fonttable); static unsigned int GetColorKey(ON_Color color, ON_SimpleArray< unsigned int >& colortable); static bool FormatTextHeight(double height, ON_wString& str); }; diff --git a/opennurbs_textlog.cpp b/opennurbs_textlog.cpp index db1b0c12..d2d33cf3 100644 --- a/opennurbs_textlog.cpp +++ b/opennurbs_textlog.cpp @@ -1043,6 +1043,10 @@ bool ON_TextLog::IsTextHash() const return (nullptr != dynamic_cast(this)); } +bool ON_TextLog::IsNull() const +{ + return this == &ON_TextLog::Null; +} ON_StringMapType ON_TextHash::StringMapType() const { diff --git a/opennurbs_textlog.h b/opennurbs_textlog.h index ab948b7a..ea04ecc3 100644 --- a/opennurbs_textlog.h +++ b/opennurbs_textlog.h @@ -52,6 +52,18 @@ public: */ static ON_TextLog Null; + /* + Returns: + True if this is ON_TextLog::Null. + */ + bool IsNull() const; + + /* + Returns: + True if the text is being used to calculate a geometric content SHA1 hash + and non-geometry information that varies based on time, computer, or user + should not be printed. + */ bool IsTextHash() const; public: diff --git a/opennurbs_viewport.cpp b/opennurbs_viewport.cpp index 52f1368f..0ea10c4c 100644 --- a/opennurbs_viewport.cpp +++ b/opennurbs_viewport.cpp @@ -4942,195 +4942,3 @@ int ON_Viewport::InViewFrustum( return 0; 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 c278049b..60801584 100644 --- a/opennurbs_viewport.h +++ b/opennurbs_viewport.h @@ -1616,13 +1616,6 @@ 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 b6f22fcf..980b12bd 100644 --- a/opennurbs_wip.h +++ b/opennurbs_wip.h @@ -17,13 +17,6 @@ #if !defined OPENNURBS_WIP_INC__ #define OPENNURBS_WIP_INC__ -// 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 -#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 diff --git a/opennurbs_xform.cpp b/opennurbs_xform.cpp index 4eef7c80..19c78c94 100644 --- a/opennurbs_xform.cpp +++ b/opennurbs_xform.cpp @@ -2087,6 +2087,38 @@ void ON_Xform::Mirror( m_xform[3][3] = 1.0; } +const ON_Xform ON_Xform::MirrorTransformation( + ON_PlaneEquation mirror_plane +) +{ + const ON_PlaneEquation e = mirror_plane.UnitizedPlaneEquation(); + const ON_3dVector N(e.x, e.y, e.z); + ON_3dVector V = (-2.0*e.d)*N; + ON_Xform mirror; + mirror.m_xform[0][0] = 1 - 2.0*N.x*N.x; + mirror.m_xform[0][1] = -2.0*N.x*N.y; + mirror.m_xform[0][2] = -2.0*N.x*N.z; + mirror.m_xform[0][3] = V.x; + + mirror.m_xform[1][0] = -2.0*N.y*N.x; + mirror.m_xform[1][1] = 1.0 - 2.0*N.y*N.y; + mirror.m_xform[1][2] = -2.0*N.y*N.z; + mirror.m_xform[1][3] = V.y; + + mirror.m_xform[2][0] = -2.0*N.z*N.x; + mirror.m_xform[2][1] = -2.0*N.z*N.y; + mirror.m_xform[2][2] = 1.0 - 2.0*N.z*N.z; + mirror.m_xform[2][3] = V.z; + + mirror.m_xform[3][0] = 0.0; + mirror.m_xform[3][1] = 0.0; + mirror.m_xform[3][2] = 0.0; + mirror.m_xform[3][3] = 1.0; + + return mirror; +} + + bool ON_Xform::ChangeBasis( diff --git a/opennurbs_xform.h b/opennurbs_xform.h index 9a2b5254..eb20e748 100644 --- a/opennurbs_xform.h +++ b/opennurbs_xform.h @@ -838,6 +838,10 @@ Notes: ON_3dVector normal_to_mirror_plane ); + static const ON_Xform MirrorTransformation( + ON_PlaneEquation mirror_plane + ); + // Description: The ChangeBasis() function is overloaded // and provides several // ways to compute a change of basis transformation. diff --git a/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj b/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj index 66530269..d8fb7b38 100644 --- a/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj +++ b/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj @@ -181,8 +181,8 @@ developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, + Base, ); mainGroup = 1DB028141ED6430300FA9144; productRefGroup = 1DB0281E1ED6430300FA9144 /* Products */; diff --git a/zlib/zlib.vcxproj b/zlib/zlib.vcxproj index 9298f70c..86ab9f51 100644 --- a/zlib/zlib.vcxproj +++ b/zlib/zlib.vcxproj @@ -47,30 +47,31 @@ {7B90C09F-DC78-42B2-AD34-380F6D466B29} Win32Proj zlib + 10.0 StaticLibrary true - v141 + v142 Unicode StaticLibrary true - v141 + v142 Unicode StaticLibrary false - v141 + v142 Unicode StaticLibrary false - v141 + v142 Unicode