diff --git a/opennurbs.h b/opennurbs.h index 68638bb9..60ac1b93 100644 --- a/opennurbs.h +++ b/opennurbs.h @@ -111,7 +111,7 @@ #include "opennurbs_texture.h" // texture definition #include "opennurbs_material.h" // simple rendering material #include "opennurbs_layer.h" // layer definition -#include "opennurbs_linetype.h" // linetype definition +#include "opennurbs_linetype.h" // linetype definition #include "opennurbs_group.h" // group name and index #include "opennurbs_light.h" // light #include "opennurbs_pointgeometry.h" // single point diff --git a/opennurbs_3dm_attributes.cpp b/opennurbs_3dm_attributes.cpp index 2aa6ad7f..fee14c67 100644 --- a/opennurbs_3dm_attributes.cpp +++ b/opennurbs_3dm_attributes.cpp @@ -25,7 +25,10 @@ class ON_3dmObjectAttributesPrivate { public: + ON_3dmObjectAttributesPrivate() = delete; ON_3dmObjectAttributesPrivate(const ON_3dmObjectAttributes* attr); + ~ON_3dmObjectAttributesPrivate() = default; + ON_3dmObjectAttributesPrivate& operator=(const ON_3dmObjectAttributesPrivate&) = default; bool operator==(const ON_3dmObjectAttributesPrivate&) const; bool operator!=(const ON_3dmObjectAttributesPrivate&) const; @@ -43,6 +46,8 @@ public: ON_Color m_hatch_background_fill; bool m_hatch_boundary_visible = false; + std::shared_ptr m_custom_linetype; + ON_DecalCollection m_decals; ON_MeshModifiers m_mesh_modifiers; }; @@ -89,6 +94,11 @@ bool ON_3dmObjectAttributesPrivate::operator==(const ON_3dmObjectAttributesPriva if (m_hatch_boundary_visible != other.m_hatch_boundary_visible) return false; + const ON_Linetype* customThis = m_custom_linetype.get(); + const ON_Linetype* customOther = other.m_custom_linetype.get(); + if (customThis != customOther) + return false; + return true; } @@ -415,10 +425,15 @@ enum ON_3dmObjectAttributesTypeCodes : unsigned char // 26 Jan 2022 Andy Le Bihan // file version 2.8: object frame ObjectFrame = 36, - // 15 Jun 2022 S. Baer (file version 2.9) - SectionFillRule + // 15 Jun 2022 S. Baer + // file version 2.9: SectionFillRule SectionFillRule = 37, + // 30 Nov 2022 S. Baer + // file version 2.10: custom linetype + CustomLinetype = 38, + // add items here - LastAttributeTypeCode = 37 + LastAttributeTypeCode = 38 }; bool ON_3dmObjectAttributes::Internal_ReadV5( ON_BinaryArchive& file ) @@ -745,7 +760,7 @@ bool ON_3dmObjectAttributes::Internal_ReadV5( ON_BinaryArchive& file ) double scale = 1.0; rc = file.ReadDouble(&scale); if (!rc) break; - SetLinetypeScale(scale); + SetLinetypePatternScale(scale); rc = file.ReadChar(&itemid); if (!rc || 0 == itemid) break; } @@ -811,6 +826,21 @@ bool ON_3dmObjectAttributes::Internal_ReadV5( ON_BinaryArchive& file ) if (minor_version <= 9) break; + if (ON_3dmObjectAttributesTypeCodes::CustomLinetype == itemid) // 38 + { + ON_Linetype lt; + rc = lt.Read(file); + if (!rc) break; + + SetCustomLinetype(lt); + + rc = file.ReadChar(&itemid); + if (!rc || 0 == itemid) break; + } + + if (minor_version <= 10) + break; + // Add new item reading above and increment the LastAttributeTypeCode value // in the enum. Be sure to test reading of old and new files by old and new // code, before checking in your changes. @@ -1005,7 +1035,9 @@ bool ON_3dmObjectAttributes::Internal_WriteV5( ON_BinaryArchive& file ) const // Chunk version = 2.8 to support object frame. // 15 Jun 2022 S. Baer // Chunk version = 2.9 to support SectionFillRule - bool rc = file.Write3dmChunkVersion(2,9); + // 30 Nov 2022 S. Baer + // Chunk version = 2.10 to support + bool rc = file.Write3dmChunkVersion(2,10); while(rc) { if (!rc) break; @@ -1277,12 +1309,12 @@ bool ON_3dmObjectAttributes::Internal_WriteV5( ON_BinaryArchive& file ) const if (!rc) break; } - if (fabs(1.0 - LinetypeScale()) > ON_EPSILON) + if (fabs(1.0 - LinetypePatternScale()) > ON_EPSILON) { c = ON_3dmObjectAttributesTypeCodes::LinetypeScale; // 33 rc = file.WriteChar(c); if (!rc) break; - rc = file.WriteDouble(LinetypeScale()); + rc = file.WriteDouble(LinetypePatternScale()); if (!rc) break; } @@ -1336,6 +1368,19 @@ bool ON_3dmObjectAttributes::Internal_WriteV5( ON_BinaryArchive& file ) const if (!rc) break; } + // 30 Nov 2022 S. Baer + // Write custom linetype + { + const ON_Linetype* linetype = CustomLinetype(); + if (linetype) + { + c = ON_3dmObjectAttributesTypeCodes::CustomLinetype; // 38 + rc = file.WriteChar(c); + if (!rc) break; + rc = linetype->Write(file); + if (!rc) break; + } + } // 0 indicates end of attributes - this should be the last item written c = 0; @@ -1586,6 +1631,16 @@ void ON_3dmObjectAttributes::Dump( ON_TextLog& dump ) const } dump.Print("\n"); } + + const ON_Linetype* linetype = CustomLinetype(); + if (nullptr == linetype) + { + dump.Print("no custom linetype\n"); + } + else + { + dump.Print("contains custom linetype\n"); + } } ON::object_mode ON_3dmObjectAttributes::Mode() const @@ -2344,16 +2399,16 @@ void ON_3dmObjectAttributes::SetSectionAttributesSource(ON::SectionAttributesSou m_private->m_section_attributes_source = source; } -double ON_3dmObjectAttributes::LinetypeScale() const +double ON_3dmObjectAttributes::LinetypePatternScale() const { return m_private ? m_private->m_linetype_scale : 1.0; } -void ON_3dmObjectAttributes::SetLinetypeScale(double scale) +void ON_3dmObjectAttributes::SetLinetypePatternScale(double scale) { if (scale < ON_EPSILON) return; - if (fabs(LinetypeScale() - scale) < ON_EPSILON) + if (fabs(LinetypePatternScale() - scale) < ON_EPSILON) return; if (nullptr == m_private) @@ -2361,6 +2416,27 @@ void ON_3dmObjectAttributes::SetLinetypeScale(double scale) m_private->m_linetype_scale = scale; } +void ON_3dmObjectAttributes::SetCustomLinetype(const ON_Linetype& linetype) +{ + if (nullptr == m_private) + m_private = new ON_3dmObjectAttributesPrivate(this); + + m_private->m_custom_linetype.reset(new ON_Linetype(linetype)); +} +const ON_Linetype* ON_3dmObjectAttributes::CustomLinetype() const +{ + const ON_Linetype* rc = nullptr; + if (m_private) + rc = m_private->m_custom_linetype.get(); + return rc; +} +void ON_3dmObjectAttributes::RemoveCustomLinetype() +{ + if (m_private) + m_private->m_custom_linetype.reset(); +} + + ON_Color ON_3dmObjectAttributes::HatchBackgroundFillColor() const { return m_private ? m_private->m_hatch_background_fill : ON_Color::UnsetColor; @@ -2392,7 +2468,6 @@ void ON_3dmObjectAttributes::SetHatchBoundaryVisible(bool on) } - //https://mcneel.myjetbrains.com/youtrack/issue/RH-20531 ON_Plane ON_3dmObjectAttributes::ObjectFrame(const ON_COMPONENT_INDEX& ci) const { diff --git a/opennurbs_3dm_attributes.h b/opennurbs_3dm_attributes.h index db437418..abefbf50 100644 --- a/opennurbs_3dm_attributes.h +++ b/opennurbs_3dm_attributes.h @@ -424,8 +424,31 @@ public: #pragma endregion // Per object linetype scale - double LinetypeScale() const; - void SetLinetypeScale(double scale); + double LinetypePatternScale() const; + void SetLinetypePatternScale(double scale); + + /* + Description: + Attributes can have optional custom linetypes associated with them. When a + custom linetype is attached to an attribute, this linetype is used for an + attribute instead of the linetype referenced by the linetype index. This + function adds a custom linetype for this attribute. + */ + void SetCustomLinetype(const ON_Linetype& linetype); + + /* + Description: + Attributes can have optional custom linetypes associated with them. This + function returns the custom linetype if one exists. If a custom linetype is + not attached to this attribute, then an empty shared pointer is returned + */ + const ON_Linetype* CustomLinetype() const; + + /* + Description: + Remove any custom linetype associated with this attribute + */ + void RemoveCustomLinetype(); #pragma region Hatch Specific Attributes ON_Color HatchBackgroundFillColor() const; @@ -434,6 +457,7 @@ public: void SetHatchBoundaryVisible(bool on); #pragma endregion + ON_Plane ObjectFrame(const ON_COMPONENT_INDEX& ci) const; void SetObjectFrame(const ON_COMPONENT_INDEX& ci, const ON_Xform& wcs_to_ocs); void SetObjectFrame(const ON_COMPONENT_INDEX& ci, const ON_Plane& plane); diff --git a/opennurbs_decals.cpp b/opennurbs_decals.cpp index 1f7a232f..4d2c47c3 100644 --- a/opennurbs_decals.cpp +++ b/opennurbs_decals.cpp @@ -1038,9 +1038,8 @@ bool ON_DecalCollection::DeleteDecal(ON_Decal& decal) void ON_DecalCollection::DeleteAllDecals(void) { - // Ensure the array is populated before deleting all the decals. This is not as silly as - // it seems because otherwise it will be populated later and they will all come back. - GetDecalArray(); + m_root_node.Clear(); + m_root_node.CreateNodeAtPath(ON_RDK_UD_ROOT); for (int i = 0; i < m_decals.Count(); i++) { @@ -1049,8 +1048,7 @@ void ON_DecalCollection::DeleteAllDecals(void) m_decals.Destroy(); - m_root_node.Clear(); - m_root_node.SetTagName(L"xml"); // (sigh). + m_populated = true; SetChanged(); } diff --git a/opennurbs_dimensionformat.cpp b/opennurbs_dimensionformat.cpp index 43b5b927..e37afdaa 100644 --- a/opennurbs_dimensionformat.cpp +++ b/opennurbs_dimensionformat.cpp @@ -227,7 +227,9 @@ bool ON_NumberFormatter::FormatNumber( if (0 != wholenumber) { if (0 != numerator) - sFormat.Format(L"%d ", (int)wholenumber); + // RH-71619, removed space + //sFormat.Format(L"%d ", (int)wholenumber); + sFormat.Format(L"%d", (int)wholenumber); else sFormat.Format(L"%d", (int)wholenumber); } @@ -313,7 +315,9 @@ bool ON_NumberFormatter::FormatNumber( if (wholeinches > 0 || include_feet) { if (bracket_fractions) - sInches.Format(L"%d [[%d/%d]]\"", wholeinches, numerator, denominator); + // RH-71619, removed space + //sInches.Format(L"%d [[%d/%d]]\"", wholeinches, numerator, denominator); + sInches.Format(L"%d[[%d/%d]]\"", wholeinches, numerator, denominator); else sInches.Format(L"%d %d/%d\"", wholeinches, numerator, denominator); } diff --git a/opennurbs_layer.cpp b/opennurbs_layer.cpp index ff0b357f..ee56dfd6 100644 --- a/opennurbs_layer.cpp +++ b/opennurbs_layer.cpp @@ -25,6 +25,8 @@ class ON_LayerPrivate { public: ON_LayerPrivate() = default; + ~ON_LayerPrivate() = default; + bool operator==(const ON_LayerPrivate&) const; bool operator!=(const ON_LayerPrivate&) const; @@ -35,6 +37,8 @@ public: int m_section_hatch_index = ON_UNSET_INT_INDEX; // ON_HatchPattern::Unset.Index(); double m_section_hatch_scale = 1.0; double m_section_hatch_rotation = 0.0; + + std::shared_ptr m_custom_linetype; }; static const ON_LayerPrivate DefaultLayerPrivate; @@ -42,6 +46,9 @@ static const ON_LayerPrivate DefaultLayerPrivate; bool ON_LayerPrivate::operator==(const ON_LayerPrivate& other) const { + if (this == &other) + return true; + if (m_clipplane_list != other.m_clipplane_list) return false; @@ -60,6 +67,11 @@ bool ON_LayerPrivate::operator==(const ON_LayerPrivate& other) const if (m_section_hatch_rotation != other.m_section_hatch_rotation) return false; + const ON_Linetype* customThis = m_custom_linetype.get(); + const ON_Linetype* customOther = other.m_custom_linetype.get(); + if (customThis != customOther) + return false; + return true; } @@ -288,6 +300,16 @@ void ON_Layer::Dump( ON_TextLog& dump ) const { dump.Print("section hatch index = %d\n", index); } + + const ON_Linetype* lt = CustomLinetype(); + if (nullptr == lt) + { + dump.Print("no custom linetype\n"); + } + else + { + dump.Print("contains custom linetype\n"); + } } // 28 Sept. 2021 @@ -307,7 +329,11 @@ enum ON_LayerTypeCodes : unsigned char // 14 June 2022 S. Baer // chunk version 1.12: add section fill rule SectionFillRule = 32, - LastLayerTypeCode = 32 + // 30 Nov 2022 S. Baer + // chunk version 1.13: add custom linetype + CustomLinetype = 33, + + LastLayerTypeCode = 33 }; bool ON_Layer::Write( @@ -316,7 +342,7 @@ bool ON_Layer::Write( { int i; - bool rc = file.Write3dmChunkVersion(1,12); + bool rc = file.Write3dmChunkVersion(1,13); while(rc) { // Save the visibility state this layer has when its parent @@ -482,39 +508,56 @@ bool ON_Layer::Write( } // section hatch (1.11) - if (SectionHatchIndex() != ON_UNSET_INT_INDEX) { - c = ON_LayerTypeCodes::SectionHatchIndex; - rc = file.WriteChar(c); - if (!rc) break; - rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::HatchPattern, SectionHatchIndex()); - if (!rc) break; - } - if (SectionHatchScale() != 1.0) - { - c = ON_LayerTypeCodes::SectionHatchScale; - rc = file.WriteChar(c); - if (!rc) break; - rc = file.WriteDouble(SectionHatchScale()); - if (!rc) break; - } - if (SectionHatchRotation() != 0.0) - { - c = ON_LayerTypeCodes::SectionHatchRotation; - rc = file.WriteChar(c); - if (!rc) break; - rc = file.WriteDouble(SectionHatchRotation()); - if (!rc) break; + if (SectionHatchIndex() != ON_UNSET_INT_INDEX) + { + c = ON_LayerTypeCodes::SectionHatchIndex; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::HatchPattern, SectionHatchIndex()); + if (!rc) break; + } + if (SectionHatchScale() != 1.0) + { + c = ON_LayerTypeCodes::SectionHatchScale; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteDouble(SectionHatchScale()); + if (!rc) break; + } + if (SectionHatchRotation() != 0.0) + { + c = ON_LayerTypeCodes::SectionHatchRotation; + rc = file.WriteChar(c); + if (!rc) break; + rc = file.WriteDouble(SectionHatchRotation()); + if (!rc) break; + } } + + // section fill (1.12) if (SectionFillRule() != ON::SectionFillRule::ClosedCurves) { - c = ON_LayerTypeCodes::SectionFillRule; + c = ON_LayerTypeCodes::SectionFillRule; //32 rc = file.WriteChar(c); if (!rc) break; rc = file.WriteChar((unsigned char)SectionFillRule()); if (!rc) break; } + // custom linetype (1.13) + { + const ON_Linetype* linetype = CustomLinetype(); + if (linetype) + { + c = ON_LayerTypeCodes::CustomLinetype; // 33 + rc = file.WriteChar(c); + if (!rc) break; + rc = linetype->Write(file); + if (!rc) break; + } + } + // 0 indicates end of new non-default attributes c = 0; rc = file.WriteChar(c); @@ -765,12 +808,25 @@ bool ON_Layer::Read( if (!rc || 0 == itemid) break; } - // break if minor_version<=12. If itemid is non-zero and - // minor_version is not > 12, then we know we have an I/O - // reading bug that needs to be tracked down if (minor_version <= 12) break; + if (ON_LayerTypeCodes::CustomLinetype == itemid) + { + ON_Linetype lt; + rc = lt.Read(file); + if (!rc) break; + SetCustomLinetype(lt); + rc = file.ReadChar(&itemid); + if (!rc || 0 == itemid) break; + } + + // break if minor_version<=13. If itemid is non-zero and + // minor_version is not > 13, then we know we have an I/O + // reading bug that needs to be tracked down + if (minor_version <= 13) + break; + // Add new item reading above and increment the LastLayerTypeCode value // in the enum. Be sure to test reading of old and new files by old and new // code, before checking in your changes. @@ -2455,3 +2511,24 @@ void ON_Layer::SetSectionHatchRotation(double rotation) m_private = new ON_LayerPrivate(); m_private->m_section_hatch_rotation = rotation; } + +void ON_Layer::SetCustomLinetype(const ON_Linetype& linetype) +{ + if (nullptr == m_private) + m_private = new ON_LayerPrivate(); + m_private->m_custom_linetype.reset(new ON_Linetype(linetype)); +} + +const ON_Linetype* ON_Layer::CustomLinetype() const +{ + const ON_Linetype* rc = nullptr; + if (m_private) + rc = m_private->m_custom_linetype.get(); + return rc; +} +void ON_Layer::RemoveCustomLinetype() +{ + if (m_private) + m_private->m_custom_linetype.reset(); +} + diff --git a/opennurbs_layer.h b/opennurbs_layer.h index 26c72c51..0a0edec3 100644 --- a/opennurbs_layer.h +++ b/opennurbs_layer.h @@ -326,6 +326,29 @@ public: */ int LinetypeIndex() const; + /* + Description: + Layers can have optional custom linetypes associated with them. When a custom + linetype is attached to a layer, this linetype is used for a layer instead of + the linetype referenced by the linetype index. This function adds a custom + linetype for this layer. + */ + void SetCustomLinetype(const ON_Linetype& linetype); + + /* + Description: + Layers can have optional custom linetypes associated with them. This function + returns the custom linetype if one exists. If a custom linetype is not + attached to this layer, then an empty shared pointer is returned + */ + const ON_Linetype* CustomLinetype() const; + + /* + Description: + Remove any custom linetype associated with this layer + */ + void RemoveCustomLinetype(); + /* Returns: Returns true if objects on layer are visible. @@ -837,6 +860,7 @@ public: double SectionHatchRotation() const; void SetSectionHatchRotation(double rotation); #pragma endregion + private: // The following information may not be accurate and is subject // to change at any time. diff --git a/opennurbs_linetype.cpp b/opennurbs_linetype.cpp index bc46cdcd..7b4ec613 100644 --- a/opennurbs_linetype.cpp +++ b/opennurbs_linetype.cpp @@ -21,6 +21,13 @@ #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif +class ON_LinetypePrivate +{ +public: + ON_SimpleArray m_segments; + ON_SimpleArray m_taper_points; +}; + bool ON_IsHairlinePrintWidth(double width_mm) { if(width_mm > 0.0 && width_mm < 0.001) @@ -99,25 +106,51 @@ const ON_Linetype* ON_Linetype::FromModelComponentRef( ON_Linetype::ON_Linetype() ON_NOEXCEPT : ON_ModelComponent(ON_ModelComponent::Type::LinePattern) -{} +{ + m_private = new ON_LinetypePrivate(); +} ON_Linetype::ON_Linetype( const ON_Linetype& src ) : ON_ModelComponent(ON_ModelComponent::Type::LinePattern,src) - , m_segments(src.m_segments) + , m_is_set_bits(src.m_is_set_bits) + , m_is_locked_bits(src.m_is_locked_bits) , m_cap_style(src.m_cap_style) , m_join_style(src.m_join_style) + , m_width(src.m_width) + , m_width_units(src.m_width_units) { + m_private = new ON_LinetypePrivate(*src.m_private); } +ON_Linetype::~ON_Linetype() +{ + delete m_private; +} + +ON_Linetype& ON_Linetype::operator=(const ON_Linetype& other) +{ + if (this != &other) + { + ON_ModelComponent::operator=(other); + m_is_set_bits = other.m_is_set_bits; + m_is_locked_bits = other.m_is_locked_bits; + m_cap_style = other.m_cap_style; + m_join_style = other.m_join_style; + m_width = other.m_width; + m_width_units = other.m_width_units; + *m_private = *other.m_private; + } + return *this; +} bool ON_Linetype::IsValid( ON_TextLog* text_log ) const { - int i, count = m_segments.Count(); - if (false == ON_ModelComponent::IsValid(text_log)) return false; // An ON_Linetype with an empty name is valid. + const ON_SimpleArray& segments = m_private->m_segments; + int count = segments.Count(); if ( count < 1 ) { if ( text_log ) @@ -127,14 +160,14 @@ bool ON_Linetype::IsValid( ON_TextLog* text_log ) const if ( 1 == count ) { - if ( m_segments[0].m_length <= 0.0 ) + if ( segments[0].m_length <= 0.0 ) { if ( text_log ) text_log->Print("ON_Linetype bogus single segment linetype - length <= 0.0 (it must be > 0)\n"); return false; } - if ( ON_LinetypeSegment::eSegType::stLine != m_segments[0].m_seg_type ) + if ( ON_LinetypeSegment::eSegType::stLine != segments[0].m_seg_type ) { if ( text_log ) text_log->Print("ON_Linetype bogus single segment linetype - type != stLine\n"); @@ -143,16 +176,16 @@ bool ON_Linetype::IsValid( ON_TextLog* text_log ) const } else { - for (i = 0; i < count; i++ ) + for (int i = 0; i < count; i++ ) { - if ( m_segments[i].m_length < 0.0 ) + if ( segments[i].m_length < 0.0 ) { if ( text_log ) text_log->Print("ON_Linetype segment has negative length.\n"); return false; } - if ( ON_LinetypeSegment::eSegType::stLine != m_segments[i].m_seg_type && ON_LinetypeSegment::eSegType::stSpace != m_segments[i].m_seg_type ) + if ( ON_LinetypeSegment::eSegType::stLine != segments[i].m_seg_type && ON_LinetypeSegment::eSegType::stSpace != segments[i].m_seg_type ) { if ( text_log ) text_log->Print("ON_Linetype segment has invalid m_seg_type.\n"); @@ -161,14 +194,14 @@ bool ON_Linetype::IsValid( ON_TextLog* text_log ) const if ( i ) { - if ( m_segments[i].m_seg_type == m_segments[i-1].m_seg_type ) + if ( segments[i].m_seg_type == segments[i-1].m_seg_type ) { if ( text_log ) text_log->Print("ON_Linetype consecutive segments have same type.\n"); return false; } - if ( 0.0 == m_segments[i].m_length && 0.0 == m_segments[i-1].m_length ) + if ( 0.0 == segments[i].m_length && 0.0 == segments[i-1].m_length ) { if ( text_log ) text_log->Print("ON_Linetype consecutive segments have length zero.\n"); @@ -188,12 +221,13 @@ bool ON_Linetype::IsValid( ON_TextLog* text_log ) const void ON_Linetype::Dump( ON_TextLog& dump ) const { ON_ModelComponent::Dump(dump); - dump.Print( "Segment count = %d\n", m_segments.Count()); + const int segmentCount = SegmentCount(); + dump.Print( "Segment count = %d\n", segmentCount); dump.Print( "Pattern length = %g\n", PatternLength()); dump.Print( "Pattern = (" ); - for( int i = 0; i < m_segments.Count(); i++) + for( int i = 0; i < segmentCount; i++) { - const ON_LinetypeSegment& seg = m_segments[i]; + const ON_LinetypeSegment& seg = m_private->m_segments[i]; if ( i ) dump.Print(","); switch( seg.m_seg_type) @@ -238,8 +272,35 @@ void ON_Linetype::Dump( ON_TextLog& dump ) const dump.Print("Join = Round\n"); break; } + + dump.Print("Width = %d\n", Width()); + ON_UnitSystem us(WidthUnits()); + ON_wString usn = us.UnitSystemName(); + const wchar_t* s = usn.Array(); + dump.Print("Width Units = %ls\n", s); + + const ON_SimpleArray* taper = TaperPoints(); + if (taper && taper->Count()>0) + { + dump.Print("Taper count = %d\n", taper->Count()); + } } +// 29 Nov. 2022 S. Baer +// This enum is patterned off of ON_3dmObjectAttributeTypeCode +enum ON_LinetypeTypeCodes : unsigned char +{ + LineCap = 1, + LineJoin = 2, + // minor version = 2 + // add width, width units, taper + Width = 3, + WidthUnits = 4, + Taper = 5, + + LastLinetypeTypeCode = 5 +}; + bool ON_Linetype::Write( ON_BinaryArchive& file) const { bool rc = false; @@ -258,7 +319,7 @@ bool ON_Linetype::Write( ON_BinaryArchive& file) const if (!file.WriteString(linetype_name)) break; - if (!file.WriteArray(m_segments)) + if (!file.WriteArray(m_private->m_segments)) break; // chunk version 1.1 fields @@ -273,7 +334,9 @@ bool ON_Linetype::Write( ON_BinaryArchive& file) const { // 12 Aug 2021 S. Baer (RH-2285) // minor_version = 1: add cap and join styles to linetype - const int minor_version = 1; + // 29 Nov 2022 S. Baer (RH-7262) + // minor_version = 2: add width, width units, and taper + const int minor_version = 2; if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 2, minor_version)) return false; for (;;) @@ -282,13 +345,13 @@ bool ON_Linetype::Write( ON_BinaryArchive& file) const if (!file.WriteModelComponentAttributes(*this,ON_ModelComponent::Attributes::BinaryArchiveAttributes)) break; - if (!file.WriteArray(m_segments)) + if (!file.WriteArray(m_private->m_segments)) break; // Only write non-default values in a similar fashion as ON_3dmObjectAttributes if (m_cap_style != ON::LineCapStyle::Round) { - const unsigned char itemType = 1; + const unsigned char itemType = ON_LinetypeTypeCodes::LineCap; // 1 if (!file.WriteChar(itemType)) break; if (!file.WriteChar((unsigned char)m_cap_style)) @@ -297,13 +360,44 @@ bool ON_Linetype::Write( ON_BinaryArchive& file) const if (m_join_style != ON::LineJoinStyle::Round) { - const unsigned char itemType = 2; + const unsigned char itemType = ON_LinetypeTypeCodes::LineJoin; // 2 if (!file.WriteChar(itemType)) break; if (!file.WriteChar((unsigned char)m_join_style)) break; } + if (fabs(Width() - 1.0) > ON_EPSILON) + { + const unsigned char itemType = ON_LinetypeTypeCodes::Width; // 3 + if (!file.WriteChar(itemType)) + break; + if (!file.WriteDouble(Width())) + break; + } + + if (WidthUnits() != ON::LengthUnitSystem::None) + { + const unsigned char itemType = ON_LinetypeTypeCodes::WidthUnits; // 4 + if (!file.WriteChar(itemType)) + break; + + unsigned char units = (unsigned char)WidthUnits(); + if (!file.WriteChar(units)) + break; + } + + const ON_SimpleArray* taper = TaperPoints(); + if (taper && taper->Count() > 0) + { + const unsigned char itemType = ON_LinetypeTypeCodes::Taper; // 5 + if (!file.WriteChar(itemType)) + break; + + if (!file.WriteArray(*taper)) + break; + } + // 0 indicates end of new linetype attributes const unsigned char attributes_end = 0; if (!file.WriteChar(attributes_end)) @@ -347,7 +441,7 @@ bool ON_Linetype::Read( ON_BinaryArchive& file) break; SetName(linetype_name); - if (!file.ReadArray(m_segments)) + if (!file.ReadArray(m_private->m_segments)) break; if (minor_version >= 1) @@ -371,33 +465,35 @@ bool ON_Linetype::Read( ON_BinaryArchive& file) if (!file.ReadModelComponentAttributes(*this,&model_component_attributes_filter)) break; - if (!file.ReadArray(m_segments)) + if (!file.ReadArray(m_private->m_segments)) break; + unsigned char item_id = 0; // 12 Aug 2021 S. Baer (RH-2285) // Add cap and join style to linetype if (minor_version >= 1) { - unsigned char item_id = 0; if (!file.ReadChar(&item_id)) break; - if (1 == item_id) + if (ON_LinetypeTypeCodes::LineCap == item_id) { unsigned char cap = 0; if (!file.ReadChar(&cap)) break; m_cap_style = ON::LineCapStyleFromUnsigned(cap); + if (!file.ReadChar(&item_id)) break; } - if (2 == item_id) + if (ON_LinetypeTypeCodes::LineJoin == item_id) { unsigned char join = 0; if (!file.ReadChar(&join)) break; m_join_style = ON::LineJoinStyleFromUnsigned(join); + if (!file.ReadChar(&item_id)) break; } @@ -406,13 +502,58 @@ bool ON_Linetype::Read( ON_BinaryArchive& file) { ON_ERROR("Bug in ON_Linetype::Read for chunk version 2.1"); } + } - if (item_id > 2) + // 12 Aug 2021 S. Baer (RH-2285) + // Add cap and join style to linetype + if (minor_version >= 2) + { + if (ON_LinetypeTypeCodes::Width == item_id) { - // we are reading file written with code newer - // than this code (minor_version > 1) - item_id = 0; + double width = 1; + if (!file.ReadDouble(&width)) + break; + SetWidth(width); + + if (!file.ReadChar(&item_id)) + break; } + + if (ON_LinetypeTypeCodes::WidthUnits == item_id) + { + unsigned char width_units = 0; + if (!file.ReadChar(&width_units)) + break; + SetWidthUnits(ON::LengthUnitSystemFromUnsigned(width_units)); + + if (!file.ReadChar(&item_id)) + break; + } + + if (ON_LinetypeTypeCodes::Taper == item_id) + { + ON_SimpleArray taper; + if (!file.ReadArray(taper)) + break; + if (nullptr == m_private) + m_private = new ON_LinetypePrivate(); + m_private->m_taper_points = taper; + + if (!file.ReadChar(&item_id)) + break; + } + + if (2 == minor_version && item_id != 0) + { + ON_ERROR("Bug in ON_Linetype::Read for chunk version 2.2"); + } + } + + if (item_id > ON_LinetypeTypeCodes::LastLinetypeTypeCode) + { + // we are reading file written with code newer + // than this code (minor_version > 2) + item_id = 0; } rc = true; @@ -437,7 +578,7 @@ bool ON_Linetype::ClearPattern() if (false == PatternIsLocked()) { m_is_set_bits &= ~ON_Linetype::pattern_bit; - m_segments.Destroy(); + m_private->m_segments.Destroy(); } return (false == PatternIsSet()); } @@ -455,10 +596,10 @@ void ON_Linetype::LockPattern() double ON_Linetype::PatternLength() const { double length = 0.0; - int seg_count = m_segments.Count(); + int seg_count = m_private->m_segments.Count(); for( int i = 0; i < seg_count; i++) { - length += m_segments[i].m_length; + length += m_private->m_segments[i].m_length; } return length; } @@ -467,17 +608,17 @@ ON_SimpleArray* ON_Linetype::ExpertSegments() { if (PatternIsLocked()) return nullptr; - return &m_segments; + return &m_private->m_segments; } const ON_SimpleArray& ON_Linetype::Segments() const { - return m_segments; + return m_private->m_segments; } int ON_Linetype::SegmentCount() const { - return m_segments.Count(); + return m_private->m_segments.Count(); } int ON_Linetype::AppendSegment( const ON_LinetypeSegment& segment) @@ -485,17 +626,17 @@ int ON_Linetype::AppendSegment( const ON_LinetypeSegment& segment) if (PatternIsLocked()) return -1; - m_segments.Append( segment); - return( m_segments.Count()-1); + m_private->m_segments.Append( segment); + return(m_private->m_segments.Count()-1); } bool ON_Linetype::RemoveSegment( int index ) { if (PatternIsLocked()) return false; - bool rc = ( index >= 0 && index < m_segments.Count()); + bool rc = ( index >= 0 && index < m_private->m_segments.Count()); if (rc) - m_segments.Remove(index); + m_private->m_segments.Remove(index); return rc; } @@ -504,9 +645,9 @@ bool ON_Linetype::SetSegment( int index, const ON_LinetypeSegment& segment) if (PatternIsLocked()) return false; - if( index >= 0 && index < m_segments.Count()) + if( index >= 0 && index < m_private->m_segments.Count()) { - m_segments[index] = segment; + m_private->m_segments[index] = segment; return true; } else @@ -518,10 +659,10 @@ bool ON_Linetype::SetSegment( int index, double length, ON_LinetypeSegment::eSeg if (PatternIsLocked()) return false; - if( index >= 0 && index < m_segments.Count()) + if( index >= 0 && index < m_private->m_segments.Count()) { - m_segments[index].m_length = length; - m_segments[index].m_seg_type = type; + m_private->m_segments[index].m_length = length; + m_private->m_segments[index].m_seg_type = type; return true; } else @@ -533,15 +674,15 @@ bool ON_Linetype::SetSegments(const ON_SimpleArray& segments if (PatternIsLocked()) return false; - m_segments = segments; + m_private->m_segments = segments; return true; } ON_LinetypeSegment ON_Linetype::Segment( int index) const { - if( index >= 0 && index < m_segments.Count()) - return m_segments[index]; + if( index >= 0 && index < m_private->m_segments.Count()) + return m_private->m_segments[index]; else return ON_LinetypeSegment::OneMillimeterLine; } @@ -566,6 +707,59 @@ ON::LineJoinStyle ON_Linetype::LineJoinStyle() const return m_join_style; } +double ON_Linetype::Width() const +{ + return m_width; +} +void ON_Linetype::SetWidth(double width) +{ + m_width = width; +} +ON::LengthUnitSystem ON_Linetype::WidthUnits() const +{ + return m_width_units; +} +void ON_Linetype::SetWidthUnits(ON::LengthUnitSystem units) +{ + m_width_units = units; +} +const ON_SimpleArray* ON_Linetype::TaperPoints() const +{ + if (m_private->m_taper_points.Count() < 1) + return nullptr; + return &(m_private->m_taper_points); +} +bool ON_Linetype::SetTaper(double startWidth, double endWidth) +{ + if (startWidth < 0 || endWidth < 0) + return false; + m_private->m_taper_points.Empty(); + m_private->m_taper_points.Append(ON_2dPoint(0, startWidth)); + m_private->m_taper_points.Append(ON_2dPoint(1.0, endWidth)); + return true; +} +bool ON_Linetype::SetTaper(double startWidth, ON_2dPoint taperPoint, double endWidth) +{ + if (startWidth < 0 || endWidth < 0) + return false; + if (!taperPoint.IsValid()) + return false; + if (taperPoint.x < 0.0 || taperPoint.x > 1.0) + return false; + if (taperPoint.y < 0.0) + return false; + + m_private->m_taper_points.Empty(); + m_private->m_taper_points.Append(ON_2dPoint(0, startWidth)); + m_private->m_taper_points.Append(taperPoint); + m_private->m_taper_points.Append(ON_2dPoint(1.0, endWidth)); + return true; +} + +void ON_Linetype::RemoveTaper() +{ + m_private->m_taper_points.Empty(); +} diff --git a/opennurbs_linetype.h b/opennurbs_linetype.h index 70e6eab8..3f85c4d2 100644 --- a/opennurbs_linetype.h +++ b/opennurbs_linetype.h @@ -87,9 +87,9 @@ public: public: ON_Linetype() ON_NOEXCEPT; - ~ON_Linetype() = default; + ~ON_Linetype(); ON_Linetype(const ON_Linetype&); - ON_Linetype& operator=(const ON_Linetype&) = default; + ON_Linetype& operator=(const ON_Linetype&); /* Description: @@ -217,17 +217,77 @@ public: Corner join style for curves */ ON::LineJoinStyle LineJoinStyle() const; + + + /* + Description: + Width of the linetype + */ + double Width() const; + + /* + Description: + Set the width of the linetype + */ + void SetWidth(double width); + + /* + Description: + Units used to define the linetype width + None = width is defined in pixels (default) + Unset = width is the same as the document's unitsystem + */ + ON::LengthUnitSystem WidthUnits() const; + + /* + Description: + Set the units used to define the linetype width + None = width is defined in pixels (default) + Unset = width is the same as the document's unitsystem + */ + void SetWidthUnits(ON::LengthUnitSystem units); + + /* + Description: + List of points defining a taper. For each point in the taper + the point's x value is between 0.00 and 1.00 and represents the % along the length of the curve + the point's y value is the width used at x + Returns nullptr if no taper points exist for this linetype + */ + const ON_SimpleArray* TaperPoints() const; + + /* + Description: + Set the taper for this linetype to a simple start and end width + */ + bool SetTaper(double startWidth, double endWidth); + + /* + Description: + Set the taper for this linetype with a single taper point + */ + bool SetTaper(double startWidth, ON_2dPoint taperPoint, double endWidth); + + /* + Description: + Remove taper points from this linetype + */ + void RemoveTaper(); + private: - enum : unsigned char - { - pattern_bit = 1 - }; + mutable class ON_LinetypePrivate* m_private = nullptr; unsigned char m_is_set_bits = 0; unsigned char m_is_locked_bits = 0; ON::LineCapStyle m_cap_style = ON::LineCapStyle::Round; ON::LineJoinStyle m_join_style = ON::LineJoinStyle::Round; - unsigned int m_reserved = 0; - ON_SimpleArray m_segments; + double m_width = 1.0; + ON::LengthUnitSystem m_width_units = ON::LengthUnitSystem::None; + unsigned char m_reserved[7] = { 0 }; + + enum : unsigned char + { + pattern_bit = 1 + }; }; #if defined(ON_DLL_TEMPLATE) diff --git a/opennurbs_lookup.cpp b/opennurbs_lookup.cpp index 393d4eef..c5f929d0 100644 --- a/opennurbs_lookup.cpp +++ b/opennurbs_lookup.cpp @@ -23,43 +23,33 @@ static bool IdIsNotNil(const ON_UUID* id) { - // This is a fast static function to check for - // zero ids. The caller always checks that - // id is not null. + // This is a fast static function to check for zero ids. + // The caller always checks that id is not null. const ON__UINT64* p = (const ON__UINT64*)id; return (p[0] != 0 || p[1] != 0); } static bool IdIsNil(const ON_UUID* id) { - // This is a fast static function to check for - // zero ids. The caller always checks that - // id is not null. + // This is a fast static function to check for zero ids. + // The caller always checks that id is not null. const ON__UINT64* p = (const ON__UINT64*)id; return (p[0] == 0 && p[1] == 0); } -static bool IdIsEqual( - const ON_UUID* a, - const ON_UUID* b - ) +static bool IdIsEqual(const ON_UUID* a, const ON_UUID* b) { - // This is a fast static function to check for - // zero ids. The caller always checks that - // id is not null. + // This is a fast static function to check for zero ids. + // The caller always checks that id is not null. const ON__UINT64* p = (const ON__UINT64*)a; const ON__UINT64* q = (const ON__UINT64*)b; return (p[0] == q[0] && p[1] == q[1]); } -static bool IdIsNotEqual( - const ON_UUID* a, - const ON_UUID* b - ) +static bool IdIsNotEqual(const ON_UUID* a, const ON_UUID* b) { - // This is a fast static function to check for - // zero ids. The caller always checks that - // id is not null. + // This is a fast static function to check for zero ids. + // The caller always checks that id is not null. const ON__UINT64* p = (const ON__UINT64*)a; const ON__UINT64* q = (const ON__UINT64*)b; return (p[0] != q[0] || p[1] != q[1]); @@ -706,18 +696,19 @@ ON__UINT64 ON_SerialNumberMap::ActiveIdCount() const return m_active_id_count; } -struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FirstElement() const +ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FirstElement() const { - struct SN_ELEMENT* e=0; - size_t i,j; + SN_ELEMENT* e=nullptr; // The first element is likely to be m_snblk_list[0]->m_sn[0] // so start looking there. - for(i = 0; i < m_snblk_list_count; i++) + // The nullptr==e test isn't necessary. It is there to keep + // the VisualC++ 2022 compiler from crashing. + for(size_t i = 0; i < m_snblk_list_count && nullptr==e; i++) { if ( m_snblk_list[i]->m_count > m_snblk_list[i]->m_purged ) { - for ( j = 0; j < m_snblk_list[i]->m_count; j++ ) + for (size_t j = 0; j < m_snblk_list[i]->m_count; j++ ) { if ( m_snblk_list[i]->m_sn[j].m_sn_active ) { diff --git a/opennurbs_point.cpp b/opennurbs_point.cpp index 188cf723..e45b5388 100644 --- a/opennurbs_point.cpp +++ b/opennurbs_point.cpp @@ -580,6 +580,8 @@ void ON_Interval::Set(double t0,double t1) double ON_Interval::ParameterAt(double x) const { + if (m_t[0] == m_t[1]) + x = 0.0; return (ON_IS_VALID(x) ? ((1.0-x)*m_t[0] + x*m_t[1]) : ON_UNSET_VALUE); } diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index a7611da3..cf60d049 100644 --- a/opennurbs_public_version.h +++ b/opennurbs_public_version.h @@ -14,10 +14,10 @@ // first step in each build. // #define RMA_VERSION_YEAR 2022 -#define RMA_VERSION_MONTH 11 -#define RMA_VERSION_DATE 21 -#define RMA_VERSION_HOUR 11 -#define RMA_VERSION_MINUTE 10 +#define RMA_VERSION_MONTH 12 +#define RMA_VERSION_DATE 1 +#define RMA_VERSION_HOUR 14 +#define RMA_VERSION_MINUTE 39 //////////////////////////////////////////////////////////////// // @@ -35,8 +35,8 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 8,0,22325,11100 -#define VERSION_WITH_PERIODS 8.0.22325.11100 +#define VERSION_WITH_COMMAS 8,0,22335,14390 +#define VERSION_WITH_PERIODS 8.0.22335.14390 #define COPYRIGHT "Copyright (C) 1993-2022, Robert McNeel & Associates. All Rights Reserved." #define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library." @@ -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 "8.0.22325.11100" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.0.22325.11100" +#define RMA_VERSION_WITH_PERIODS_STRING "8.0.22335.14390" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.0.22335.14390" diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp index 82be538b..eac32383 100644 --- a/opennurbs_statics.cpp +++ b/opennurbs_statics.cpp @@ -128,6 +128,8 @@ const ON_SubDComponentLocation ON_SubD::DefaultSubDAppearance = ON_SubDComponent // The default type must be packed, unpacked, zero, or nan and should be packed or upacked. const ON_SubDTextureCoordinateType ON_SubD::DefaultTextureCoordinateType = ON_SubDTextureCoordinateType::Packed; +const double ON_SubDEdge::InfinteSharpness = 5.0; +const double ON_SubDEdge::SharpnessTolerance = 0.01; const double ON_SubDSectorType::MinimumCornerAngleRadians = (2.0*ON_PI)/((double)(ON_SubDSectorType::MaximumCornerAngleIndex)); const double ON_SubDSectorType::MaximumCornerAngleRadians = 2.0*ON_PI - ON_SubDSectorType::MinimumCornerAngleRadians; diff --git a/opennurbs_string.h b/opennurbs_string.h index a86f79f7..4a3ea3ce 100644 --- a/opennurbs_string.h +++ b/opennurbs_string.h @@ -2718,6 +2718,9 @@ public: /// RUBLE SIGN U+20BD (₽) static const wchar_t RubleSign = (wchar_t)ON_UnicodeCodePoint::ON_RubleSign; + /// INFINITY SYMBOL U+221E (∞) + static const wchar_t InfinitySymbol = (wchar_t)ON_UnicodeCodePoint::ON_InfinitySymbol; + /// /// UNIVERSAL RECYCLING SYMBOL U+2672 (♲) /// This is a good code point for testing glyph substitution. diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp index 252d7727..9a7968ba 100644 --- a/opennurbs_subd.cpp +++ b/opennurbs_subd.cpp @@ -346,7 +346,6 @@ ON_SubDEdgeTag ON_SubD::EdgeTagFromUnsigned( ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEdgeTag::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEdgeTag::Smooth); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEdgeTag::Crease); - //ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEdgeTag::Sharp); ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDEdgeTag::SmoothX); } return ON_SUBD_RETURN_ERROR(ON_SubDEdgeTag::Unset); @@ -388,7 +387,6 @@ bool ON_SubD::EdgeTagIsSet( return ( ON_SubDEdgeTag::Smooth == edge_tag || ON_SubDEdgeTag::Crease == edge_tag - //|| ON_SubDEdgeTag::Sharp == edge_tag || ON_SubDEdgeTag::SmoothX == edge_tag ); } @@ -3709,6 +3707,404 @@ bool ON_SubDEdge::IsSmoothX() const return (ON_SubDEdgeTag::SmoothX == m_edge_tag) ? true : false; } + +// These semisharp edge tools need to be out of the protected ON_SUBD_SHARP_EDGES +// in order for public opennurbs to work. They simply provide access to +// ON_SubDEdge::m_sharpness which has been in public opennurbs since 7.0. +double ON_SubDEdge::Sharpness() const +{ + return Sharpness(ON_DBL_QNAN, 0.0, ON_SubDEdge::InfinteSharpness); +} + +double ON_SubDEdge::Sharpness( + double unset_edge_value, + double smooth_edge_value, + double crease_edge_value +) const +{ + if (IsCrease()) + return crease_edge_value; + if (IsSmooth()) + return ((m_sharpness >= 0.0 && m_sharpness < ON_SubDEdge::InfinteSharpness) ? m_sharpness : 0.0); + return unset_edge_value; +} + +void ON_SubDEdge::SetSharpness(double sharpness) +{ + m_sharpness = IsSmooth() ? ON_SubDEdge::SanitizeSharpness(sharpness, false) : 0.0; +} + +double ON_SubDEdge::SanitizeSharpness(double sharpness, bool bPermitInfinteSharpness) +{ + // When sharpness is withing ON_SubDEdge::SharpnessTolerance of an integer value, + // snap to that integer value. + const double f = floor(sharpness); + if (sharpness - f <= ON_SubDEdge::SharpnessTolerance) + sharpness = f; + else if (f + 1.0 - sharpness <= ON_SubDEdge::SharpnessTolerance) + sharpness = f + 1.0; + + if (sharpness >= 0.0 && sharpness < ON_SubDEdge::InfinteSharpness) + return sharpness; + + if (ON_SubDEdge::InfinteSharpness == sharpness && bPermitInfinteSharpness) + return ON_SubDEdge::InfinteSharpness; + + return 0.0; +} + +#if defined(ON_SUBD_SHARP_EDGES) + +bool ON_SubD::HasSemisharpness() const +{ + ON_SubDEdgeIterator eit = this->EdgeIterator(); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + if (e->IsSemisharp()) + return true; + } + return false; +} + +unsigned int ON_SubD::SemisharpEdgeCount(ON_Interval& sharpness_range) const +{ + unsigned int sharp_edge_count = 0; + double mins = 0.0; + double maxs = 0.0; + ON_SubDEdgeIterator eit = this->EdgeIterator(); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + const double s = e->Sharpness(); + if (s > 0.0 && s < ON_SubDEdge::InfinteSharpness) + { + if (0 == sharp_edge_count) + { + mins = s; + maxs = s; + } + else if (s < mins) + mins = s; + else if (s > maxs) + maxs = s; + ++sharp_edge_count; + } + } + sharpness_range.Set(mins, maxs); + return sharp_edge_count; +} + +unsigned int ON_SubD::SemisharpEdgeCount() const +{ + ON_Interval sharpness_range(0.0, 0.0); + return SemisharpEdgeCount(sharpness_range); +} + +bool ON_SubDVertex::IsSemisharp() const +{ + // This definition of vertex sharpness is from DeRose, Kass, Truong 1998 + // If there are exactly two sharp edges adjacent to the vertex, then + // the vertex sharpness is the average of the edge sharpnesses. + if (IsSmooth() && nullptr != m_edges) + { + 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->IsSemisharp()) + return true; + } + } + return false; +} + + +unsigned int ON_SubD::ClearSemisharpness() +{ + unsigned int semisharp_edge_count = 0; + ON_SubDEdgeIterator eit = this->EdgeIterator(); + for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) + { + const double s = e->Sharpness(); + if (false == (s== 0.0)) + { + const_cast(e)->SetSharpness(0.0); + ++semisharp_edge_count; + } + } + + if (semisharp_edge_count != 0) + this->ChangeGeometryContentSerialNumberForExperts(true); + + return semisharp_edge_count; +} + +double ON_SubDVertex::Sharpness() const +{ + ON_3dPoint sharp_subdivision_point; + return GetSharpSubdivisionPoint(sharp_subdivision_point); +} + + +double ON_SubDVertex::GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_point) const +{ + // This definition of vertex sharpness is from DeRose, Kass, Truong 1998 + // If the vertex is adjacent to zero or one semisharp edges, + // then the ordindary vertex subdivision rule is used and 0.0 is returned. + // If the vertex is adjacent to 3 or more semisharp edges, + // then the corner vertex subdivision rule is used and 10.0 is returned. + // If there are exactly two semisharp edges adjacent to the vertex, + // then the vertex sharpness is the average of the edge sharpnesses. + // If this vertex sharpness is >= 1, then the corner vertex subdivision rule is used. + // Otherwise, 0 < vertex sharpness < 1 and the semisharp vertex subdivision point is + // (1-vertex sharpness)*(ordinary subdivision point) + (vertex sharpness)*(corner subdivision point). + sharp_subdivision_point = ON_3dPoint::NanPoint; + + if (((this->IsSmoothOrDart() && m_face_count==m_edge_count) || (this->IsCrease() && m_edge_count >= 3 && m_face_count+((unsigned short)1) == m_edge_count)) + && nullptr != m_edges) + { + // This is a single sector vertex that might have a semisharp edge attached. + + unsigned int crease_edge_count = 0; + unsigned int sharp_edge_count = 0; + double sharpness[2] = { 0.0, 0.0 }; + const ON_SubDVertex* other_v[2] = { nullptr,nullptr }; + 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) + { + const double edge_sharpness = e->Sharpness(ON_DBL_QNAN,0.0,ON_SubDEdge::InfinteSharpness); + if (edge_sharpness > 0.0) + { + const unsigned n = crease_edge_count + sharp_edge_count; + if (n < 2) + { + other_v[n] = e->OtherEndVertex(this); + sharpness[n] = edge_sharpness; + } + if (ON_SubDEdge::InfinteSharpness == edge_sharpness) + ++crease_edge_count; // dart's crease edge or one of the corner's crease edges + else if (edge_sharpness < ON_SubDEdge::InfinteSharpness) + ++sharp_edge_count; // semisharp edge + else + continue; + if (n >= 2 && sharp_edge_count > 0) + { + sharp_subdivision_point = this->ControlNetPoint(); + return ON_SubDEdge::InfinteSharpness; + } + } + } + } + + if (2 == crease_edge_count+sharp_edge_count && sharp_edge_count > 0) + { + if (nullptr != other_v[0] && nullptr != other_v[1]) + sharp_subdivision_point = 0.125 * (6.0 * ControlNetPoint() + other_v[0]->ControlNetPoint() + other_v[1]->ControlNetPoint()); + return ON_SubDEdge::AverageSharpness(sharpness[0], sharpness[1]); + } + + sharp_subdivision_point = ON_3dPoint::NanPoint; + return 0.0; + } + + sharp_subdivision_point = ON_3dPoint::NanPoint; + return 0.0; +} + + +double ON_SubDVertex::EvaluationSharpness(ON_3dPoint& sharp_subdivision_point) const +{ + // WARNING: + // This function is temporary and for internal use only. + // It will be removed in the near future. + // Any code referencing this function will unpredictably fail in the near future. + // This is primative and is not thread safe. + double vertex_sharpness; + if (ON_SubDEdge::SemisharpEvaluationIsEnabled()) + { + vertex_sharpness = GetSharpSubdivisionPoint(sharp_subdivision_point); + } + else + { + sharp_subdivision_point = ON_3dPoint::NanPoint; + vertex_sharpness = 0.0; + } + return vertex_sharpness; +} + +unsigned int ON_SubDVertex::SemisharpEdgeCount() const +{ + ON_Interval sharpness_range; + return SemisharpEdgeCount(sharpness_range); +} + +unsigned int ON_SubDVertex::SemisharpEdgeCount(ON_Interval& sharpness_range) const +{ + unsigned int semisharp_edge_count = 0; + double mins = 0.0; + double maxs = 0.0; + if (IsSmooth() && nullptr != m_edges) + { + for (unsigned short vei = 0; vei < m_edge_count; ++vei) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr); + const double s = (nullptr != e) ? e->Sharpness(ON_DBL_QNAN, 0.0, ON_SubDEdge::InfinteSharpness) : 0.0; + if (s > 0.0 && s < ON_SubDEdge::InfinteSharpness) + { + if (0 == semisharp_edge_count) + { + mins = s; + maxs = s; + } + else if (s < mins) + mins = s; + else if (s > maxs) + maxs = s; + ++semisharp_edge_count; + } + } + } + sharpness_range.Set(mins, maxs); + return semisharp_edge_count; +} + +double ON_SubDEdge::AverageSharpness( + double sharpness0, + double sharpness1 +) +{ + if (sharpness0 >= 0.0 && sharpness0 <= ON_SubDEdge::InfinteSharpness && sharpness1 >= 0.0 && sharpness1 <= ON_SubDEdge::InfinteSharpness) + { + if (sharpness0 == sharpness1) + return sharpness0; + if (sharpness0 == ON_SubDEdge::InfinteSharpness || sharpness1 == ON_SubDEdge::InfinteSharpness) + return ON_SubDEdge::InfinteSharpness; + if (0.0 == sharpness0) + return sharpness1; + if (0.0 == sharpness1) + return sharpness0; + return ON_SubDEdge::SanitizeSharpness(0.5 * (sharpness0 + sharpness1), false); + } + return 0.0; +} + +bool ON_SubDEdge::IsSemisharp() const +{ + return IsSmooth() && Sharpness() > 0.0; +} + +double ON_SubDEdge::GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_point) const +{ + const double edge_sharpness = Sharpness(); + if (edge_sharpness > 0.0) + { + sharp_subdivision_point = this->ControlNetCenterPoint(); + return edge_sharpness; + } + + sharp_subdivision_point = ON_3dPoint::NanPoint; + return 0.0; +} + +double ON_SubDEdge::SubdivideSharpness( + unsigned end_index +) const +{ + double sharpness1 = 0.0; + if (end_index >= 0 && end_index <= 1 && nullptr != m_vertex[end_index] && nullptr != m_vertex[end_index]->m_edges ) + { + const double sharpness0 = Sharpness(); + if (0.0 < sharpness0 && sharpness0 < ON_SubDEdge::InfinteSharpness) + { + if (m_vertex[end_index]->IsSmooth()) + { + double other_sharpness0 = 0.0; + const ON_SubDVertex* v = m_vertex[end_index]; + for (unsigned short vei = 0; vei < v->m_edge_count; ++vei) + { + const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(v->m_edges[vei].m_ptr); + if (nullptr == e || this == e) + continue; + const double s = e->Sharpness(); + if (0.0 < s && s < ON_SubDEdge::InfinteSharpness) + { + if (0.0 == other_sharpness0) + { + other_sharpness0 = s; + } + else + { + // There are 3 or more semisharp edges at v; + return ON_SubDEdge::SanitizeSharpness(sharpness0 - 1.0, false); + } + } + } + if (0.0 < other_sharpness0 && other_sharpness0 < ON_SubDEdge::InfinteSharpness) + { + // Chaikin's curve subdivision rule [DTK1998] Appendix B + sharpness1 = ON_SubDEdge::SanitizeSharpness(((sharpness0 == other_sharpness0) ? sharpness0 : (0.25 * (other_sharpness0 + 3.0 * sharpness0))) - 1.0, false); + //sharpness1 = ON_SubDEdge::SanitizeSharpness(sharpness0 - 1.0, false); + } + else + sharpness1 = ON_SubDEdge::SanitizeSharpness(sharpness0 - 1.0, false); + } + else + { + // non-smooth vertex + sharpness1 = ON_SubDEdge::SanitizeSharpness(sharpness0 - 1.0, false); + } + } + } + + return sharpness1; +} + +bool ON_SubDEdge::SemisharpEvaluationIsEnabled() +{ + // WARNING: + // This function is temporary and for internal use only. + // It will be removed in the near future. + // Any code referencing this function will unpredictably fail in the near future. + // This is primative and is not thread safe. + return ON_SubDEdge::m_bEnableSemisharpEvaluation; +} + +void ON_SubDEdge::SetEnableSemisharpEvaluation(bool bEnableSemisharpEvaluation) +{ + // WARNING: + // This function is temporary and for internal use only. + // It will be removed in the near future. + // Any code referencing this function will unpredictably fail in the near future. + // This is primative and is not thread safe. + ON_SubDEdge::m_bEnableSemisharpEvaluation = bEnableSemisharpEvaluation ? true : false; +} + +double ON_SubDEdge::EvaluationSharpness(ON_3dPoint& sharp_subdivision_point) const +{ + // WARNING: + // This function is temporary and for internal use only. + // It will be removed in the near future. + // Any code referencing this function will unpredictably fail in the near future. + // This is primative and is not thread safe. + double edge_sharpness; + if (ON_SubDEdge::SemisharpEvaluationIsEnabled()) + { + edge_sharpness = GetSharpSubdivisionPoint(sharp_subdivision_point); + } + else + { + sharp_subdivision_point = ON_3dPoint::NanPoint; + edge_sharpness = 0.0; + } + return edge_sharpness; +} + +// This global bool is temporary and is used to determine if subdivision and evaluation code +// pays attention to sharpness. This global bool will be removed in the near future. +// Any code referencing this variable will unpredictably fail in the near future. +bool ON_SubDEdge::m_bEnableSemisharpEvaluation = false; +#endif + bool ON_SubDVertex::IsSingleSectorVertex() const { const bool bIsCreaseOrCorner = IsCreaseOrCorner(); @@ -4376,6 +4772,10 @@ void ON_SubDEdge::CopyFrom( m_edge_tag = src->m_edge_tag; +#if defined(ON_SUBD_SHARP_EDGES) + m_sharpness = src->m_sharpness; +#endif + unsigned int end0 = bReverseEdge ? 1 : 0; if (bCopyVertexArray) @@ -7167,7 +7567,9 @@ unsigned int ON_SubD::DumpTopology( const unsigned subd_vertex_count = VertexCount(); const unsigned subd_edge_count = EdgeCount(); const unsigned subd_face_count = FaceCount(); + if (level_count > 1) + { text_log.Print(L"SubD[%" PRIu64 "]: %u levels. Level %u is active (%u vertices, %u edges, %u faces).\n", runtime_sn, level_count, @@ -7176,6 +7578,7 @@ unsigned int ON_SubD::DumpTopology( subd_edge_count, subd_face_count ); + } else { text_log.Print( @@ -7187,6 +7590,40 @@ unsigned int ON_SubD::DumpTopology( ); } +#if defined(ON_SUBD_SHARP_EDGES) + ON_Interval subd_semisharp_range(0.0, 0.0); + const unsigned int subd_semisharp_edge_count = SemisharpEdgeCount(subd_semisharp_range); + if (subd_semisharp_edge_count > 0) + { + text_log.PushIndent(); + if (subd_semisharp_range[0] == subd_semisharp_range[1]) + { + if (1 == subd_semisharp_edge_count) + text_log.Print( + L"1 semisharp edge with sharpness = %g.\n", + subd_semisharp_range[0] + ); + else + text_log.Print( + L"%u semisharp edges with sharpness = %g.\n", + subd_semisharp_edge_count, + subd_semisharp_range[0] + ); + } + else + { + text_log.Print( + L"%u semisharp edges with sharpnesses from %g to %g.\n", + subd_semisharp_edge_count, + subd_semisharp_range[0], + subd_semisharp_range[1] + ); + + } + text_log.PopIndent(); + } +#endif + const ON_SubDHashType htype[] = { ON_SubDHashType::Topology, ON_SubDHashType::TopologyAndEdgeCreases, @@ -11731,8 +12168,10 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ if ( IsSmooth() ) { - // A smooth edge must have exactly two neighboring faces and - // at most one end vertex can be tagged. + // All smooth edges (Smooth and SmoothX tags) must have exactly two neighboring faces. + // Both ends of a level 0 SmoothX edge can be non-smooth vertices but the SmoothX tag + // indicates it is to be subdivided as a smooth edge. + // All other smooth edge must have at least one edn attached to a smooth vertex. if (2 != m_face_count) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); @@ -11741,6 +12180,24 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ if (nullptr == faces[0] || nullptr == faces[1]) return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, edgeP, true); +#if defined(ON_SUBD_SHARP_EDGES) + // When the semisharp code is working, change EvaluationSharpness() to Sharpness(). + // Calling EvaluationSharpness() is a temporary hack to make + // the TestSubDSharpSubdivide command work but not break all + // the ordinary evaluation code that relies heavily on + // ordinary subdivision points. + ON_3dPoint sharp_subdivision_point; + const double edge_sharpness = this->EvaluationSharpness(sharp_subdivision_point); + if (edge_sharpness >= 1.0) + { + // Semi-sharp crease with sharpness >= 1 at current level + subdivision_point[0] = sharp_subdivision_point.x; + subdivision_point[1] = sharp_subdivision_point.y; + subdivision_point[2] = sharp_subdivision_point.z; + return true; + } +#endif + // for each neighbor face, sum the vertex locations that are not on this edge double facePsum[2][3]; const unsigned int face_edge_count[2] @@ -11793,6 +12250,17 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ 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 defined(ON_SUBD_SHARP_EDGES) + if (edge_sharpness > 0.0) + { + // Semi-sharp crease with 0 < sharpness < 1 at the current level + const double a = 1.0 - edge_sharpness; + subdivision_point[0] = a * subdivision_point[0] + edge_sharpness * sharp_subdivision_point.x; + subdivision_point[1] = a * subdivision_point[1] + edge_sharpness * sharp_subdivision_point.y; + subdivision_point[2] = a * subdivision_point[2] + edge_sharpness * sharp_subdivision_point.z; + } +#endif return true; } @@ -11807,6 +12275,16 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ 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 defined(ON_SUBD_SHARP_EDGES) + if (edge_sharpness > 0.0) + { + // Semi-sharp crease with 0 < sharpness < 1 at the current level + const double a = 1.0 - edge_sharpness; + subdivision_point[0] = a * subdivision_point[0] + edge_sharpness * sharp_subdivision_point.x; + subdivision_point[1] = a * subdivision_point[1] + edge_sharpness * sharp_subdivision_point.y; + subdivision_point[2] = a * subdivision_point[2] + edge_sharpness * sharp_subdivision_point.z; + } +#endif return true; } @@ -11824,6 +12302,16 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[ 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 defined(ON_SUBD_SHARP_EDGES) + if (edge_sharpness > 0.0) + { + // Semi-sharp crease with 0 < sharpness < 1 at the current level + const double a = 1.0 - edge_sharpness; + subdivision_point[0] = a * subdivision_point[0] + edge_sharpness * sharp_subdivision_point.x; + subdivision_point[1] = a * subdivision_point[1] + edge_sharpness * sharp_subdivision_point.x; + subdivision_point[2] = a * subdivision_point[2] + edge_sharpness * sharp_subdivision_point.x; + } +#endif return true; } @@ -12182,23 +12670,24 @@ bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( double vertex_point[3] ) { - if (nullptr != vertex_point) + if (nullptr == vertex_point) { - vertex_point[0] = ON_DBL_QNAN; - vertex_point[1] = ON_DBL_QNAN; - vertex_point[2] = ON_DBL_QNAN; + ON_SUBD_ERROR("input vertex_point is nullptr."); + return false; } + vertex_point[0] = ON_DBL_QNAN; + vertex_point[1] = ON_DBL_QNAN; + vertex_point[2] = ON_DBL_QNAN; + if (nullptr == vertex) { ON_SUBD_ERROR("input vertex is nullptr."); return false; } - const unsigned int n = vertex->m_face_count; - if (nullptr == vertex - || nullptr == vertex->m_faces + if (nullptr == vertex->m_faces || nullptr == vertex->m_edges || vertex->m_face_count != vertex->m_edge_count || n < ON_SubDSectorType::MinimumSectorFaceCount(ON_SubDVertexTag::Smooth) @@ -12210,6 +12699,23 @@ bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( const double* vertexP = vertex->m_P; +#if defined(ON_SUBD_SHARP_EDGES) + // When the semisharp code is working, change EvaluationSharpness() to Sharpness(). + // Calling EvaluationSharpness() is a temporary hack to make + // the TestSubDSharpSubdivide command work but not break all + // the ordinary evaluation code that relies heavily on + // ordinary subdivision points. + ON_3dPoint sharp_subdivision_point; + const double vertex_sharpness = vertex->EvaluationSharpness(sharp_subdivision_point); + if (vertex_sharpness >= 1.0) + { + vertex_point[0] = sharp_subdivision_point.x; + vertex_point[1] = sharp_subdivision_point.y; + vertex_point[2] = sharp_subdivision_point.z; + return true; + } +#endif + // It is critical to use the centroids of the neighboring faces // in this step because the number of edges in each face's @@ -12265,6 +12771,19 @@ bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint( vertex_point[1] = v_weight*vertexP[1] + ef_weight*(edgePsum[1] + facePsum[1]); vertex_point[2] = v_weight*vertexP[2] + ef_weight*(edgePsum[2] + facePsum[2]); + +#if defined(ON_SUBD_SHARP_EDGES) + if (vertex_sharpness > 0.0) + { + // 0 < vertex_sharpness < 1 + const double a = 1.0 - vertex_sharpness; + vertex_point[0] = a * vertex_point[0] + vertex_sharpness * sharp_subdivision_point.x; + vertex_point[1] = a * vertex_point[1] + vertex_sharpness * sharp_subdivision_point.y; + vertex_point[2] = a * vertex_point[2] + vertex_sharpness * sharp_subdivision_point.z; + return true; + } +#endif + return true; } @@ -12287,7 +12806,7 @@ bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( const unsigned int minimum_n = ON_SubDSectorType::MinimumSectorEdgeCount(vertex->m_vertex_tag); if (n < minimum_n || n != vertex->m_face_count || nullptr == vertex->m_faces) return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true); - + double facePsum[3] = { 0 }; const ON_SubDFace*const* vertex_faces = vertex->m_faces; @@ -12353,6 +12872,24 @@ bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point); } +#if defined(ON_SUBD_SHARP_EDGES) + // When the semisharp code is working, change EvaluationSharpness() to Sharpness(). + // Calling EvaluationSharpness() is a temporary hack to make + // the TestSubDSharpSubdivide command work but not break all + // the ordinary evaluation code that relies heavily on + // ordinary subdivision points. + ON_3dPoint sharp_subdivision_point = ON_3dPoint::NanPoint; + const double vertex_sharpness = vertex->EvaluationSharpness(sharp_subdivision_point); + if (vertex_sharpness >= 1.0) + { + // use corner subdivision point + vertex_point[0] = sharp_subdivision_point.x; + vertex_point[1] = sharp_subdivision_point.y; + vertex_point[2] = sharp_subdivision_point.z; + return true; + } +#endif + double edgePsum[3] = { 0 }; class ON_SubDEdgePtr* edges = vertex->m_edges; for (unsigned int i = 0; i < n; i++) @@ -12396,6 +12933,18 @@ bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint( vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2]; } +#if defined(ON_SUBD_SHARP_EDGES) + if (vertex_sharpness > 0.0) + { + // 0 < vertex_sharpness < 1 + const double a = 1.0 - vertex_sharpness; + vertex_point[0] = a * vertex_point[0] + vertex_sharpness * sharp_subdivision_point.x; + vertex_point[1] = a * vertex_point[1] + vertex_sharpness * sharp_subdivision_point.y; + vertex_point[2] = a * vertex_point[2] + vertex_sharpness * sharp_subdivision_point.z; + return true; + } +#endif + return true; } @@ -12452,6 +13001,10 @@ bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_poin { class ON_SubDEdgePtr* edges = m_edges; const ON_SubDVertex* edge0_vertex = nullptr; + const ON_SubDVertex* edge1_vertex = nullptr; +#if defined(ON_SUBD_SHARP_EDGES) + double max_sharpness = 0.0; +#endif for (unsigned int i = 0; i < n; i++) { @@ -12463,7 +13016,15 @@ bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_poin } if (ON_SubDEdgeTag::Crease != edge->m_edge_tag) + { +#if defined(ON_SUBD_SHARP_EDGES) + const double s = edge->Sharpness(0.0, 0.0, 0.0); + if (s > max_sharpness) + max_sharpness = s; +#endif continue; + } + const ON_SubDVertex* edge_vertex = edge->OtherEndVertex(this); if (nullptr == edge_vertex) @@ -12484,14 +13045,73 @@ bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_poin continue; } + if (nullptr == edge1_vertex) + { + edge1_vertex = edge_vertex; + continue; + } + + if (edge1_vertex == edge_vertex) + { + ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); + continue; + } + + // 3 or more creased edges attached to this crease vertex. + ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true); + subdivision_point[0] = vertexP[0]; + subdivision_point[1] = vertexP[1]; + subdivision_point[2] = vertexP[2]; + return true; + } + + if (nullptr != edge0_vertex && nullptr != edge1_vertex) + { // We found the two crease edges that share this crease vertex. // (The parenthesis around the edgeP sum is to insure this result // is independent of the order of the edges.) vertexP = m_P; - const double* edgeP[2] = { edge0_vertex->m_P, edge_vertex->m_P }; - subdivision_point[0] = (vertexP[0] * 6.0 + (edgeP[0][0] + edgeP[1][0]))*0.125; - subdivision_point[1] = (vertexP[1] * 6.0 + (edgeP[0][1] + edgeP[1][1]))*0.125; - subdivision_point[2] = (vertexP[2] * 6.0 + (edgeP[0][2] + edgeP[1][2]))*0.125; + +#if defined(ON_SUBD_SHARP_EDGES) + if (ON_SubDEdge::SemisharpEvaluationIsEnabled() && max_sharpness >= 1.0) + { + // 2 creases and a semisharp edge with sharpness >= 1 + // This modification is not part of the DKT1998 Pixar algorithm. + // It is required to get semisharp creases to evenly end at + // ordinary boundaries. It does break crease edge evaluation relying + // only on boundary settings, but at this time (Dale Lear Nov 2022), + // I'm guessing it is the best option out of 2 nonideal choices. + subdivision_point[0] = vertexP[0]; + subdivision_point[1] = vertexP[1]; + subdivision_point[2] = vertexP[2]; + return true; + } +#endif + + const double* edgeP[2] = { edge0_vertex->m_P, edge1_vertex->m_P }; + subdivision_point[0] = (vertexP[0] * 6.0 + (edgeP[0][0] + edgeP[1][0])) * 0.125; + subdivision_point[1] = (vertexP[1] * 6.0 + (edgeP[0][1] + edgeP[1][1])) * 0.125; + subdivision_point[2] = (vertexP[2] * 6.0 + (edgeP[0][2] + edgeP[1][2])) * 0.125; + +#if defined(ON_SUBD_SHARP_EDGES) + if (ON_SubDEdge::SemisharpEvaluationIsEnabled() && max_sharpness > 0.0) + { + // 2 creases and a semisharp edge with 0 < sharpness < 1 + // + // This modification is not part of the DKT1998 Pixar algorithm. + // It is required to get semisharp creases to evenly end at + // ordinary boundaries. It does break crease edge evaluation relying + // only on boundary settings, but at this time (Dale Lear Nov 2022), + // I'm guessing it is the best option out of 2 nonideal choices. + // + const double a = 1.0 - max_sharpness; + subdivision_point[0] = a * subdivision_point[0] + max_sharpness * vertexP[0]; + subdivision_point[1] = a * subdivision_point[1] + max_sharpness * vertexP[1]; + subdivision_point[2] = a * subdivision_point[2] + max_sharpness * vertexP[2]; + return true; + } +#endif + return true; } @@ -12746,13 +13366,32 @@ bool ON_SubDimple::LocalSubdivide( // split edges for (unsigned edex = 0; edex < edge_count; ++edex) { - ON_SubDEdge* e = edges[edex]; + ON_SubDEdge* e = edges[edex]; + +#if defined(ON_SUBD_SHARP_EDGES) + const double sharpness = e->Sharpness(); +#endif + e->EdgeModifiedNofification(); const ON_SubDEdge* new_edge = SplitEdge(e, edge_points[edex]); if (nullptr == new_edge) return ON_SUBD_RETURN_ERROR(false); new_edge->m_status.ClearRuntimeMark(); e->m_status.SetRuntimeMark(); + +#if defined(ON_SUBD_SHARP_EDGES) + // LOCAL subdivide is more of an add control points to an existing level + // as opposed to a global subdivision level operation. It will be common + // for an input semisharp edge chain crease to have some of its edges + // subdivided and others remain unchanged. + // Dale Lear's best guess on Nov 9, 2022 is that + // resusing the orginal sharpness is the best way to maximize overall + // user happiness. This makes some sense because the level of the edges + // is not changing in a LOCAL subdivide. + // It is a near certainty that some users will not like this in some cases. + const_cast(e)->SetSharpness(sharpness); + const_cast(new_edge)->SetSharpness(sharpness); +#endif } ON_SimpleArray fbdry(32); @@ -12961,8 +13600,31 @@ unsigned int ON_SubDimple::GlobalSubdivide() if ( nullptr != mid_vertex && ON_SubDVertexTag::Smooth == mid_vertex->m_vertex_tag ) edge_tag = ON_SubDEdgeTag::Smooth; } - AddEdge(edge_tag, end_vertex[0], w[0], mid_vertex, 0.0); - AddEdge(edge_tag, mid_vertex, 0.0, end_vertex[1], w[1]); + +#if defined(ON_SUBD_SHARP_EDGES) + class ON_SubDEdge* e10 = +#endif + AddEdge(edge_tag, end_vertex[0], w[0], mid_vertex, 0.0); + +#if defined(ON_SUBD_SHARP_EDGES) + class ON_SubDEdge* e11 = +#endif + AddEdge(edge_tag, mid_vertex, 0.0, end_vertex[1], w[1]); + +#if defined(ON_SUBD_SHARP_EDGES) + const double e0_sharpness + = (ON_SubDEdgeTag::Smooth == edge_tag) + ? e0->Sharpness() + : 0.0; + if (e0_sharpness > 0.0 && e0_sharpness < ON_SubDEdge::InfinteSharpness) + { + if (nullptr != e10) + e10->SetSharpness(e0->SubdivideSharpness(0)); + if (nullptr != e11) + e11->SetSharpness(e0->SubdivideSharpness(1)); + } +#endif + } for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face) @@ -13173,6 +13835,28 @@ unsigned int ON_SubDimple::Internal_GlobalQuadSubdivideFace( return f1_count; } +unsigned int ON_SubD::GlobalSubdivideQuadCount() const +{ + return GlobalSubdivideQuadCount(1); +} + +unsigned int ON_SubD::GlobalSubdivideQuadCount(unsigned int subdivision_count) const +{ + if (subdivision_count >= 16) + return ON_UINT_MAX; + unsigned int subdivide_quad_count = 0; + if (subdivision_count >= 1) + { + unsigned int level1_quad_count = 0; + ON_SubDFaceIterator fit(*this); + for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace()) + level1_quad_count += f->EdgeCount(); + const unsigned pow4 = 1U << (2U*subdivision_count); // pow4 = 4^subdivision_count + subdivide_quad_count = (subdivide_quad_count > ON_UINT_MAX / pow4) ? ON_UINT_MAX : (level1_quad_count * pow4); + } + return subdivide_quad_count; +} + bool ON_SubD::GlobalSubdivide() { return GlobalSubdivide(1U); @@ -16134,10 +16818,6 @@ unsigned int ON_SubDLevel::UpdateEdgeTags( edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient; break; - //case ON_SubDEdgeTag::Sharp: - // ON_SUBD_ERROR("ON_SubDEdgeTag::Sharp is not valid in this version of opennurbs."); - // break; - case ON_SubDEdgeTag::SmoothX: if ( 2 != tagged_end_index && bBothVertexTagsAreSet) edge->m_edge_tag = ON_SubDEdgeTag::Smooth; @@ -19478,7 +20158,14 @@ unsigned int ON_SubD::SetEdgeTags( ON_SubDEdge* edge = cptr_list[i].Edge(); if (nullptr == edge) continue; - if (bChangeToSmooth == edge->IsSmooth()) + const bool bIsSemisharp = +#if defined(ON_SUBD_SHARP_EDGES) + edge->IsSemisharp() +#else + false +#endif + ; + if (bChangeToSmooth == edge->IsSmooth() && false == bIsSemisharp) continue; if (bChangeToSmooth && 2 != edge->FaceCount()) continue; @@ -19487,6 +20174,8 @@ unsigned int ON_SubD::SetEdgeTags( changed_edge_count++; edge->m_edge_tag = edge_tag; + if (bChangeToSmooth && bIsSemisharp) + edge->SetSharpness(0.0); edge->UnsetSectorCoefficientsForExperts(); for (int evi = 0; evi < 2; evi++) { @@ -19573,8 +20262,18 @@ unsigned int ON_SubD::RemoveAllCreases() ON_SubDEdgeIterator eit(*this); for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge()) { - if ( false == e->IsCrease() || 2 != e->m_face_count) + const bool bIsSemisharp = +#if defined(ON_SUBD_SHARP_EDGES) + e->IsSemisharp() +#else + false +#endif + ; + if ( (false == e->IsCrease() && false == bIsSemisharp) || 2 != e->m_face_count) continue; +#if defined(ON_SUBD_SHARP_EDGES) + const_cast(e)->SetSharpness(0.0); +#endif const_cast(e)->m_edge_tag = ON_SubDEdgeTag::Smooth; e->UnsetSectorCoefficientsForExperts(); for (int evi = 0; evi < 2; evi++) @@ -23721,377 +24420,3 @@ double ON_SubDExpandEdgesParameters::CleanupOffset(double x) -#if defined(ON_SUBD_CENSUS) -////////////////////////////////////////////////////////////////////////// -// -// ON_CensusCounter -// - - -class ON_PointerHashElement -{ -public: - ON__UINT_PTR m_sn = 0; - ON__UINT_PTR m_ptr = 0; - ON_PointerHashElement* m_next = nullptr; -}; - -class ON_PointerHashTable -{ -public: - - void Add(ON_CensusCounter::Class c, ON__UINT_PTR ptr); - void Remove(ON_CensusCounter::Class c, ON__UINT_PTR ptr); - - ON__UINT_PTR SerialNumber(ON_CensusCounter::Class c, ON__UINT_PTR ptr) const; - - enum - { - hash_count = 1024 - }; - ON_PointerHashElement* m_table[(unsigned int)ON_CensusCounter::Class::count][hash_count]; - - - unsigned int m_count = 0; - - - static bool TheOneExists(); - static ON_PointerHashTable* TheOne(); - static void DestroyTheOne(); - -private: - ON_PointerHashTable(); - ~ON_PointerHashTable(); - ON_PointerHashTable(const ON_PointerHashTable&) = delete; - ON_PointerHashTable& operator=(const ON_PointerHashTable&) = delete; - - static unsigned int PointerHash(ON__UINT_PTR ptr); - ON_FixedSizePool m_fsp; - static ON_PointerHashTable* m_the_one; -}; - -ON_PointerHashTable* ON_PointerHashTable::m_the_one = nullptr; - -ON_PointerHashTable::ON_PointerHashTable() -{ - memset(m_table,0,sizeof(m_table)); - m_fsp.Create(sizeof(ON_PointerHashElement),0,0); -} - -ON_PointerHashTable::~ON_PointerHashTable() -{ - memset(m_table,0,sizeof(m_table)); - m_count = 0; - m_fsp.Destroy(); -} - -bool ON_PointerHashTable::TheOneExists() -{ - return (nullptr != ON_PointerHashTable::m_the_one); -} - -ON_PointerHashTable* ON_PointerHashTable::TheOne() -{ - if (nullptr == ON_PointerHashTable::m_the_one) - { - ON_PointerHashTable::m_the_one = new ON_PointerHashTable(); - } - return ON_PointerHashTable::m_the_one; -} - -void ON_PointerHashTable::DestroyTheOne() -{ - if (nullptr != ON_PointerHashTable::m_the_one) - { - delete ON_PointerHashTable::m_the_one; - ON_PointerHashTable::m_the_one = nullptr; - } -} - -unsigned int ON_PointerHashTable::PointerHash(ON__UINT_PTR ptr) -{ - return (ON_CRC32(0, sizeof(ptr),&ptr) % ON_PointerHashTable::hash_count); -} - -void ON_PointerHashTable::Add(ON_CensusCounter::Class c, ON__UINT_PTR ptr) -{ - static ON__UINT_PTR sn = 0; - // not thread safe - a crude debugging tool - ON_PointerHashElement* phe = (ON_PointerHashElement*)m_fsp.AllocateDirtyElement(); - phe->m_sn = ++sn; - phe->m_ptr = ptr; - const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr); - phe->m_next = m_table[(unsigned int)c][hash_dex]; - m_table[(unsigned int)c][hash_dex] = phe; - m_count++; -} - -void ON_PointerHashTable::Remove(ON_CensusCounter::Class c, ON__UINT_PTR ptr) -{ - const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr); - ON_PointerHashElement* phe = m_table[(unsigned int)c][hash_dex]; - for (ON_PointerHashElement* phe0 = nullptr; nullptr != phe; phe = phe->m_next) - { - if (ptr == phe->m_ptr) - { - if ( nullptr == phe0 ) - m_table[(unsigned int)c][hash_dex] = phe->m_next; - else - phe0 = phe->m_next; - m_fsp.ReturnElement(phe); - m_count--; - return; - } - phe0 = phe; - } - // pointer not in the table - ON_SubDIncrementErrorCount(); - return; -} - - -ON__UINT_PTR ON_PointerHashTable::SerialNumber(ON_CensusCounter::Class c, ON__UINT_PTR ptr) const -{ - const unsigned int hash_dex = ON_PointerHashTable::PointerHash(ptr); - for ( ON_PointerHashElement* phe = m_table[(unsigned int)c][hash_dex]; nullptr != phe; phe = phe->m_next) - { - if (ptr == phe->m_ptr) - return phe->m_sn; - } - // pointer not in the table - return 0; -} - -void ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class c, ON__UINT_PTR ptr) -{ - ON_PointerHashTable::TheOne()->Add(c,ptr); -} - -void ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class c, ON__UINT_PTR ptr) -{ - ON_PointerHashTable::TheOne()->Remove(c,ptr); -} - - -void ON_CensusCounter::Clear() -{ - ON_PointerHashTable::DestroyTheOne(); -} - -void ON_CensusCounter::CensusReport( - class ON_TextLog& text_log - ) -{ - ON_PointerHashTable* ht = ON_PointerHashTable::TheOneExists() ? ON_PointerHashTable::TheOne() : nullptr; - if ( nullptr == ht ) - { - text_log.Print("No class census information exists.\n"); - return; - } - - text_log.Print("%d items exist.\n",ht->m_count); - for (unsigned int i = 0; i < (unsigned int)ON_CensusCounter::Class::count; i++) - { - bool bPrintClassName = true; - const char* sClassName = nullptr; - const ON_CensusCounter::Class c = (ON_CensusCounter::Class)i; - switch (c) - { - case ON_CensusCounter::Class::subd: - sClassName = "ON_SubD"; - break; - case ON_CensusCounter::Class::subd_impl: - sClassName = "ON_SubDimple"; - break; - case ON_CensusCounter::Class::subd_limit_mesh: - sClassName = "ON_SubDMesh"; - break; - case ON_CensusCounter::Class::subd_limit_mesh_impl: - sClassName = "ON_SubDMeshImpl"; - break; - case ON_CensusCounter::Class::subd_ref: - sClassName = "ON_SubDef"; - break; - default: - sClassName = "Bug in ON_CensusCounter"; - break; - } - - for (unsigned int j = 0; j < ON_PointerHashTable::hash_count; j++) - { - for (ON_PointerHashElement* e = ht->m_table[i][j]; nullptr != e; e = e->m_next) - { - const unsigned int sn = (unsigned int)e->m_sn; - - if (bPrintClassName) - { - text_log.Print("\n\n%s census:\n",sClassName); - bPrintClassName = false; - } - - switch (c) - { - case ON_CensusCounter::Class::subd: - { - const ON_SubD* subd = (const ON_SubD*)e->m_ptr; - if ( subd == &ON_SubD::Empty ) - text_log.Print("ON_SubD::Empty (%u) ", sn); - else - text_log.Print("ON_SubD(%u) ", sn); - const ON_SubDimple* dimple = subd->SubDimple(); - if ( nullptr == dimple ) - text_log.Print(" ON_SubDimple(nullptr)\n"); - else - { - unsigned int dimple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_impl, (ON__UINT_PTR)dimple); - text_log.Print(" ON_SubDimple(%u)x%u\n", dimple_sn, subd->SubDimpleUseCount()); - } - } - break; - - case ON_CensusCounter::Class::subd_impl: - { - text_log.Print("ON_SubDimple(%u)\n", sn); - } - break; - - case ON_CensusCounter::Class::subd_limit_mesh: - { - const ON_SubDMesh* subd_limit_mesh = (const ON_SubDMesh*)e->m_ptr; - if ( subd_limit_mesh == &ON_SubDMesh::Empty ) - text_log.Print("ON_SubDMesh::Empty (%u) ", sn); - else - text_log.Print("ON_SubDMesh(%u) ", sn); - const class ON_SubDMeshImpl* limple = subd_limit_mesh->SubLimple(); - if ( nullptr == limple ) - text_log.Print(" ON_SubDMeshImpl(nullptr)\n"); - else - { - unsigned int limple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_limit_mesh_impl, (ON__UINT_PTR)limple); - text_log.Print(" ON_SubDMeshImpl(%u)x%u\n", limple_sn, subd_limit_mesh->SubLimpleUseCount()); - } - } - break; - - case ON_CensusCounter::Class::subd_limit_mesh_impl: - { - const ON_SubDMeshImpl* subd_limple = (const ON_SubDMeshImpl*)e->m_ptr; - text_log.Print("ON_SubDMeshImpl(%u)", sn); - - std::shared_ptr limple_sp(subd_limple->m_subdimple_wp.lock()); - const ON_SubDimple* dimple = limple_sp.get(); - if ( nullptr == dimple ) - text_log.Print(" ON_SubDimple(nullpr)\n"); - else - { - unsigned int dimple_sn = (unsigned int)ht->SerialNumber(ON_CensusCounter::Class::subd_impl, (ON__UINT_PTR)dimple); - text_log.Print(" ON_SubDimple(%u)x%u+1\n", dimple_sn, (unsigned int)(limple_sp.use_count()-1)); - } - - } - break; - - case ON_CensusCounter::Class::subd_ref: - { - const ON_SubDRef* subd_ref = (const ON_SubDRef*)e->m_ptr; - const ON_SubD* subd = &subd_ref->SubD(); - ON__UINT_PTR subd_sn = ht->SerialNumber(ON_CensusCounter::Class::subd, (ON__UINT_PTR)subd); - text_log.Print("ON_SubDRef(%u) ON_SubD(%u)x%u\n", sn, subd_sn, subd_ref->ReferenceCount()); - } - break; - } - } - } - } -} - - -static ON__UINT_PTR ON_SubDCensusCounter_This(ON_SubDCensusCounter* p) -{ - const ON_SubD* pSubD = (const ON_SubD*)((unsigned char*)p - sizeof(ON_Geometry)); - return (ON__UINT_PTR)pSubD; -} - -ON_SubDCensusCounter::ON_SubDCensusCounter() ON_NOEXCEPT -{ - ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this)); -} - -ON_SubDCensusCounter::~ON_SubDCensusCounter() ON_NOEXCEPT -{ - ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this)); -} - -ON_SubDCensusCounter::ON_SubDCensusCounter(const ON_SubDCensusCounter&) ON_NOEXCEPT -{ - ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd,ON_SubDCensusCounter_This(this)); -} - -ON_SubDRefCensusCounter::ON_SubDRefCensusCounter() ON_NOEXCEPT -{ - ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this); -} - -ON_SubDRefCensusCounter::~ON_SubDRefCensusCounter() ON_NOEXCEPT -{ - ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this); -} - -ON_SubDRefCensusCounter::ON_SubDRefCensusCounter(const ON_SubDRefCensusCounter&) ON_NOEXCEPT -{ - ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_ref,(ON__UINT_PTR)this); -} - - -ON_SubDImpleCensusCounter::ON_SubDImpleCensusCounter() ON_NOEXCEPT -{ - ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this); -} - -ON_SubDImpleCensusCounter::~ON_SubDImpleCensusCounter() ON_NOEXCEPT -{ - ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this); -} - -ON_SubDImpleCensusCounter::ON_SubDImpleCensusCounter(const ON_SubDImpleCensusCounter&) ON_NOEXCEPT -{ - ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_impl,(ON__UINT_PTR)this); -} - - - -ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter() ON_NOEXCEPT -{ - ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); -} - -ON_SubDMeshCensusCounter::~ON_SubDMeshCensusCounter() ON_NOEXCEPT -{ - ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); -} - -ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT -{ - ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh,(ON__UINT_PTR)this); -} - - - - -ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter() ON_NOEXCEPT -{ - ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); -} - -ON_SubDMeshCensusCounter::~ON_SubDMeshCensusCounter() ON_NOEXCEPT -{ - ON_CensusCounter::RegisterDeath(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); -} - -ON_SubDMeshCensusCounter::ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT -{ - ON_CensusCounter::RegisterBirth(ON_CensusCounter::Class::subd_limit_mesh_impl,(ON__UINT_PTR)this); -} - -#endif - diff --git a/opennurbs_subd.h b/opennurbs_subd.h index d22357e7..00fe761e 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -11,103 +11,6 @@ // //////////////////////////////////////////////////////////////// - -#if 0 -#define ON_SUBD_CENSUS -////////////////////////////////////////////////////////////////////////// -// -// ON_CensusCounter -// -// This tool is used to study memory leaks and other shared ptr issues. -// The classes have no size but effect performance. Use only in -// debugging situations. Never ship a release with ON_SUBD_CENSUS defined. -// -class ON_CLASS ON_CensusCounter -{ -public: - enum class Class : unsigned int - { - unset = 0, - subd = 1, - subd_impl = 2, - subd_limit_mesh = 3, - subd_limit_mesh_impl = 4, - subd_ref = 5, - - count - }; - - static void RegisterBirth(ON_CensusCounter::Class,ON__UINT_PTR); - - static void RegisterDeath(ON_CensusCounter::Class,ON__UINT_PTR); - - static void CensusReport( - class ON_TextLog& - ); - - static void Clear(); -}; - -class ON_CLASS ON_SubDCensusCounter -{ -public: - ON_SubDCensusCounter() ON_NOEXCEPT; - ~ON_SubDCensusCounter() ON_NOEXCEPT; - ON_SubDCensusCounter(const ON_SubDCensusCounter&) ON_NOEXCEPT; - ON_SubDCensusCounter& operator=(const ON_SubDCensusCounter&) = default; - //ON_SubDCensusCounter( ON_SubDCensusCounter&& ) ON_NOEXCEPT; - //ON_SubDCensusCounter& operator=( ON_SubDCensusCounter&& ) ON_NOEXCEPT; -}; - -class ON_CLASS ON_SubDRefCensusCounter -{ -public: - ON_SubDRefCensusCounter() ON_NOEXCEPT; - ~ON_SubDRefCensusCounter() ON_NOEXCEPT; - ON_SubDRefCensusCounter(const ON_SubDRefCensusCounter&) ON_NOEXCEPT; - ON_SubDRefCensusCounter& operator=(const ON_SubDRefCensusCounter&) = default; - //ON_SubDRefCensusCounter( ON_SubDRefCensusCounter&& ) ON_NOEXCEPT; - //ON_SubDRefCensusCounter& operator=( ON_SubDRefCensusCounter&& ) ON_NOEXCEPT; -}; - - -class ON_CLASS ON_SubDImpleCensusCounter -{ -public: - ON_SubDImpleCensusCounter() ON_NOEXCEPT; - ~ON_SubDImpleCensusCounter() ON_NOEXCEPT; - ON_SubDImpleCensusCounter(const ON_SubDImpleCensusCounter&) ON_NOEXCEPT; - ON_SubDImpleCensusCounter& operator=(const ON_SubDImpleCensusCounter&) = default; - //ON_SubDImplCensusCounter( ON_SubDImplCensusCounter&& ) ON_NOEXCEPT; - //ON_SubDImplCensusCounter& operator=( ON_SubDImplCensusCounter&& ) ON_NOEXCEPT; -}; - -class ON_CLASS ON_SubDMeshCensusCounter -{ -public: - ON_SubDMeshCensusCounter() ON_NOEXCEPT; - ~ON_SubDMeshCensusCounter() ON_NOEXCEPT; - ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT; - ON_SubDMeshCensusCounter& operator=(const ON_SubDMeshCensusCounter&) = default; - //ON_SubDMeshCensusCounter( ON_SubDMeshCensusCounter&& ) ON_NOEXCEPT; - //ON_SubDMeshCensusCounter& operator=( ON_SubDMeshCensusCounter&& ) ON_NOEXCEPT; -}; - - -class ON_CLASS ON_SubDMeshCensusCounter -{ -public: - ON_SubDMeshCensusCounter() ON_NOEXCEPT; - ~ON_SubDMeshCensusCounter() ON_NOEXCEPT; - ON_SubDMeshCensusCounter(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT; - ON_SubDMeshCensusCounter& operator=(const ON_SubDMeshCensusCounter&) ON_NOEXCEPT; - ON_SubDMeshCensusCounter( ON_SubDMeshCensusCounter&& ) ON_NOEXCEPT; - ON_SubDMeshCensusCounter& operator=( ON_SubDMeshCensusCounter&& ) ON_NOEXCEPT; -}; - -#endif - - //////////////////////////////////////////////////////////////// // // Definition of subdivision surface @@ -117,6 +20,8 @@ public: #if !defined(OPENNURBS_SUBD_INC_) #define OPENNURBS_SUBD_INC_ + + /// /// ON_SubDGetControlNetMeshPriority specifies what type of ON_SubD information /// is most important to transfer to the ON_Mesh. @@ -3521,11 +3426,6 @@ class ON_CLASS ON_SubD : public ON_Geometry { ON_OBJECT_DECLARE(ON_SubD); -#if defined(ON_SUBD_CENSUS) -private: - ON_SubDCensusCounter m_census_counter; -#endif - public: static const ON_SubD Empty; @@ -3721,24 +3621,6 @@ public: ON_SubDVertexTag vertex_tag ); - - #if 0 - // .NET code that generates enums in hash pragma region R-H_C_S-H-ARED_ENUM cannot handle if 0. - /// - /// Reserved for version 2 of the ON_SubD project. - /// Currently this tag is not used and is invalid. - /// - /// FUTURE: The edge is a "soft crease" or "semi-sharp". - /// At lease one end vertex must be tagged as ON_SubDVertexTag::Smooth - /// The edge must have exactly two faces. - /// The value of ON_SubDEdge::m_sharpness controls how - /// soft/hard the edge appears. - /// ON_SubDEdge::m_sharpness = 0 is identical to ON_SubDEdgeTag::Smooth. - /// ON_SubDEdge::m_sharpness = 1 is identical to ON_SubDEdgeTag::Crease. - /// - Sharp = 3, -#endif - static ON_SubDEdgeTag EdgeTagFromUnsigned( unsigned int edge_tag_as_unsigned ); @@ -3762,7 +3644,7 @@ public: Parameters: edge_tag - [in] Returns: - True if edge_tag is Smooth, Crease, Sharp, or X. + True if edge_tag is Smooth, Crease, or SmoothX. False otherwise. */ static bool EdgeTagIsSet( @@ -4734,6 +4616,34 @@ public: bool IsEmpty() const; bool IsNotEmpty() const; +#if defined(ON_SUBD_SHARP_EDGES) + /// + /// Determine if any edges in this SubD have sem-sharp edges. + /// + /// True if the SubD has at lease one semisharp edge. + bool HasSemisharpness() const; + + /// + /// Get the range of sharpness values assigned to semisharp edges + /// and return the number of semisharp edges. + /// + /// The range of sharpness values is returned here. (0,0) is returned if there are no semisharp edges. + /// Number of semisharp edges. + unsigned int SemisharpEdgeCount(ON_Interval& sharpness_range) const; + + /// + /// Number of semisharp edges. + /// + /// The range of sharpness values is returned here. (0,0) is returned if there are no semisharp edges. + /// Number of semisharp edges. + unsigned int SemisharpEdgeCount() const; + + /// + /// Converts all semisharp edges to smooth edges. + /// + /// Number of semisharp edges converted to smooth edges. + unsigned int ClearSemisharpness(); +#endif /* Description: @@ -6019,6 +5929,16 @@ public: bool GlobalSubdivide(); + /// + /// The number of quads a call to GlobalSubdivide() will create. + ///< / returns> + unsigned int GlobalSubdivideQuadCount() const; + + /// + /// The number of quads a call to GlobalSubdivide(subdivision_count) will create. + ///< / returns> + unsigned int GlobalSubdivideQuadCount(unsigned int subdivision_count) const; + /* Description: Apply the Catmull-Clark subdivision algorithm to the faces in face_list[]. @@ -7439,11 +7359,6 @@ public: // class ON_CLASS ON_SubDRef { -#if defined(ON_SUBD_CENSUS) -private: - ON_SubDRefCensusCounter m_census_counter; -#endif - public: static const ON_SubDRef Empty; @@ -10476,11 +10391,6 @@ private: /// class ON_CLASS ON_SubDMesh { -#if defined(ON_SUBD_CENSUS) -private: - ON_SubDMeshCensusCounter m_census_counter; -#endif - public: static const ON_SubDMesh Empty; @@ -11448,7 +11358,6 @@ public: private: - //ON_SubD::VertexEdgeOrder m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset; unsigned char m_reserved1 = 0; unsigned short m_reserved2 = 0; unsigned int m_reserved3 = 0; @@ -11622,6 +11531,70 @@ public: */ bool IsSmooth() const; +#if defined(ON_SUBD_SHARP_EDGES) + + /// + /// Semi-sharp vertices are smooth vertices attached to one or more semisharp edges. + /// + /// + /// True if this vertex is smooth and is attached to one or more semisharp edges. + ///< / returns> + bool IsSemisharp() const; + + /// + /// Semi-sharp vertices are smooth vertices attached to one or more semisharp edges. + /// + /// + /// If the vertex is attached to zero or one semisharp edges, 0 is returned. + /// If the vertex is attached to three or more semisharp edges, + /// ON_SubDEdge::InfinteSharpness is returned. + /// If the vertex is attached to two semisharp edges, + /// the average value of the edges' sharpness is returned. + ///< / returns> + double Sharpness() const; + + /// + /// Semi-sharp vertices are smooth vertices attached to one or more semisharp edges. + /// + /// If the returned sharpness is > 0, + /// then the sharp subdivision point is returned. + /// When the returned sharpness is > 0 and < 1, + /// the final subdivision point is a weighted average of + /// sharp_subdivision_point and the ordinary smooth subdivision point. + /// + /// + /// If the vertex is attached to zero or one semisharp edges, 0 is returned. + /// If the vertex is attached to three or more semisharp edges, + /// ON_SubDEdge::InfinteSharpness is returned. + /// If the vertex is attached to two semisharp edges, + /// the average value of the edges' sharpness is returned. + ///< / returns> + double GetSharpSubdivisionPoint(ON_3dPoint& sharp_subdivision_point) const; + + /// + /// WARNING: + /// This function is temporary and for internal use only. + /// It will be removed in the near future. + /// Any code referencing this function will unpredictably fail in the near future. + /// This is primative and is not thread safe. + /// + /// (ON_SubDEdge::SemisharpEvaluationIsEnabled() ? Sharpness() : 0.0) + double EvaluationSharpness( + ON_3dPoint& sharp_subdivision_point + ) const; + + unsigned int SemisharpEdgeCount() const; + + /// + /// Get the range of sharpness values assigned to semisharp edges + /// and return the number of semisharp edges. + /// + /// The range of sharpness values is returned here. (0,0) is returned if there are no semisharp edges. + /// Number of semisharp edges. + unsigned int SemisharpEdgeCount(ON_Interval& sharpness_range) const; + +#endif + /* Returns: True if m_vertex_tag is ON_SubDVertexTag::Crease. @@ -12352,14 +12325,57 @@ public: // you need to recompute all sector coefficients using subd->UpdateAllTagsAndSectorCoefficients(true); mutable double m_sector_coefficient[2] = {}; - // If m_edge_tag is not ON_SubDEdgeTag::Sharp, then m_sharpness is ignored. - // If m_edge_tag is ON_SubDEdgeTag::Sharp, then m_sharpness controls how hard/soft - // the edge appears. - // The behavior of a "sharp" edge with m_sharpness = 1 is identical to a crease edge. - // A "sharp" edge with m_sharpness = 0 is identical to a smooth edge. - // For this reason, m_sharpness must be > 0 and < 1. +private: + // Semi-sharp crease sharpness. + // See ON_SubDEdge::Sharpness() comments for details. double m_sharpness = 0.0; +#if defined(ON_SUBD_SHARP_EDGES) +public: + /// + /// WARNING: + /// This function is temporary and for internal use only. + /// It will be removed in the near future. + /// Any code referencing this function will unpredictably fail in the near future. + /// This is primative and is not thread safe. + /// + /// (ON_SubDEdge::SemisharpEvaluationIsEnabled() ? Sharpness() : 0.0) + double EvaluationSharpness( + ON_3dPoint& sharp_subdivision_point + ) const; + + double GetSharpSubdivisionPoint( + ON_3dPoint& sharp_subdivision_point + ) const; + + + /// + /// WARNING: + /// This function is temporary and for internal use only. + /// It will be removed in the near future. + /// Any code referencing this function will unpredictably fail in the near future. + /// This is primative and is not thread safe. + /// + /// ON_SubDEdge::m_bEnableSemiharpEvaluation + static bool SemisharpEvaluationIsEnabled(); + + /// + /// WARNING: + /// This function is temporary and for internal use only. + /// It will be removed in the near future. + /// Any code referencing this function will unpredictably fail in the near future. + /// This is primative and is not thread safe. + /// + /// + static void SetEnableSemisharpEvaluation(bool bEnableSemiharpEvaluation); +private: + // This global bool is temporary and is used to determine if subdivision and evaluation code + // pays attention to sharpness. This global bool will be removed in the near future. + // Any code referencing this variable will unpredictably fail in the near future. + // This is primative and is not thread safe. + static bool m_bEnableSemisharpEvaluation; +#endif + private: // Cached limit curve // GetEdgeSurfaceCurveControlPoints( bUseSavedSurfacePoint=true ) can change the value of m_limit_curve. @@ -12672,6 +12688,100 @@ public: */ bool IsSmoothX() const; +#if defined(ON_SUBD_SHARP_EDGES) + + /// + /// Semi-sharp edges are tagged as smooth edges with a sharpness > 0 and are + /// a blend between a smooth and a creased edge. + /// + /// True if the edge is tagged as smooth and has Sharpness > 0. + bool IsSemisharp() const; + + /// + /// Determine the sharpness to assign to the subdivided edge. + /// + /// 0 or 1 indicating the left or right subdivided edge. + /// Sharpness to assigned to the subdivided edge. + double SubdivideSharpness( + unsigned end_index + ) const; + + static double AverageSharpness( + double sharpness0, + double sharpness1 + ); +#endif + + /// + /// Semisharp edges are tagged as smooth edges with a + /// sharpness > 0 and < ON_SubDEdge::InfinteSharpness. + /// + /// + /// If the edge is smooth and not semisharp, 0 is returned. + /// If the edge is a crease, ON_SubDEdge::InfinteSharpness is returned. + /// If the edge is semisharp, a sharpness value strictly between 0 and ON_SubDEdge::InfinteSharpness is returned. + /// Otherwise, ON_DBL_QNAN is returned. + /// + /// 0.0 if the edges is smooth and ON_SubDEdge::InfinteSharpness if the edge is a crease. + /// A value strictly between 0 and ON_SubDEdge::InfinteSharpness if the edge is a semisharp. + double Sharpness() const; + + /// + /// A version of sharpness that allows the return value to be specified for edges that + /// are not explicitly semisharp. This is useful to simplify code branch decisions + /// when smooth, semisharp, and crease edges need to be handled differently. + /// + /// + /// Value to return when the edge tag is unset. ON_UNSET_VALUE and ON_DBL_QNAN are good choices. + /// + /// + /// Value to return when the edge is smooth but not semisharp. 0.0 is often a good choice. + /// + /// + /// Value to return when the edge is smooth but not semisharp. + /// ON_SubDEdge::InfinteSharpness is often a good choice. + /// Value of sharpness for the specified situation. + double Sharpness( + double unset_edge_value, + double smooth_edge_value, + double crease_edge_value + ) const; + + /// + /// Semi-sharp edges are tagged as smooth edges with a sharpness > 0 and are + /// a blend between a smooth and a creased edge. Set N = floor(sharpness). + /// For subdivision levels < N, the semisharp edge and attached vertices + /// are subdivided using crease subdivision rules. + /// For subdivision levels >= ceil(sharpness), the semisharp edge and attached vertices + /// are subdivided using smooth subdivision rules. + /// When N < sharpness < ceil(sharpness), at level N the semisharp edge and attached vertices + /// are subdivided using a linear blend of crease and smooth subdivision rules. + /// (1-s)*(smooth rules) * s(crease rules), where s = (sharpness-N). + /// + /// 0 &le sharpness < ON_SubDEdge::InfiniteSharpness. + void SetSharpness(double sharpness); + + + /// + /// ON_SubDEdge::InfinteSharpness = 5.0 + /// Semi-sharp edges have a Sharpness() > 0.0 and < ON_SubDEdge::InfinteSharpness. + /// + static const double InfinteSharpness; + + + /// + /// ON_SubDEdge::SharpnessTolerance = 0.01 + /// If an edge has sharpness within ON_SubDEdge::SharpnessTolerance of an integer value, + /// the sharpness is set to that integer value. + /// + static const double SharpnessTolerance; + + static double SanitizeSharpness( + double sharpness, + bool bPermitInfinteSharpness + ); + + /* Returns: True if m_edge_tag is ON_SubDEdgeTag::Crease. diff --git a/opennurbs_subd_archive.cpp b/opennurbs_subd_archive.cpp index e81ee389..f32a9be4 100644 --- a/opennurbs_subd_archive.cpp +++ b/opennurbs_subd_archive.cpp @@ -850,7 +850,7 @@ bool ON_SubDEdge::Write( break; if (!archive.WriteDouble(2,m_sector_coefficient)) break; - if (!archive.WriteDouble(m_sharpness)) + if (!archive.WriteDouble(Sharpness(0.0,0.0,0.0))) break; if (!Internal_WriteVertexList(2, m_vertex, archive)) break; @@ -923,7 +923,7 @@ bool ON_SubDEdge::Read( e->m_sector_coefficient[0] = sector_coefficient[0]; e->m_sector_coefficient[1] = sector_coefficient[1]; - e->m_sharpness = sharpness; + e->SetSharpness(sharpness); if (!Internal_ReadFacePtrList(archive,face_count,sizeof(e->m_face2)/sizeof(e->m_face2[0]),e->m_face2,e->m_facex_capacity,e->m_facex)) break; diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h index 6062e635..17914b74 100644 --- a/opennurbs_subd_data.h +++ b/opennurbs_subd_data.h @@ -1950,12 +1950,6 @@ private: mutable ON_SubDHash m_subd_toplology_and_edge_creases_hash = ON_SubDHash::Empty; mutable ON_SubDHash m_subd_geometry_hash = ON_SubDHash::Empty; -public: - - -#if defined(ON_SUBD_CENSUS) - ON_SubDImpleCensusCounter m_census_counter; -#endif public: ON_SubDimple(); ~ON_SubDimple(); @@ -2059,7 +2053,7 @@ public: /* Description: - Split and edge. + Split an edge. The input edge is modifed to terminate at the input vertex. The new edge begins at the input vertex and ends at the final vertex of the original input edge. @@ -2858,10 +2852,6 @@ private: class /*DO NOT EXPORT*/ON_SubDMeshImpl { -#if defined(ON_SUBD_CENSUS) - ON_SubDMeshCensusCounter m_census_counter; -#endif - public: ON_SubDMeshImpl(); ~ON_SubDMeshImpl() = default; diff --git a/opennurbs_unicode.h b/opennurbs_unicode.h index 68eef874..216331f7 100644 --- a/opennurbs_unicode.h +++ b/opennurbs_unicode.h @@ -300,6 +300,9 @@ enum ON_UnicodeCodePoint /// RUBLE SIGN U+20BD (₽) ON_RubleSign = 0x20BD, + /// INFINITY SYMBOL U+221E (∞) + ON_InfinitySymbol = 0x221E, + /// /// UNIVERSAL RECYCLING SYMBOL U+2672 (♲) /// This is a good cold point for testing glyph substitution. diff --git a/opennurbs_xml.cpp b/opennurbs_xml.cpp index a4a0331b..b0cd0902 100644 --- a/opennurbs_xml.cpp +++ b/opennurbs_xml.cpp @@ -3750,14 +3750,16 @@ class ON_XMLRootNode::CImpl final public: }; -ON_XMLRootNode::ON_XMLRootNode() : ON_XMLNode(sXMLRootNodeName) +ON_XMLRootNode::ON_XMLRootNode() + : + ON_XMLNode(sXMLRootNodeName) { m_impl = new (m_Impl) CImpl; IMPL_CHECK; } ON_XMLRootNode::ON_XMLRootNode(const ON_XMLRootNode& src) : - ON_XMLNode(L"xml") + ON_XMLNode(sXMLRootNodeName) { m_impl = new (m_Impl) CImpl; IMPL_CHECK; @@ -3850,6 +3852,13 @@ bool ON_XMLRootNode::WriteToFile(const wchar_t* wszPath, bool includeFormatting, return true; } +void ON_XMLRootNode::Clear(void) +{ + ON_XMLNode::Clear(); + + SetTagName(sXMLRootNodeName); +} + ON_XMLRootNode::CImpl& ON_XMLRootNode::Impl(void) const { return *m_impl; @@ -3960,7 +3969,6 @@ void ON_XMLUserData::SetValue(const wchar_t* wszXMLPath, const ON_XMLVariant& va void ON_XMLUserData::Clear(void) const { m_impl->m_XMLRoot.Clear(); - m_impl->m_XMLRoot.SetTagName(L"xml"); } int ON_XMLUserData::Version(void) const @@ -4494,7 +4502,6 @@ bool ON_XMLParametersV8::GetParam(const wchar_t* wszParamName, ON_XMLVariant& vV return true; } -#define ON_RDK_UD_ROOT L"render-content-manager-data" #define ON_RDK_UD_MATERIAL L"material" #define ON_RDK_UD_INSTANCE_ID L"instance-id" @@ -5139,20 +5146,6 @@ bool ON_RunXMLTests(const wchar_t* test_folder) #pragma warning (pop) -//------------------------------------------------------------------------------------------------------------------- -#define INITIALIZE ON_XMLNode* pSetting = _root.CreateNodeAtPath(ON_RDK_DOCUMENT); \ - ON_XMLNode* pCurrent = pSetting; -//------------------------------------------------------------------------------------------------------------------- -#define BEGIN(child) ON_XMLNode* p##child##Temp = pCurrent->CreateNodeAtPath(ON_RDK_##child); \ - { \ - ON_XMLNode* pCurrent = p##child##Temp; \ - ON_XMLNode* p##child = pCurrent; \ - p##child; p##child##Temp; // Fixes warnings. - -#define BEGIN_SECTION(child) BEGIN(child) ON_XMLParameters section(*pCurrent); -#define PROPERTY(name, value) section.SetParam(ON_RDK_##name, ON_XMLVariant(value)); -#define CREATE(child) BEGIN(child) END -#define END } //------------------------------------------------------------------------------------------------------------------- #define ON_RDK_MATERIAL_SECTION L"material" ON_RDK_POSTFIX_SECTION #define ON_RDK_ENVIRONMENT_SECTION L"environment" ON_RDK_POSTFIX_SECTION @@ -5166,215 +5159,244 @@ ON_UUID chanDistanceFromCamera = { 0xb752ce0b, 0xc219, 0x4bdd, { 0xb1, //------------------------------------------------------------------------------------------------------------------- #pragma ON_PRAGMA_WARNING_PUSH -#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4456) +#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4456) // Declaration hides previous local declaration -ON_RdkDocumentDefaults::ON_RdkDocumentDefaults(int major_version, ValueSets vs, void*) - : - _vs(vs), - _reserved(nullptr) +class CreateXMLException { - const bool bForOldFile = major_version < 6; +}; - INITIALIZE +static ON_XMLNode& Create(ON_XMLNode& node, const wchar_t* s) +{ + auto* child_node = node.CreateNodeAtPath(s); + if (nullptr == child_node) + throw CreateXMLException(); // This should never, ever happen. - if (ValueSets::All == vs) - { - BEGIN (CURRENT_CONTENT) - BEGIN (ENVIRONMENT) - END - END - - CREATE (DEFAULT_CONTENT_SECTION) - CREATE (MATERIAL_SECTION) - CREATE (ENVIRONMENT_SECTION) - CREATE (TEXTURE_SECTION) - } - - BEGIN (SETTINGS) - - if (ValueSets::All == vs) - { - BEGIN_SECTION (NAMED_VIEWS) - PROPERTY (SORT_MODE, L""); - END - BEGIN_SECTION (NAMED_CPLANES) - PROPERTY (SORT_MODE, L""); - END - BEGIN_SECTION (NAMED_POSITIONS) - PROPERTY (SORT_MODE, L""); - END - BEGIN_SECTION (NAMED_SNAPSHOTS) - PROPERTY (SORT_MODE, L""); - END - } - - if (ValueSets::All == vs) - { - BEGIN_SECTION (MISCELLANEOUS) - - PROPERTY (CUSTOM_IMAGE_SIZE_IS_PRESET, false); - - BEGIN_SECTION(NAME_COLLISION_SUPPRESS) - PROPERTY (IMPORT, false); - PROPERTY (PASTE, false); - END - END - } - - if (ValueSets::All == vs) - { - BEGIN_SECTION(EXCLUDED_RENDER_ENGINES) - PROPERTY (UUIDS, L""); - END - } - - if (ValueSets::All == vs) - { - BEGIN_SECTION(FILTERS) - PROPERTY (NAME_FILTER, L""); - PROPERTY (NAME_FILTER_INVERT, false); - PROPERTY (SHOW_UNASSIGNED, true); - PROPERTY (SHOW_V4, true); - PROPERTY (SHOW_HIDDEN, false); - PROPERTY (SHOW_REFERENCE, false); - END - } - - if (ValueSets::All == vs) - { - BEGIN_SECTION(POST_EFFECTS) - PROPERTY (PEP_EARLY_SELECTION, ON_nil_uuid); - PROPERTY (PEP_TONE_SELECTION, uuidPostEffect_ToneMapper_Clamp); - PROPERTY (PEP_LATE_SELECTION, uuidPostEffect_Gamma); - - BEGIN_SECTION(PEP_TYPE_EARLY) - END - BEGIN_SECTION(PEP_TYPE_TONE) - END - BEGIN_SECTION(PEP_TYPE_LATE) - END - END - } - - if (ValueSets::All == vs) - { - BEGIN_SECTION(RENDERING) - BEGIN_SECTION(RENDER_CHANNELS) - ON_wString sA, sB; - ON_UuidToString(chanRGBA, sA); - ON_UuidToString(chanDistanceFromCamera, sB); - sA.MakeUpper(); sB.MakeUpper(); - PROPERTY (RCH_LIST, sA + L";" + sB); - PROPERTY (RCH_MODE, ON_RDK_RCH_MODE_AUTOMATIC); - END - - PROPERTY (EMBED_SUPPORT_FILES_ON, true /* TODO:!!!!!!!! */); // rso.EmbedFilesDocumentDefault()); - PROPERTY (DITHERING, ON_RDK_DITHERING_FLOYD_STEINBERG); - PROPERTY (USE_DITHERING, false); - PROPERTY (GAMMA, bForOldFile ? 1.0f : 2.2f); - PROPERTY (USE_POST_PROCESS_GAMMA, true); - PROPERTY (USE_LINEAR_WORKFLOW, bForOldFile ? false : true); - PROPERTY (CUSTOM_REFLECTIVE_ENVIRONMENT_ON, bForOldFile ? false : true); - PROPERTY (CUSTOM_REFLECTIVE_ENVIRONMENT, ON_nil_uuid); - END - } - else - if (bForOldFile) - { - BEGIN_SECTION(RENDERING) - PROPERTY (CUSTOM_REFLECTIVE_ENVIRONMENT_ON, false); - END - } - - if (ValueSets::All == vs) - { - BEGIN_SECTION(SUN) - PROPERTY (SUN_ENABLE_ON, false); - PROPERTY (SUN_MANUAL_CONTROL_ON, false); - PROPERTY (SUN_MANUAL_CONTROL_ALLOWED, true); - PROPERTY (SUN_AZIMUTH, 0.0); - PROPERTY (SUN_ALTITUDE, 0.0); - PROPERTY (SUN_DATE_YEAR, 2000); - PROPERTY (SUN_DATE_MONTH, 1); - PROPERTY (SUN_DATE_DAY, 1); - PROPERTY (SUN_TIME_HOURS, 12.0); - PROPERTY (SUN_DAYLIGHT_SAVING_ON, false); - PROPERTY (SUN_DAYLIGHT_SAVING_MINUTES, 60); - PROPERTY (SUN_SHADOW_INTENSITY, 1.0); - PROPERTY (SUN_INTENSITY, 1.0); - PROPERTY (SUN_OBSERVER_LATITUDE, 0.0); - PROPERTY (SUN_OBSERVER_LONGITUDE, 0.0); - PROPERTY (SUN_OBSERVER_TIMEZONE, 0.0); - PROPERTY (SUN_SKYLIGHT_ON, bForOldFile ? false : true); - PROPERTY (SUN_SKYLIGHT_SHADOW_INTENSITY, 1.0); - PROPERTY (SUN_SKYLIGHT_CUSTOM_ENVIRONMENT_ON, bForOldFile ? false : true); - PROPERTY (SUN_SKYLIGHT_CUSTOM_ENVIRONMENT, ON_nil_uuid); - END - } - - if (ValueSets::All == vs) - { - BEGIN_SECTION(SAFE_FRAME) - - PROPERTY (SF_ON, false); - PROPERTY (SF_PERSPECTIVE_ONLY, true); - PROPERTY (SF_FIELD_DISPLAY_ON, false); - - BEGIN_SECTION(SF_LIVE_FRAME) - PROPERTY (SFF_ON, true); - END - - BEGIN_SECTION(SF_ACTION_FRAME) - PROPERTY (SFF_ON, true); - PROPERTY (SFF_XSCALE, 0.9); - PROPERTY (SFF_YSCALE, 0.9); - PROPERTY (SFF_LINK, true); - END - - BEGIN_SECTION(SF_TITLE_FRAME) - PROPERTY (SFF_ON, true); - PROPERTY (SFF_XSCALE, 0.8); - PROPERTY (SFF_YSCALE, 0.8); - PROPERTY (SFF_LINK, true); - END - END - } - - if (ValueSets::All == vs) - { - BEGIN_SECTION(GROUND_PLANE) - PROPERTY (GP_ON, bForOldFile ? false : true); - PROPERTY (GP_SHOW_UNDERSIDE, false); - PROPERTY (GP_ALTITUDE, 0.0); - PROPERTY (GP_AUTO_ALTITUDE, true); - PROPERTY (GP_SHADOW_ONLY, bForOldFile ? false : true); - PROPERTY (GP_MATERIAL, L""); - PROPERTY (GP_TEXTURE_SIZE, ON_2dPoint(1.0, 1.0)); - PROPERTY (GP_TEXTURE_OFFSET, ON_2dPoint(0.0, 0.0)); - PROPERTY (GP_TEXTURE_ROTATION, 0.0); - PROPERTY (GP_OFFSET_LOCK, false); - PROPERTY (GP_REPEAT_LOCK, true); - END - } - else - if (bForOldFile) - { - BEGIN_SECTION(GROUND_PLANE) - PROPERTY (GP_SHADOW_ONLY, false); - END - } - - END + return *child_node; } -const ON_XMLNode ON_RdkDocumentDefaults::Node(void) const +ON_RdkDocumentDefaults::ON_RdkDocumentDefaults(int version, ValueSets vs, void*) + : + _vs(vs), + _major_version(version), + _reserved(nullptr) +{ + try + { + CreateXML(); + } + catch (CreateXMLException) + { + ON_ERROR("CRITICAL - Failed to create default XML"); + } +} + +void ON_RdkDocumentDefaults::CreateXML(void) +{ + const bool for_old_file = (_major_version < 6); + + auto& doc = Create(_root, ON_RDK_DOCUMENT); + { + if (ValueSets::All == _vs) + { + Create(doc, ON_RDK_CURRENT_CONTENT).CreateNodeAtPath(ON_RDK_ENVIRONMENT); + Create(doc, ON_RDK_DEFAULT_CONTENT_SECTION); + Create(doc, ON_RDK_MATERIAL_SECTION); + Create(doc, ON_RDK_ENVIRONMENT_SECTION); + Create(doc, ON_RDK_TEXTURE_SECTION); + } + + auto& settings = Create(doc, ON_RDK_SETTINGS); + { + if (ValueSets::All == _vs) + { + // Named items. + ON_XMLParameters(Create(settings, ON_RDK_NAMED_VIEWS )).SetParam(ON_RDK_SORT_MODE, L""); + ON_XMLParameters(Create(settings, ON_RDK_NAMED_CPLANES )).SetParam(ON_RDK_SORT_MODE, L""); + ON_XMLParameters(Create(settings, ON_RDK_NAMED_POSITIONS)).SetParam(ON_RDK_SORT_MODE, L""); + ON_XMLParameters(Create(settings, ON_RDK_NAMED_SNAPSHOTS)).SetParam(ON_RDK_SORT_MODE, L""); + + // Miscellaneous. + auto& misc = Create(settings, ON_RDK_MISCELLANEOUS); + ON_XMLParameters(misc).SetParam(ON_RDK_CUSTOM_IMAGE_SIZE_IS_PRESET, false); + { + // Name collision suppression. + ON_XMLParameters p(Create(misc, ON_RDK_NAME_COLLISION_SUPPRESS)); + p.SetParam(ON_RDK_IMPORT, false); + p.SetParam(ON_RDK_PASTE , false); + } + + // Excluded render engines. + ON_XMLParameters p(Create(settings, ON_RDK_EXCLUDED_RENDER_ENGINES)); + p.SetParam(ON_RDK_UUIDS, L""); + + // Filters. + ON_XMLParameters f(Create(settings, ON_RDK_FILTERS)); + f.SetParam(ON_RDK_NAME_FILTER, L""); + f.SetParam(ON_RDK_NAME_FILTER_INVERT, false); + f.SetParam(ON_RDK_SHOW_UNASSIGNED, true); + f.SetParam(ON_RDK_SHOW_V4, true); + f.SetParam(ON_RDK_SHOW_HIDDEN, false); + f.SetParam(ON_RDK_SHOW_REFERENCE, false); + + // Post effects. + auto& peps = Create(settings, ON_RDK_POST_EFFECTS); + { + ON_XMLParameters p(peps); + p.SetParam(ON_RDK_PEP_EARLY_SELECTION, ON_nil_uuid); + p.SetParam(ON_RDK_PEP_TONE_SELECTION, uuidPostEffect_ToneMapper_Clamp); + p.SetParam(ON_RDK_PEP_LATE_SELECTION, uuidPostEffect_Gamma); + + Create(peps, ON_RDK_PEP_TYPE_EARLY); + Create(peps, ON_RDK_PEP_TYPE_TONE); + Create(peps, ON_RDK_PEP_TYPE_LATE); + } + } + + // Rendering section. + if (ValueSets::All == _vs) + { + auto& rendering = Create(settings, ON_RDK_RENDERING); + { + // Render channels. + auto& render_channels = Create(rendering, ON_RDK_RENDER_CHANNELS); + { + ON_wString a, b; + ON_UuidToString(chanRGBA, a); + ON_UuidToString(chanDistanceFromCamera, b); + a.MakeUpper(); b.MakeUpper(); + ON_XMLParameters p(render_channels); + p.SetParam(ON_RDK_RCH_LIST, a + L";" + b); + p.SetParam(ON_RDK_RCH_MODE, ON_RDK_RCH_MODE_AUTOMATIC); + } + + // Misc rendering settings. + ON_XMLParameters p(rendering); + p.SetParam(ON_RDK_EMBED_SUPPORT_FILES_ON, true); + p.SetParam(ON_RDK_DITHERING, ON_RDK_DITHERING_FLOYD_STEINBERG); + p.SetParam(ON_RDK_USE_DITHERING, false); + p.SetParam(ON_RDK_GAMMA, for_old_file ? 1.0f : 2.2f); + p.SetParam(ON_RDK_USE_POST_PROCESS_GAMMA, true); + p.SetParam(ON_RDK_USE_LINEAR_WORKFLOW, for_old_file ? false : true); + p.SetParam(ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT_ON, for_old_file ? false : true); + p.SetParam(ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT, ON_nil_uuid); + } + } + else + { + if (for_old_file) + { + auto& rendering = Create(settings, ON_RDK_RENDERING); + { + ON_XMLParameters p(rendering); + p.SetParam(ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT_ON, false); + } + } + } + + if (ValueSets::All == _vs) + { + // Sun. + auto& sun = Create(settings, ON_RDK_SUN); + { + ON_XMLParameters p(sun); + p.SetParam(ON_RDK_SUN_ENABLE_ON, false); + p.SetParam(ON_RDK_SUN_MANUAL_CONTROL_ON, false); + p.SetParam(ON_RDK_SUN_MANUAL_CONTROL_ALLOWED, true); + p.SetParam(ON_RDK_SUN_AZIMUTH, 0.0); + p.SetParam(ON_RDK_SUN_ALTITUDE, 0.0); + p.SetParam(ON_RDK_SUN_DATE_YEAR, 2000); + p.SetParam(ON_RDK_SUN_DATE_MONTH, 1); + p.SetParam(ON_RDK_SUN_DATE_DAY, 1); + p.SetParam(ON_RDK_SUN_TIME_HOURS, 12.0); + p.SetParam(ON_RDK_SUN_DAYLIGHT_SAVING_ON, false); + p.SetParam(ON_RDK_SUN_DAYLIGHT_SAVING_MINUTES, 60); + p.SetParam(ON_RDK_SUN_SHADOW_INTENSITY, 1.0); + p.SetParam(ON_RDK_SUN_INTENSITY, 1.0); + p.SetParam(ON_RDK_SUN_OBSERVER_LATITUDE, 0.0); + p.SetParam(ON_RDK_SUN_OBSERVER_LONGITUDE, 0.0); + p.SetParam(ON_RDK_SUN_OBSERVER_TIMEZONE, 0.0); + p.SetParam(ON_RDK_SUN_SKYLIGHT_ON, for_old_file ? false : true); + p.SetParam(ON_RDK_SUN_SKYLIGHT_SHADOW_INTENSITY, 1.0); + p.SetParam(ON_RDK_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT_ON, for_old_file ? false : true); + p.SetParam(ON_RDK_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT, ON_nil_uuid); + } + + // Safe frame. + auto& safe_frame = Create(settings, ON_RDK_SAFE_FRAME); + { + ON_XMLParameters p(safe_frame); + p.SetParam(ON_RDK_SF_ON, false); + p.SetParam(ON_RDK_SF_PERSPECTIVE_ONLY, true); + p.SetParam(ON_RDK_SF_4x3_FIELD_DISPLAY_ON, false); + + auto& live_frame = Create(safe_frame, ON_RDK_SF_LIVE_FRAME); + { + ON_XMLParameters p(live_frame); + p.SetParam(ON_RDK_SFF_ON, true); + } + + auto& action_frame = Create(safe_frame, ON_RDK_SF_ACTION_FRAME); + { + ON_XMLParameters p(action_frame); + p.SetParam(ON_RDK_SFF_ON, true); + p.SetParam(ON_RDK_SFF_XSCALE, 0.9); + p.SetParam(ON_RDK_SFF_YSCALE, 0.9); + p.SetParam(ON_RDK_SFF_LINK, true); + } + + auto& title_frame = Create(safe_frame, ON_RDK_SF_TITLE_FRAME); + { + ON_XMLParameters p(title_frame); + p.SetParam(ON_RDK_SFF_ON, true); + p.SetParam(ON_RDK_SFF_XSCALE, 0.8); + p.SetParam(ON_RDK_SFF_YSCALE, 0.8); + p.SetParam(ON_RDK_SFF_LINK, true); + } + } + } + + // Ground plane. + if (ValueSets::All == _vs) + { + auto& ground_plane = Create(settings, ON_RDK_GROUND_PLANE); + { + ON_XMLParameters p(ground_plane); + p.SetParam(ON_RDK_GP_ON, for_old_file ? false : true); + p.SetParam(ON_RDK_GP_SHOW_UNDERSIDE, false); + p.SetParam(ON_RDK_GP_ALTITUDE, 0.0); + p.SetParam(ON_RDK_GP_AUTO_ALTITUDE, true); + p.SetParam(ON_RDK_GP_SHADOW_ONLY, for_old_file ? false : true); + p.SetParam(ON_RDK_GP_MATERIAL, L""); + p.SetParam(ON_RDK_GP_TEXTURE_SIZE, ON_2dPoint(1.0, 1.0)); + p.SetParam(ON_RDK_GP_TEXTURE_OFFSET, ON_2dPoint(0.0, 0.0)); + p.SetParam(ON_RDK_GP_TEXTURE_ROTATION, 0.0); + p.SetParam(ON_RDK_GP_OFFSET_LOCK, false); + p.SetParam(ON_RDK_GP_REPEAT_LOCK, true); + } + } + else + { + if (for_old_file) + { + auto& ground_plane = Create(settings, ON_RDK_GROUND_PLANE); + { + ON_XMLParameters p(ground_plane); + p.SetParam(ON_RDK_GP_SHADOW_ONLY, false); + } + } + } + } + } +} + +const ON_XMLNode& ON_RdkDocumentDefaults::Node(void) const { return _root; } void ON_RdkDocumentDefaults::CopyDefaultsTo(ON_XMLNode& dest) const { - if (_vs == ValueSets::All) + if (ValueSets::All == _vs) { dest = _root; } diff --git a/opennurbs_xml.h b/opennurbs_xml.h index 2faf74e7..231439da 100644 --- a/opennurbs_xml.h +++ b/opennurbs_xml.h @@ -18,117 +18,116 @@ typedef bool (*ON_XMLRecurseChildrenCallback)(class ON_XMLNode*, void*); // This is the structure of the RDK document XML. -#define ON_RDK_DOCUMENT L"render-content-manager-document" +#define ON_RDK_DOCUMENT L"render-content-manager-document" - #define ON_RDK_CURRENT_CONTENT L"content" - #define ON_RDK_DEFAULT_CONTENT_SECTION L"default-content-section" + #define ON_RDK_CURRENT_CONTENT L"content" + #define ON_RDK_DEFAULT_CONTENT_SECTION L"default-content-section" #define ON_RDK_SETTINGS L"settings" - #define ON_RDK_NAMED_VIEWS L"named-views" - #define ON_RDK_NAMED_CPLANES L"named-cplanes" - #define ON_RDK_NAMED_POSITIONS L"named-positions" - #define ON_RDK_NAMED_SNAPSHOTS L"named-snapshots" - #define ON_RDK_SORT_MODE L"sort-mode" - #define ON_RDK_SORT_MODE_ASCENDING L"ascending" - #define ON_RDK_SORT_MODE_DESCENDING L"descending" - #define ON_RDK_SORT_MODE_CUSTOM L"custom" + #define ON_RDK_NAMED_VIEWS L"named-views" + #define ON_RDK_NAMED_CPLANES L"named-cplanes" + #define ON_RDK_NAMED_POSITIONS L"named-positions" + #define ON_RDK_NAMED_SNAPSHOTS L"named-snapshots" + #define ON_RDK_SORT_MODE L"sort-mode" + #define ON_RDK_SORT_MODE_ASCENDING L"ascending" + #define ON_RDK_SORT_MODE_DESCENDING L"descending" + #define ON_RDK_SORT_MODE_CUSTOM L"custom" - #define ON_RDK_MISCELLANEOUS L"miscellaneous" + #define ON_RDK_MISCELLANEOUS L"miscellaneous" #define ON_RDK_CUSTOM_IMAGE_SIZE_IS_PRESET L"custom-image-size-is-preset" #define ON_RDK_NAME_COLLISION_SUPPRESS L"smart-merge-name-collision-suppress" - #define ON_RDK_IMPORT L"import" - #define ON_RDK_PASTE L"paste" + #define ON_RDK_IMPORT L"import" + #define ON_RDK_PASTE L"paste" #define ON_RDK_EXCLUDED_RENDER_ENGINES L"excluded-render-engines" - #define ON_RDK_UUIDS L"uuids" + #define ON_RDK_UUIDS L"uuids" #define ON_RDK_FILTERS L"filters" - #define ON_RDK_NAME_FILTER L"name-filter" - #define ON_RDK_NAME_FILTER_INVERT L"name-filter-invert" - #define ON_RDK_SHOW_UNASSIGNED L"show-unassigned-materials" - #define ON_RDK_SHOW_V4 L"show-v4-materials" - #define ON_RDK_SHOW_HIDDEN L"show-hidden-materials" - #define ON_RDK_SHOW_REFERENCE L"show-reference-materials" + #define ON_RDK_NAME_FILTER L"name-filter" + #define ON_RDK_NAME_FILTER_INVERT L"name-filter-invert" + #define ON_RDK_SHOW_UNASSIGNED L"show-unassigned-materials" + #define ON_RDK_SHOW_V4 L"show-v4-materials" + #define ON_RDK_SHOW_HIDDEN L"show-hidden-materials" + #define ON_RDK_SHOW_REFERENCE L"show-reference-materials" + + #define ON_RDK_POST_EFFECTS L"post-effects-new" + #define ON_RDK_PEP_TYPE_EARLY L"early" + #define ON_RDK_PEP_TYPE_TONE L"tone-mapping" + #define ON_RDK_PEP_TYPE_LATE L"late" + #define ON_RDK_PEP_SELECTION L"selection" + #define ON_RDK_PEP_SELECTION_POSTFIX L"-" ON_RDK_PEP_SELECTION + #define ON_RDK_PEP_EARLY_SELECTION ON_RDK_PEP_TYPE_EARLY ON_RDK_PEP_SELECTION_POSTFIX + #define ON_RDK_PEP_TONE_SELECTION ON_RDK_PEP_TYPE_TONE ON_RDK_PEP_SELECTION_POSTFIX + #define ON_RDK_PEP_LATE_SELECTION ON_RDK_PEP_TYPE_LATE ON_RDK_PEP_SELECTION_POSTFIX #define ON_RDK_RENDERING L"rendering" - #define ON_RDK_RENDER_CHANNELS L"render-channels" - #define ON_RDK_RCH_MODE L"mode" - #define ON_RDK_RCH_MODE_AUTOMATIC L"automatic" - #define ON_RDK_RCH_MODE_CUSTOM L"custom" + #define ON_RDK_RENDER_CHANNELS L"render-channels" + #define ON_RDK_RCH_LIST L"list" + #define ON_RDK_RCH_MODE L"mode" + #define ON_RDK_RCH_MODE_AUTOMATIC L"automatic" + #define ON_RDK_RCH_MODE_CUSTOM L"custom" - #define ON_RDK_RCH_LIST L"list" + #define ON_RDK_EMBED_SUPPORT_FILES_ON L"embed-support-files-on" + #define ON_RDK_GAMMA L"gamma" + #define ON_RDK_USE_DITHERING L"use-dithering" + #define ON_RDK_USE_POST_PROCESS_GAMMA L"use-post-process-gamma" + #define ON_RDK_USE_LINEAR_WORKFLOW L"use-linear-workflow" - #define ON_RDK_EMBED_SUPPORT_FILES_ON L"embed-support-files-on" - #define ON_RDK_GAMMA L"gamma" - #define ON_RDK_USE_DITHERING L"use-dithering" - #define ON_RDK_USE_POST_PROCESS_GAMMA L"use-post-process-gamma" - #define ON_RDK_USE_LINEAR_WORKFLOW L"use-linear-workflow" - - #define ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT_ON L"custom-env-for-refl-and-refr-on" - #define ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT L"custom-env-for-refl-and-refr" + #define ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT_ON L"custom-env-for-refl-and-refr-on" + #define ON_RDK_CUSTOM_REFLECTIVE_ENVIRONMENT L"custom-env-for-refl-and-refr" #define ON_RDK_DITHERING L"dithering" - #define ON_RDK_DITHERING_FLOYD_STEINBERG L"floyd-steinberg" - #define ON_RDK_DITHERING_SIMPLE_NOISE L"simple-noise" + #define ON_RDK_DITHERING_FLOYD_STEINBERG L"floyd-steinberg" + #define ON_RDK_DITHERING_SIMPLE_NOISE L"simple-noise" - #define ON_RDK_SUN L"sun" - #define ON_RDK_SUN_ENABLE_ALLOWED L"enable-allowed" - #define ON_RDK_SUN_ENABLE_ON L"enable-on" - #define ON_RDK_SUN_MANUAL_CONTROL_ALLOWED L"manual-control-allowed" - #define ON_RDK_SUN_MANUAL_CONTROL_ON L"manual-control-on" - #define ON_RDK_SUN_NORTH L"north" - #define ON_RDK_SUN_AZIMUTH L"sun-azimuth" - #define ON_RDK_SUN_ALTITUDE L"sun-altitude" - #define ON_RDK_SUN_DATE_YEAR L"year" - #define ON_RDK_SUN_DATE_MONTH L"month" - #define ON_RDK_SUN_DATE_DAY L"day" - #define ON_RDK_SUN_TIME_HOURS L"time" - #define ON_RDK_SUN_DAYLIGHT_SAVING_ON L"daylight-saving-on" - #define ON_RDK_SUN_DAYLIGHT_SAVING_MINUTES L"daylight-saving-minutes" - #define ON_RDK_SUN_OBSERVER_LATITUDE L"observer-latitude" - #define ON_RDK_SUN_OBSERVER_LONGITUDE L"observer-longitude" - #define ON_RDK_SUN_OBSERVER_TIMEZONE L"observer-timezone" - #define ON_RDK_SUN_SKYLIGHT_ON L"skylight-on" - #define ON_RDK_SUN_SKYLIGHT_SHADOW_INTENSITY L"skylight-shadow-intensity" - #define ON_RDK_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT_ON L"skylight-custom-environment-on" - #define ON_RDK_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT L"skylight-custom-environment" - #define ON_RDK_SUN_SHADOW_INTENSITY L"shadow-intensity" - #define ON_RDK_SUN_INTENSITY L"intensity" + #define ON_RDK_SUN L"sun" + #define ON_RDK_SUN_ENABLE_ALLOWED L"enable-allowed" + #define ON_RDK_SUN_ENABLE_ON L"enable-on" + #define ON_RDK_SUN_MANUAL_CONTROL_ALLOWED L"manual-control-allowed" + #define ON_RDK_SUN_MANUAL_CONTROL_ON L"manual-control-on" + #define ON_RDK_SUN_NORTH L"north" + #define ON_RDK_SUN_AZIMUTH L"sun-azimuth" + #define ON_RDK_SUN_ALTITUDE L"sun-altitude" + #define ON_RDK_SUN_DATE_YEAR L"year" + #define ON_RDK_SUN_DATE_MONTH L"month" + #define ON_RDK_SUN_DATE_DAY L"day" + #define ON_RDK_SUN_TIME_HOURS L"time" + #define ON_RDK_SUN_DAYLIGHT_SAVING_ON L"daylight-saving-on" + #define ON_RDK_SUN_DAYLIGHT_SAVING_MINUTES L"daylight-saving-minutes" + #define ON_RDK_SUN_OBSERVER_LATITUDE L"observer-latitude" + #define ON_RDK_SUN_OBSERVER_LONGITUDE L"observer-longitude" + #define ON_RDK_SUN_OBSERVER_TIMEZONE L"observer-timezone" + #define ON_RDK_SUN_SKYLIGHT_ON L"skylight-on" + #define ON_RDK_SUN_SKYLIGHT_SHADOW_INTENSITY L"skylight-shadow-intensity" + #define ON_RDK_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT_ON L"skylight-custom-environment-on" + #define ON_RDK_SUN_SKYLIGHT_CUSTOM_ENVIRONMENT L"skylight-custom-environment" + #define ON_RDK_SUN_SHADOW_INTENSITY L"shadow-intensity" + #define ON_RDK_SUN_INTENSITY L"intensity" - #define ON_RDK_SAFE_FRAME L"safe-frame" - #define ON_RDK_SF_ON L"on" - #define ON_RDK_SF_PERSPECTIVE_ONLY L"perspective-only" - #define ON_RDK_SF_FIELD_DISPLAY_ON L"field-display-on" - #define ON_RDK_SF_LIVE_FRAME L"live-frame" - #define ON_RDK_SF_ACTION_FRAME L"action-frame" - #define ON_RDK_SF_TITLE_FRAME L"title-frame" - #define ON_RDK_SFF_ON L"on" - #define ON_RDK_SFF_XSCALE L"x-scale" - #define ON_RDK_SFF_YSCALE L"y-scale" - #define ON_RDK_SFF_LINK L"link" + #define ON_RDK_SAFE_FRAME L"safe-frame" + #define ON_RDK_SF_ON L"on" + #define ON_RDK_SF_PERSPECTIVE_ONLY L"perspective-only" + #define ON_RDK_SF_4x3_FIELD_DISPLAY_ON L"field-display-on" + #define ON_RDK_SF_LIVE_FRAME L"live-frame" + #define ON_RDK_SF_ACTION_FRAME L"action-frame" + #define ON_RDK_SF_TITLE_FRAME L"title-frame" + #define ON_RDK_SFF_ON L"on" + #define ON_RDK_SFF_XSCALE L"x-scale" + #define ON_RDK_SFF_YSCALE L"y-scale" + #define ON_RDK_SFF_LINK L"link" - #define ON_RDK_GROUND_PLANE L"ground-plane" - #define ON_RDK_GP_ON L"on" - #define ON_RDK_GP_ALTITUDE L"altitude" - #define ON_RDK_GP_MATERIAL L"material" - #define ON_RDK_GP_TEXTURE_OFFSET L"texture-offset" - #define ON_RDK_GP_TEXTURE_SIZE L"texture-size" - #define ON_RDK_GP_TEXTURE_ROTATION L"texture-rotation" - #define ON_RDK_GP_OFFSET_LOCK L"offset-lock" - #define ON_RDK_GP_REPEAT_LOCK L"repeat-lock" - #define ON_RDK_GP_SHOW_UNDERSIDE L"show-underside" - #define ON_RDK_GP_AUTO_ALTITUDE L"auto-altitude" - #define ON_RDK_GP_SHADOW_ONLY L"shadow-only" - - #define ON_RDK_POST_EFFECTS L"post-effects-new" - #define ON_RDK_PEP_TYPE_EARLY L"early" - #define ON_RDK_PEP_TYPE_TONE L"tone-mapping" - #define ON_RDK_PEP_TYPE_LATE L"late" - #define ON_RDK_PEP_SELECTION L"selection" - #define ON_RDK_PEP_SELECTION_POSTFIX L"-" ON_RDK_PEP_SELECTION - #define ON_RDK_PEP_EARLY_SELECTION ON_RDK_PEP_TYPE_EARLY ON_RDK_PEP_SELECTION_POSTFIX - #define ON_RDK_PEP_TONE_SELECTION ON_RDK_PEP_TYPE_TONE ON_RDK_PEP_SELECTION_POSTFIX - #define ON_RDK_PEP_LATE_SELECTION ON_RDK_PEP_TYPE_LATE ON_RDK_PEP_SELECTION_POSTFIX + #define ON_RDK_GROUND_PLANE L"ground-plane" + #define ON_RDK_GP_ON L"on" + #define ON_RDK_GP_ALTITUDE L"altitude" + #define ON_RDK_GP_MATERIAL L"material" + #define ON_RDK_GP_TEXTURE_OFFSET L"texture-offset" + #define ON_RDK_GP_TEXTURE_SIZE L"texture-size" + #define ON_RDK_GP_TEXTURE_ROTATION L"texture-rotation" + #define ON_RDK_GP_OFFSET_LOCK L"offset-lock" + #define ON_RDK_GP_REPEAT_LOCK L"repeat-lock" + #define ON_RDK_GP_SHOW_UNDERSIDE L"show-underside" + #define ON_RDK_GP_AUTO_ALTITUDE L"auto-altitude" + #define ON_RDK_GP_SHADOW_ONLY L"shadow-only" #define ON_RDK_POSTFIX_SECTION L"-section" #define ON_RDK_SLASH L"/" @@ -398,7 +397,7 @@ public: // Change data. void RemoveAllProperties(void); // Removes and deletes all child nodes and properties, and clears the tag name. - void Clear(void); + virtual void Clear(void); // Moves this node before another node. void MoveBefore(ON_XMLNode& other); @@ -563,6 +562,8 @@ public: bool ReadFromFile(const wchar_t* path, bool warnings_as_errors=false, bool validate_tags=true); bool WriteToFile (const wchar_t* path, bool include_formatting=true, bool utf8=false, bool sorted_properties=false) const; + virtual void Clear(void) override; + private: class CImpl; CImpl* m_impl; @@ -733,13 +734,17 @@ public: const ON_RdkDocumentDefaults& operator = (const ON_RdkDocumentDefaults&) = delete; - const ON_XMLNode Node(void) const; + const ON_XMLNode& Node(void) const; void CopyDefaultsTo(ON_XMLNode& dest) const; +private: + void CreateXML(void); + private: ON_XMLRootNode _root; const ValueSets _vs; + const int _major_version; void* _reserved; }; ///////////////////////////////////////////////////////////////////////////////////////////////////////